diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..1d953f4bd --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..4c9293853 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,10 @@ +# Changes + +# Documentation +Please briefly describe the documentation you have written in the following categories, or why you didn't write documentation for a category: +* what general comments you have added +* what doc comments you have added +* what documentation you have added to the website for users of Silver +* what documentation you have added to the website for developers of Silver + +*Please remove all the prefilled text after the "Documentation" heading before submitting your pull request.* diff --git a/.gitignore b/.gitignore index d0ebb6df2..aca1b0661 100644 --- a/.gitignore +++ b/.gitignore @@ -26,10 +26,12 @@ runtime/imp/main/src/ide runtime/imp/main/src/silver runtime/imp/main/target runtime/java/bin/ -test/**/*.test.output +*.test.output test/**/test.jar -test/silver.testing.bin.jar -test/silver_features/test_out.txt +tutorials/**/output.c + +# direnv +.direnv/ # Useful spot to stash your experiments sandbox diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..719b5cdf3 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "silver.jvmArgs": "-Xmx10G -Xss40M" +} \ No newline at end of file diff --git a/Jenkinsfile b/Jenkinsfile index 9d4c5deaa..e0e10db02 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -7,9 +7,12 @@ melt.setProperties(overrideJars: true) melt.trynode('silver') { def WS = pwd() def SILVER_GEN = "${WS}/generated" + def newenv = silver.getSilverEnv(WS) stage("Build") { + melt.clearGenerated() + checkout scm // Bootstrap logic to obtain jars @@ -69,21 +72,36 @@ melt.trynode('silver') { sh "./deep-rebuild" // Clean (but leave generated files) sh "./deep-clean -delete" + // Generate docs + sh "./make-docs" // Package sh "rm -rf silver-latest* || true" // Robustness to past failures sh "./make-dist latest" // Upon succeeding at initial build, archive for future builds archiveArtifacts(artifacts: "jars/*.jar", fingerprint: true) + melt.archiveCommitArtifacts("jars/*.jar") + } + + stage("Modular Analyses") { + sh "./self-compile --clean --mwda --dont-translate" } stage("Test") { - def tests = ["silver_features", "copper_features", "patt", "stdlib", "performance", "csterrors"] - def tuts = ["simple/with_all", "simple/with_do_while", "simple/with_repeat_until", "simple/with_implication", "simple/host", "dc", "lambda", "turing", "hello"] + // These test cases and tutorials are run as seperate tasks to allow for parallelism + def tests = ["silver_features", "copper_features", "patt", "flow", "stdlib", "performance", "csterrors", "silver_construction", "origintracking", "implicit_monads"] + def tuts = ["simple/with_all", "simple/with_do_while", "simple/with_repeat_until", "simple/with_implication", "simple/host", "simple/arb_host", "simple/arb_with_all", "dc", "lambda", "turing", "hello", "stlc"] def tasks = [:] tasks << tests.collectEntries { t -> [(t): task_test(t, WS)] } tasks << tuts.collectEntries { t -> [(t): task_tutorial(t, WS)] } + // Build test driver + withEnv (newenv) { + dir ("${WS}/test") { + sh "silver --clean silver:testing:bin" + } + } + // Unpack tarball (into ./silver-latest/) (for tutorial testing) sh "tar zxf silver-latest.tar.gz" // Run tests @@ -96,7 +114,7 @@ melt.trynode('silver') { // Projects with 'develop' as main branch, we'll try to build specific branch names if they exist def github_projects = ["/melt-umn/ableC", "/melt-umn/Oberon0", "/melt-umn/ableJ14", "/melt-umn/meta-ocaml-lite", "/melt-umn/lambda-calculus", "/melt-umn/rewriting-regex-matching", "/melt-umn/rewriting-optimization-demo", - "/internal/ring"] + "/internal/ring", "/melt-umn/caml-light"] // Specific other jobs to build def specific_jobs = ["/internal/matlab/master", "/internal/metaII/master", "/internal/simple/master"] // AbleP is now downstream from Silver-AbleC, so we don't need to build it here: "/melt-umn/ableP/master" @@ -122,8 +140,12 @@ melt.trynode('silver') { // NOTE: we exclude generated, which means there's no generated dir in the custom // location, which means if you don't set it, things should blow up. + sh "rsync -a --delete generated/doc/ ${silver.SILVER_WORKSPACE}/../custom-silver-doc/" + sh "cp silver-latest.tar.gz ${melt.ARTIFACTS}/" sh "cp jars/*.jar ${melt.ARTIFACTS}/" + + build "/melt-umn/melt-website/master" } } @@ -142,16 +164,19 @@ def getMergedBranch() { // Test in local workspace def task_test(String testname, String WS) { + def newenv = silver.getSilverEnv(WS) return { node { sh "touch ensure_workspace" // convince jenkins to create our workspace def GEN = pwd() // This node's workspace - // Go back to our "parent" workspace, into the test - dir(WS + '/test/' + testname) { - sh "./silver-compile --clean -G ${GEN}" - if (fileExists("test.jar")) { - sh "java -Xss2M -jar test.jar" - sh "rm test.jar" + // Go back to our "parent" workspace, into the tests directory + dir(WS + '/test/') { + // HACK: edit the test specs to specify the generated directory + sh "./set-generated-dir ${GEN} ${testname}" + // Run the tests + withEnv (newenv) { + echo "Running test ${testname}" + sh "java -jar silver.testing.bin.jar ${testname}" } } // Blow away these generated files in our private workspace diff --git a/README.md b/README.md index 3092c06be..1515a6b76 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ extensions, with language features and analyses to support this. ## Using silver -Silver requires Java 8, Ant, Bash, and wget. It can run on Linux, MacOS, and Windows Subsystem for Linux (WSL) in Windows 10. +Silver requires Java 8, Ant 1.10.0, Bash, and wget. It can run on Linux, MacOS, and Windows Subsystem for Linux (WSL) in Windows 10. See [The Silver Install Guide](http://melt.cs.umn.edu/silver/install-guide) for detailed information on how to get Silver set up. @@ -67,3 +67,4 @@ Silver and Copper are distributed under the GNU Lesser General Public License. See the files COPYING and COPYING.LESSER for details of these licenses. More information can be found at http://www.gnu.org/licenses/. + diff --git a/build-everything b/build-everything deleted file mode 100755 index 292f893f0..000000000 --- a/build-everything +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash - -set -eu - -for grammar in $(find grammars -name '*.sv' | sed 's#grammars/\(.*\)/.*\.sv#\1#' | sort | uniq | sed 's#/#:#g'); do - java -Xss8M -Xmx2000M -jar jars/silver.composed.Default.jar --clean $grammar -done diff --git a/deep-clean b/deep-clean index 3ba71c016..9344fe23e 100755 --- a/deep-clean +++ b/deep-clean @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # ./deep-clean to test the waters (dry run) # ./deep-clean -delete to blow it away @@ -10,7 +10,7 @@ if [ ! -d grammars ] || [ ! -d tutorials ]; then exit 1 fi -# Note: use ./* rather than . This avoids looking in .hg +# Note: use ./* rather than . This avoids looking in .git find ./build/ $1 find ./grammars/ -name "build.xml" $1 find ./* -name "*~" $1 @@ -29,10 +29,10 @@ find ./tutorials/ -name "build.xml" $1 find ./tutorials/ -name "output.c" $1 find "build.xml" $1 find "silver.testing.bin.jar" $1 -find ./runtime/imp/main/src/core $1 -find ./runtime/imp/main/src/ide $1 -find ./runtime/imp/main/target $1 +find ./runtime/lsp4j/target $1 +find ./language-server/langserver/target $1 +find ./language-server/launcher/target $1 if [ "$2" == "all" ]; then - rm -rf generated/src/* generated/bin/* runtime/java/bin/* generated/ide/* + rm -rf generated/src/* generated/bin/* runtime/java/bin/* generated/doc/* fi diff --git a/deep-rebuild b/deep-rebuild index ee3d53773..331a0ed9e 100755 --- a/deep-rebuild +++ b/deep-rebuild @@ -1,14 +1,17 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu export SILVER_HOME=$(pwd) -JVM_ARGS="-Xss8M -Xmx2000M -jar ../jars/silver.composed.Default.jar" +JVM_ARGS="-Xss16M -Xmx5G -jar ../jars/silver.compiler.composed.Default.jar" +export ANT_OPTS=-Xss10M # just run this script, no parameters or options. # One new option: --newcore, for when the core FFI stuff has to change in tandem with the runtime # Only I should ever have to use this, as once it's done ONCE, fetch-jars will eliminate anyone else's need to do it. +trap 'echo -e "\a"' EXIT + mkdir -p build cd build @@ -20,9 +23,15 @@ fi # Modifications may have been made to how the translation is done echo "One full cycle ..." -time java $JVM_ARGS --relative-jar --clean silver:composed:Default +echo "" +echo " > Silver Build" +echo " Silver Build" +echo " Runtime Build" +echo " Silver Build" +echo "" +time java $JVM_ARGS --relative-jar --clean silver:compiler:composed:Default ant -mv silver.composed.Default.jar ../jars/ +mv silver.compiler.composed.Default.jar ../jars/ # Modifications may need to be made to how core translates, to accomodate translation/runtime changes if [ $# -gt 0 ] && [ "$1" == "--newcore" ]; then @@ -33,34 +42,48 @@ fi # Should backup old jars here -echo "Before we start, let's get this thing built ..." -java $JVM_ARGS --clean silver:support:monto -java $JVM_ARGS --clean lib:xml:ast +echo "" +echo " Silver Build" +echo " > Silver Build" +echo " Runtime Build" +echo " Silver Build" +echo "" echo "Start ..." -time java $JVM_ARGS --relative-jar --clean silver:composed:Default +# Also building silver:xml:ast here since the runtime depends on that +time java $JVM_ARGS --relative-jar --clean silver:compiler:composed:Default silver:xml:ast # Modifications may have been made to the runtime echo "No ant yet! Let's go build the runtime! ..." +echo "" +echo " Silver Build" +echo " Silver Build" +echo " > Runtime Build" +echo " Silver Build" +echo "" cd ../runtime/java ant mv SilverRuntime.jar ../../jars/ cd ../../build echo "Now finish ..." ant -mv silver.composed.Default.jar ../jars/ +mv silver.compiler.composed.Default.jar ../jars/ # Finally, we should have a fully working binary on all the new code. Run it again to reach a fixed point: echo "One more full cycle ..." -time java -ea $JVM_ARGS --relative-jar --clean silver:composed:Default +echo "" +echo " Silver Build" +echo " Silver Build" +echo " Runtime Build" +echo " > Silver Build" +echo "" +time java -ea $JVM_ARGS --relative-jar --clean silver:compiler:composed:Default ant -mv silver.composed.Default.jar ../jars/ +mv silver.compiler.composed.Default.jar ../jars/ # If the last build failed, should restore backup jars -echo -e "\a" - echo "Cleaning out stale runtime class files..." rm -rf ../generated/bin/common diff --git a/fetch-jars b/fetch-jars index f90ca697a..54f2f5f5c 100755 --- a/fetch-jars +++ b/fetch-jars @@ -1,20 +1,33 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -LOCAL_STORE=/web/research/melt.cs.umn.edu/downloads/silver-dev/jars -REMOTE_STORE="http://melt.cs.umn.edu/downloads/silver-dev/jars" +# Usage: ./fetch-jars [rev-name] [--copper] + + +if [[ $* != *--copper* && $# -gt 0 || $# -gt 1 ]]; then + rev=$(git rev-parse $1) + echo "Warning: Fetching unstable jars! (commit $rev)" + + LOCAL_STORE= + REMOTE_STORE="https://foundry.remexre.xyz/commit-artifacts/$rev" + JARS_BAK="JARS-BAK/$rev" +else + LOCAL_STORE=/web/research/melt.cs.umn.edu/downloads/silver-dev/jars + REMOTE_STORE="https://melt.cs.umn.edu/downloads/silver-dev/jars" + JARS_BAK=JARS-BAK +fi if [[ $* == *--copper* ]]; then # Only fetch the Copper jars, if requested - FILES="CopperCompiler.jar CopperRuntime.jar" + FILES="CopperCompiler.jar" else - FILES="CopperCompiler.jar CopperRuntime.jar silver.composed.Default.jar SilverRuntime.jar IDEPluginRuntime.jar jeromq-0.3.4.jar gson-2.8.0.jar" + FILES="CopperCompiler.jar commonmark-0.17.1.jar silver.compiler.composed.Default.jar SilverRuntime.jar" fi mkdir -p jars -if [ -d $LOCAL_STORE ]; then +if [[ -n "$LOCAL_STORE" && -d $LOCAL_STORE ]]; then for file in $FILES; do cp $LOCAL_STORE/$file jars/ done @@ -29,16 +42,16 @@ else done # We're going to download them to here - mkdir -p JARS-BAK + mkdir -p $JARS_BAK # -N Pay attention to timestamps, to avoid needless redownloads. # -P jars/ Put the files in jars/ # -nv Don't be so verbose! - wget -N -P JARS-BAK/ -nv $URLS + wget -N -P $JARS_BAK/ -nv $URLS # Always overwrite all the files in jars. for file in $FILES; do - cp JARS-BAK/$file jars/ + cp $JARS_BAK/$file jars/ done fi diff --git a/generated/link-to-export-scratch.sh b/generated/link-to-export-scratch.sh index 63c824d0a..1fbe86502 100755 --- a/generated/link-to-export-scratch.sh +++ b/generated/link-to-export-scratch.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu diff --git a/generated/tests.skip b/generated/tests.skip deleted file mode 100644 index e69de29bb..000000000 diff --git a/grammars/core/DocConfig.sv b/grammars/core/DocConfig.sv deleted file mode 100644 index 3a120abea..000000000 --- a/grammars/core/DocConfig.sv +++ /dev/null @@ -1,8 +0,0 @@ -grammar core; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Core library\nmenu_title: core library\nmenu_weight: 20\n---" - - split-files: "true" -@} - diff --git a/grammars/core/Either.sv b/grammars/core/Either.sv deleted file mode 100644 index bef18aaca..000000000 --- a/grammars/core/Either.sv +++ /dev/null @@ -1,76 +0,0 @@ -grammar core; - -synthesized attribute fromLeft :: a; -synthesized attribute fromRight :: a; -synthesized attribute isLeft :: Boolean; -synthesized attribute isRight :: Boolean; - -{-- - - The basic sum type, counterpart to Pair. - - - - Occasionally used as a poor-quality "result or error" type. - - By convention, the error type is the FIRST type, and the - - expected return value is the second. - - e.g. Either - -} -nonterminal Either with fromLeft, fromRight, isLeft, isRight; - -abstract production left -top::Either ::= value::a -{ - top.fromLeft = value; - top.fromRight = error("fromRight accessed on a Either that was actually left!"); - top.isLeft = true; - top.isRight = false; -} - -abstract production right -top::Either ::= value::b -{ - top.fromLeft = error("fromRight accessed on a Either that was actually left!"); - top.fromRight = value; - top.isLeft = false; - top.isRight = true; -} - - -{-- - - Order preserving partitioning of a list of eithers into a pair - - of lists of the two different results. - -} -function partitionEithers -Pair<[a] [b]> ::= l::[Either] -{ - local recurse :: Pair<[a] [b]> = partitionEithers(tail(l)); - - return case l of - | [] -> pair([], []) - | left(a) :: _ -> pair(a :: recurse.fst, recurse.snd) - | right(b) :: _ -> pair(recurse.fst, b :: recurse.snd) - end; -} - -{-- - - Returns the left value, or the default if there is no left value. - -} -function fromLeft -a ::= e::Either o::a -{ - return case e of - | left(a) -> a - | right(_) -> o - end; -} - -{-- - - Returns the right value, or the default if there is no right value. - -} -function fromRight -b ::= e::Either o::b -{ - return case e of - | left(_) -> o - | right(b) -> b - end; -} - diff --git a/grammars/core/IO.sv b/grammars/core/IO.sv deleted file mode 100644 index 45080b1c9..000000000 --- a/grammars/core/IO.sv +++ /dev/null @@ -1,464 +0,0 @@ -grammar core; - -{-- - - The resulting world-state token of an IO action. - -} -synthesized attribute io :: IO; -{-- - - The resulting value of an IO action. - -} -synthesized attribute iovalue :: a; - -{-- - - A container for the results of IO actions. - - - - @param a The type of value returned by the IO action. - -} -nonterminal IOVal with io, iovalue; - -{-- - - The sole constructor of IOVal results. - - - - @param i The resulting world-state token. - - @param v The resulting value. - -} -abstract production ioval -top::IOVal ::= i::IO v::a -{ - top.io = i; - top.iovalue = v; -} - -{-- - - IO is the IO Token used to sequence actions. - -} -type IO foreign = "common.IOToken"; - ------- IO Actions: - -{-- - - Displays a string on standard out. Newlines are NOT automatically added. - - - - @param s The string to print. - - @param i The "before" world-state token. - - @return The "after" world-state token. - -} -function print -IO ::= s::String i::IO -{ - return error("Not Yet Implemented: print"); -} foreign { - "java" : return "%i%.print(%s%)"; -} - -function readLineStdin -IOVal ::= i::IO -{ - return error ("Not Yet Implemented: getStr"); -} foreign { - "java" : return "%i%.readLineStdin()"; -} - -{-- - - Terminates with the specified error code. - - - - @param val The error code to terminate with. (0 is considered "success") - - @param i The "before" world-state token. - - @return Does not actually return! - -} -function exit -IO ::= val::Integer i::IO -{ - return error("Not Yet Implemented: exit"); -} foreign { - "java" : return "%i%.exit(%val%)"; -} - -{-- - - Creates a directory, including any parents that need to be created along the way. - - Similar to 'mkdir -p'. If it fails, it may create only some of them. - - - - @param s The path to create. - - @param i The "before" world-state token. - - @return true if completely successful. false if an error occurred along the way. - -} -function mkdir -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: mkdir"); -} foreign { - "java" : return "%i%.mkdir(%s%)"; -} - -{-- - - Executes a shell command. Specifically executes 'bash -c'. So, not fully cross-platform. - - - - Avoid using this if possible. If you need an IO action not present, request it, please. - - - - Access to command's output is not directly available, but it is run in a shell. You can - - redirect to a file and read that. - - - - @param s The string for the shell to execute. - - @param i The "before" world-state token. - - @return The exit value of the subprocess. - -} -function system -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: system"); -} foreign { - "java" : return "%i%.system(%s%)"; -} - -{-- - - Write a string to a file, replacing whatever is there already. - - - - @param file The filename to write to. - - @param contents The string to write to the file. - - @param i The "before" world-state token. - - @return The "after" world-state token. May throw a java IO exception, which cannot be caught by Silver. - -} -function writeFile -IO ::= file::String contents::String i::IO -{ - return error("Not Yet Implemented: writeFile"); -} foreign { - "java" : return "%i%.writeFile(%file%, %contents%)"; -} - -{-- - - Append a string to a file. - - - - @param file The filename to append to. - - @param contents The string to append to the file. - - @param i The "before" world-state token. - - @return The "after" world-state token. May throw a java IO exception, which cannot be caught by Silver. - -} -function appendFile -IO ::= file::String contents::String i::IO -{ - return error("Not Yet Implemented: appendFile"); -} foreign { - "java" : return "%i%.appendFile(%file%, %contents%)"; -} - -------- IO Read Actions: - -{-- - - The time, in seconds since 1970, when this file (or directory) was last modified. - - - - @param s The file to query. - - @param i The "before" world-state token. - - @return The modification time of this file. Or 0 if file was not found. - -} -function fileTime -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: fileTime"); -} foreign { - "java" : return "%i%.fileTime(%s%)"; -} - -{-- - - Checks if a file is an ordinary file. (non-directory, non-special) - - - - @param s The file to query. - - @param i The "before" world-state token. - - @return true if if the file is ordinary. false otherwise. - -} -function isFile -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: isFile"); -} foreign { - "java" : return "%i%.isFile(%s%)"; -} - -{-- - - Checks if a path is a directory. - - - - @param s The path to query. - - @param i The "before" world-state token. - - @return true if if the exists and is a directory. false otherwise. - -} -function isDirectory -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: isDirectory"); -} foreign { - "java" : return "%i%.isDirectory(%s%)"; -} - -{-- - - Read the entire contents of a file. All instances of "\r\n" are replaced by "\n" - - for compatibility reasons. - - - - @param s The file to read. - - @param i The "before" world-state token. - - @return The contents of the file. May throw a java IO exception, which cannot be caught by Silver. - -} -function readFile -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: readFile"); -} foreign { - "java" : return "%i%.readFile(%s%)"; -} - -{-- - - Return the current working directory. - - - - @param i The "before" world-state token. - - @return The current working directory of the process. - -} -function cwd -IOVal ::= i::IO -{ - return error("Not Yet Implemented: cwd"); -} foreign { - "java" : return "%i%.cwd()"; -} - -{-- - - Obtain the value of an environment variable. - - - - @param s The name of the environment variable to read. - - @param i The "before" world-state token. - - @return The variables string. Empty string if the key doesn't exist. - -} -function envVar -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: envVar"); -} foreign { - "java" : return "%i%.envVar(%s%)"; -} - -{-- - - List the contents of a directory. Returns empty list if not a directory or - - other IO error. - - - - @param s The path to list the contents of. - - @param i The "before" world-state token. - - @return All files and directories in the named directory. Or [] on error. - -} -function listContents -IOVal<[String]> ::= s::String i::IO -{ - return error("Not Yet Implemented: listContents"); -} foreign { - "java" : return "%i%.listContents(%s%)"; -} - -{-- - - Delete a file, or an empty directory. - - - - @param s The path to file to delete. - - @param i The "before" world-state token. - - @return true if the file is deleted successfully. false otherwise. - -} -function deleteFile -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: deleteFile"); -} foreign { - "java" : return "%i%.deleteFile(%s%)"; -} -{-- - - Delete a set of files. - - - - @param s The list of paths to files to delete. - - @param i The "before" world-state token. - - @return true if all files are deleted successfully. false otherwise. - -} -function deleteFiles -IOVal ::= s::[String] i::IO -{ - return error("Not Yet Implemented: deleteFiles"); -} foreign { - "java" : return "%i%.deleteFiles(%s%)"; -} -{-- - - Empty a directory of all normal files (i.e. leaving subdirectories alone) - - - - @param s The path to the directory to empty - - @param i The "before" world-state token. - - @return true if contents are deleted successfully. false otherwise. - -} -function deleteDirFiles -IOVal ::= s::String i::IO -{ - return error("Not Yet Implemented: deleteDirFiles"); -} foreign { - "java" : return "%i%.deleteDirFiles(%s%)"; -} - -{-- - - Delete a non-empty directory and all subdirectories and files. - - - - @param s The path to the directory to delete - - @param i The "before" world-state token. - - @return The IO token. Errors (other than non-existence of the path) are fatal. - -} -function deleteTree -IO ::= s::String i::IO -{ - return error("Not Yet Implemented: deleteTree"); -} foreign { - "java" : return "%i%.deleteTree(%s%)"; -} - -{-- - - Copy a file from src to dst. - - - - @param src The path of the file to copy. - - @param dst The path of the file to write, or the directory to copy the file to. - - @return the IO token. Errors are fatal. - -} -function copyFile -IO ::= src::String dst::String i::IO -{ - return error("Not Yet Implemented: copyFile"); -} foreign { - "java" : return "%i%.copyFile(%src%, %dst%)"; -} - -{-- - - Update a file's modification time to the current time. - - - - @param file The file to update the modification time of. - - @param i The IO token. - - @return The IO token. Errors are suppressed. - -} -function touchFile -IO ::= file::String i::IO -{ - return error("Not Yet Implemented: touchFile"); -} foreign { - "java" : return "%i%.touchFile(%file%)"; -} -{-- - - Update a set of files' modification time to the current time. - - - - @param files The list of files to update the modification time of. - - @param i The IO token. - - @return The IO token. Errors are suppressed. - -} -function touchFiles -IO ::= files::[String] i::IO -{ - return error("Not Yet Implemented: touchFiles"); -} foreign { - "java" : return "%i%.touchFiles(%files%)"; -} - ------- IO Misc. - -{-- - - Die with the stated error message and a stack trace. Note that Silver stacks - - may be hard to read (it's a lazy language.) - - - - @param msg The path to list the contents of. - - @return Does not return. - -} -function error -a ::= msg::String -{ - return error("Not Yet Implemented: error"); -- lol -} foreign { - "java" : return "common.Util.error(%msg%.toString())"; -} - -{-- - - Create a bogus world-state token, for use with unsafeTrace. - - - - @return A fake world-state token. - - @see unsafeTrace - -} -function unsafeIO -IO ::= -{ - return error("Not Yet Implemented: unsafeIO"); -} foreign { - "java" : return "common.IOToken.singleton"; -} - -{-- - - Generate an integer unique to this run of this process. Starts from 0 and just - - counts up each call. - - - - @return An integer unique to this process. - -} -function genInt -Integer ::= -{ - return error("Not Yet Implemented: genInt"); -} foreign { - "java" : return "common.Util.genInt()"; -} - -{-- - - Generates a random number between [0, 1) - -} -function genRand -Float ::= -{ - return error("Not Yet Implemented: genRand"); -} foreign { - "java" : return "((float)Math.random())"; -} - - -{-- - - Execute an IO action when a value is demanded by the Silver runtime. - - When this gets executed may be unpredictable. - - - - @param val The value to evaluate to, after the IO action is performed. - - @param act The world-state token to demand and consume. - - @return val, unchanged. - - @see unsafeIO - -} -function unsafeTrace -a ::= val::a act::IO -{ - return error("Not Yet Implemented: unsafeTrace"); -} foreign { - "java" : return "common.Util.io(%act%, %val%)"; -} - - - --- Function for manipulating strings representing file and directory names. - -function dirNameInFilePath -String ::= filePath::String -{ - return if indexOfLastSlash == -1 then filePath - else substring(0, indexOfLastSlash, filePath); - - local attribute indexOfLastSlash :: Integer; - indexOfLastSlash = lastIndexOf("/", filePath); -} - -function fileNameInFilePath -String ::= filePath::String -{ - return if indexOfLastSlash == -1 then filePath - else substring(indexOfLastSlash+1, length(filePath), filePath); - - local attribute indexOfLastSlash :: Integer; - indexOfLastSlash = lastIndexOf("/", filePath); -} - - -function splitFileNameAndExtension -Pair ::= filePath::String -{ - return if indexOfLastDot == -1 then pair(filePath, "") - else pair(substring(0, indexOfLastDot, filePath) , - substring(indexOfLastDot+1, length(filePath), filePath)); - - local attribute indexOfLastDot :: Integer; - indexOfLastDot = lastIndexOf(".", filePath); -} - diff --git a/grammars/core/List.sv b/grammars/core/List.sv deleted file mode 100644 index 327d028fc..000000000 --- a/grammars/core/List.sv +++ /dev/null @@ -1,518 +0,0 @@ -grammar core; - -{@comment - Applies a function to each element of the list. - - This is a list of links. - @link[map] - @link[foldr] - - param f The function to apply - param l The list to map over - return The list containing the results of applying the function to l -@} -function map -[b] ::= f::(b ::= a) l::[a] -{ - return if null(l) then [] - else f(head(l)) :: map(f, tail(l)); -} - -{-- - - Applies an operator right-associatively over a list. - - (i.e. replaces cons with 'f', nil with 'i' in the list) - - - - @param f The operator to apply - - @param i The "end element" to use in place of 'nil' - - @param l The list to fold - - @return The result of the function applied right-associatively to the list. - -} -function foldr -b ::= f::(b ::= a b) i::b l::[a] -{ - return if null(l) then i - else f(head(l), foldr(f, i, tail(l))); -} - -{-- - - Applies an operator left-associatively over a list. - - - - @param f The operator to apply - - @param i The value to "start with" - - @param l The list to fold - - @return The result of the function applied left-associatively to the list. - -} -function foldl -b ::= f::(b ::= b a) i::b l::[a] -{ - return if null(l) then i - else foldl(f, f(i, head(l)), tail(l)); -} - -{-- - - Right-fold, assuming there is always one element, and leaving that element - - unchanged for single element lists. - - - - @see foldr - -} -function foldr1 -a ::= f::(a ::= a a) l::[a] -{ - return if null(l) then error("Applying foldr1 to empty list.") - else if null(tail(l)) then head(l) - else f(head(l), foldr1(f, tail(l))); -} - -{-- - - Left-fold, assuming there is always one element, and leaving that element - - unchanged for single element lists. - - - - @see foldl - -} -function foldl1 -a ::= f::(a ::= a a) l::[a] -{ - return if null(l) then error("Applying foldl1 to empty list.") - else foldl(f, head(l), tail(l)); -} - -{-- - - Filter out elements of a list. - - - - @param f The filter function - - @param lst The input list to filter - - @return Only those elements of 'lst' that 'f' returns true for, in the - - same order as they appeared in 'lst' - -} -function filter -[a] ::= f::(Boolean ::= a) lst::[a] -{ - return if null(lst) - then [] - else if f(head(lst)) - then head(lst) :: filter(f, tail(lst)) - else filter(f, tail(lst)); -} - -{-- - - Partition a list in two - - - - @param f Decision function - - @param lst The list to partition - - @return A pair of all elements returning true, and all elements returning false. - -} -function partition -Pair<[a] [a]> ::= f::(Boolean ::= a) lst::[a] -{ - local attribute recurse :: Pair<[a] [a]>; - recurse = partition(f, tail(lst)); - - return if null(lst) then pair([],[]) - else if f(head(lst)) - then pair(head(lst) :: recurse.fst, recurse.snd) - else pair(recurse.fst, head(lst) :: recurse.snd); -} - -{-- - - Determine if an element appears in a list. - - - - @param eq The equality function to use - - @param elem The element to search for - - @param lst The list to search - - @return True if the equality function returns true for some element of the list, - - false otherwise. - -} -function containsBy -Boolean ::= eq::(Boolean ::= a a) elem::a lst::[a] -{ - return (!null(lst)) && (eq(elem, head(lst)) || containsBy(eq, elem, tail(lst))); -} - -{-- - - Removes all duplicates from a list. - - - - @param eq The equality function to use - - @param xs The list to remove duplicates from - - @return A list containing no duplicates, according to the equality function. - -} -function nubBy -[a] ::= eq::(Boolean ::= a a) xs::[a] -{ - return if null(xs) then [] - else head(xs) :: nubBy(eq, removeBy(eq, head(xs), tail(xs))); -} - -{-- - - Removes all instances of an element from a list. - - - - @param eq The equality function to use - - @param x The element to remove - - @param xs The list to remove the element from - - @return A list with no remaining instances of 'x' according to 'eq' - -} -function removeBy -[a] ::= eq::(Boolean ::= a a) x::a xs::[a] -{ - return if null(xs) then [] - else (if eq(x,head(xs)) then [] else [head(xs)]) ++ removeBy(eq, x, tail(xs)); -} - -{-- - - Removes all instances of several elements from a list: xs - ys - - - - @param eq The equality function to use - - @param ys The list of elements to remove - - @param xs The list to remove elements from - - @return A list with no remaining instances in 'ys' according to 'eq' - -} -function removeAllBy -[a] ::= eq::(Boolean ::= a a) ys::[a] xs::[a] -{ - return if null(ys) then xs - else removeAllBy(eq, tail(ys), removeBy(eq, head(ys), xs)); -} - -{-- - - Returns the initial elements of a list. - - - - @param lst The list to examine - - @return The initial elements of 'lst'. If 'lst' is empty, crash. - -} -function init -[a] ::= lst::[a] -{ - return if null(tail(lst)) - then [] - else head(lst)::init(tail(lst)); -} - -{-- - - Returns the last element of a list. - - - - @param lst The list to examine - - @return The last element of 'lst'. If 'lst' is empty, crash. - -} -function last -a ::= lst::[a] -{ - return if null(tail(lst)) then head(lst) - else last(tail(lst)); -} - -{-- - - Concatenates a list of lists. - - - - @param lst A list containing lists - - @return A flattened list - -} -function concat -[a] ::= lst::[[a]] -{ - return foldr(append, [], lst); -} - -{-- - - Map a function over a list, and then conatenates the results together. - - - - @param f A function to apply to each element of a list, returning a list. - - @param lst A list - - @return The combined list - -} -function flatMap -[b] ::= f::([b] ::= a) lst::[a] -{ - return concat(map(f, lst)); -} - -function drop -[a] ::= number::Integer lst::[a] -{ - return if null(lst) || number <= 0 then lst - else drop(number-1, tail(lst)); -} -function take -[a] ::= number::Integer lst::[a] -{ - return if null(lst) || number <= 0 then [] - else head(lst) :: take(number-1, tail(lst)); -} -function dropWhile -[a] ::= f::(Boolean::=a) lst::[a] -{ - return if null(lst) || !f(head(lst)) then lst - else dropWhile(f, tail(lst)); -} -function takeWhile -[a] ::= f::(Boolean::=a) lst::[a] -{ - return if null(lst) || !f(head(lst)) then [] - else head(lst) :: takeWhile(f, tail(lst)); -} -function takeUntil -[a] ::= f::(Boolean::=a) lst::[a] -{ - return if null(lst) || f(head(lst)) - then [] - else head(lst) :: takeUntil(f, tail(lst)); -} - -function positionOf -Integer ::= eq::(Boolean ::= a a) x::a xs::[a] -{ - return positionOfHelper(eq,x,xs,0); -} - -function positionOfHelper -Integer ::= eq::(Boolean ::= a a) x::a xs::[a] currentPos::Integer -{ - return if null(xs) then -1 - else if eq(x, head(xs)) then currentPos - else positionOfHelper(eq, x, tail(xs), currentPos+1); -} - -function repeat -[a] ::= v::a times::Integer -{ - return if times <= 0 then [] - else v :: repeat(v, times-1); -} - -function range -[Integer] ::= lower::Integer upper::Integer -{ - return if lower >= upper then [] else lower :: range(lower + 1, upper); -} - -function zipWith -[c] ::= f::(c ::= a b) l1::[a] l2::[b] -{ - return if null(l1) || null(l2) then [] - else f(head(l1), head(l2)) :: zipWith(f, tail(l1), tail(l2)); -} - -function reverse -[a] ::= lst::[a] -{ - return reverseHelp(lst, []); -} -function reverseHelp -- do not use -[a] ::= lst::[a] sofar::[a] -{ - return if null(lst) then sofar - else reverseHelp(tail(lst), head(lst) :: sofar); -} - -function sortBy -[a] ::= lte::(Boolean ::= a a) lst::[a] -{ - return sortByHelp(lte, lst, length(lst)); -} -function sortByHelp -- do not use -[a] ::= lte::(Boolean ::= a a) lst::[a] upTo::Integer -{ - return if upTo == 0 then [] - else if upTo == 1 then [head(lst)] - else mergeBy(lte, front_half, back_half); - - local attribute front_half :: [a]; - front_half = sortByHelp(lte, lst, middle); - - local attribute back_half :: [a]; - back_half = sortByHelp(lte, drop(middle, lst), upTo - middle); - - local attribute middle :: Integer; - middle = toInteger(toFloat(upTo) / 2.0); -} -function mergeBy -- do not use -[a] ::= lte::(Boolean ::= a a) l1::[a] l2::[a] -{ - return if null(l1) then l2 - else if null(l2) then l1 - else if lte(head(l1), head(l2)) - then head(l1) :: mergeBy(lte, tail(l1), l2) - else head(l2) :: mergeBy(lte, l1, tail(l2)); -} - -function groupBy -[[a]] ::= eq::(Boolean ::= a a) l::[a] -{ - local attribute helpercall :: Pair<[a] [a]>; - helpercall = groupByHelp(eq, head(l), l); - - return if null(l) then [] - else helpercall.fst :: if null(helpercall.snd) then [] - else groupBy(eq, helpercall.snd); -} -function groupByHelp -- do not use -Pair<[a] [a]> ::= eq::(Boolean ::= a a) f::a l::[a] -{ - -- f is the representative element we're comparing with, but is not considered - -- included when we're called. - local attribute recurse :: Pair<[a] [a]>; - recurse = groupByHelp(eq, f, tail(l)); - - return if null(l) || !eq(f, head(l)) - then pair([], l) - else pair(head(l) :: recurse.fst, recurse.snd); -} - -{-- - - Inserts the separator in between all elements of the list. - -} -function intersperse -[a] ::= sep::a xs::[a] -{ return if null(xs) then [] - else if null(tail(xs)) then xs - else head(xs) :: sep :: intersperse(sep, tail(xs)); -} - - --- Set operations -function unionBy -[a] ::= eq::(Boolean ::= a a) l::[a] r::[a] -{ - return if null(l) then r - else - (if containsBy(eq, head(l), r) - then [] - else [head(l)]) - ++ unionBy(eq, tail(l), r); -} - -function intersectBy -[a] ::= eq::(Boolean ::= a a) l::[a] r::[a] -{ - return if null(l) then [] - else - (if containsBy(eq, head(l), r) - then [head(l)] - else []) - ++ intersectBy(eq, tail(l), r); -} - -function unionsBy -[a] ::= eq::(Boolean ::= a a) ss::[[a]] -{ - return nubBy(eq, concat(ss)); -} - -function powerSet -[[a]] ::= xs::[a] -{ - return - case xs of - | h :: t -> - let rest::[[a]] = powerSet(t) - in rest ++ map(cons(h, _), rest) - end - | [] -> [[]] - end; -} - - --- Boolean list operations -function all -Boolean ::= l::[Boolean] -{ - return foldr(\ a::Boolean b::Boolean -> a && b, true, l); -} - -function any -Boolean ::= l::[Boolean] -{ - return foldr(\ a::Boolean b::Boolean -> a || b, false, l); -} - --------------------------------------------------------------------------------- - -function nil -[a] ::= -{ - return decorate i_nilList() with {}; -} foreign { - "java" : return "common.ConsCell.nil"; -} - -function cons -[a] ::= h::a t::[a] -{ - return decorate i_consList(h, t) with {}; -} foreign { - "java" : return "new common.ConsCell(%?h?%, %?t?%)"; -} - -function append -[a] ::= l1::[a] l2::[a] -{ - return if l1.i_emptyList - then l2 - else cons(head(l1), append(tail(l1), l2)); -} foreign { - "java" : return "common.AppendCell.append(%l1%, %?l2?%)"; -} - - -function null -Boolean ::= l::[a] -{ - return l.i_emptyList; -} foreign { - "java" : return "%l%.nil()"; -} - -function listLength -- not called 'length' since this is a builtin language feature, but thats how you should call it. -Integer ::= l::[a] -{ - return l.i_lengthList; -} foreign { - "java" : return "Integer.valueOf(%l%.length())"; -} - -function head -a ::= l::[a] -{ - return l.i_headList; -} foreign { - "java" : return "%l%.head()"; -} - -function tail -[a] ::= l::[a] -{ - return l.i_tailList; -} foreign { - "java" : return "%l%.tail()"; -} - --------------------------------------------------------------------------------- - -{- Note to self: Remember that the type equivalence of ['a] is Decorated List<'a>. - It can get confusing if you believe that ['a] is List<'a>. (NOT TRUE) - -} - - -synthesized attribute i_headList :: a; -synthesized attribute i_tailList :: Decorated List; -synthesized attribute i_emptyList :: Boolean; -synthesized attribute i_lengthList :: Integer; - -nonterminal List with i_headList, i_tailList, i_emptyList, i_lengthList; - -abstract production i_nilList -l::List ::= -{ - l.i_emptyList = true; - l.i_lengthList = 0; - l.i_headList = error("requested head of nil"); - l.i_tailList = error("requested tail of nil"); -} - -abstract production i_consList -l::List ::= h::a t::Decorated List -{ - l.i_emptyList = false; - l.i_lengthList = t.i_lengthList + 1; - l.i_headList = h; - l.i_tailList = t; -} - diff --git a/grammars/core/Location.sv b/grammars/core/Location.sv deleted file mode 100644 index ae8b47020..000000000 --- a/grammars/core/Location.sv +++ /dev/null @@ -1,99 +0,0 @@ -grammar core; - -annotation location :: Location; - -{-- - - Data structure storing location information on tree nodes from a parse. - -} -nonterminal Location with filename, line, column, endLine, endColumn, index, endIndex; - -synthesized attribute filename :: String; -synthesized attribute line :: Integer; -synthesized attribute column :: Integer; -synthesized attribute endLine :: Integer; -synthesized attribute endColumn :: Integer; -synthesized attribute index :: Integer; -synthesized attribute endIndex :: Integer; - -{-- - - The main constructor for location information. - - - - filename, line and column can be mutated by action blocks during parsing, - - but character index cannot. - - - - @param filename The "virtual filename". Initially whatever the parser is given. - - @param line (Beginning) line number, inclusive. Lines are numbered starting with 1. - - @param column (Beginning) column number, inclusive. Columns are numbered starting with 0. (For now.) - - @param endLine (Ending) line number, inclusive. - - @param endColumn (Ending) column number, exclusive. - - @param index (Beginning) character index, inclusive. - - @param endIndex (Ending) character index, exclusive. - - - - e.g. "Hi" as an entire file contents would have its entire location as: - - (_, 1, 0, 1, 2, 0, 2) - -} -abstract production loc -top::Location ::= filename::String line::Integer column::Integer - endLine::Integer endColumn::Integer - index::Integer endIndex::Integer -{ - top.filename = filename; - top.line = line; - top.column = column; - top.endLine = endLine; - top.endColumn = endColumn; - top.index = index; - top.endIndex = endIndex; -} - -{-- - - A secondary constructor for location information, for locations not from source code - - - - @param text The text to return as unparse as defined in langutil - -} -abstract production txtLoc -top::Location ::= text::String -{ - top.filename = "N/A"; - top.line = -1; - top.column = -1; - top.endLine = -1; - top.endColumn = -1; - top.index = -1; - top.endIndex = -1; -} - -{-- - - A helper constructor for location information, for built-in locations - - - - @param module The name of the extension/modifcation/module defining the location - -} -function builtinLoc -Location ::= module::String -{ - return txtLoc("Built in from " ++ module); -} - -{-- - - A helper constructor for location information, for invalid or undefined bogus locations - -} -function bogusLoc -Location ::= -{ - return txtLoc("Invalid or undefined bogus location"); -} - -{-- - - Less than or equal predicate, for use with sortBy, if desired. - -} -function locationLte -Boolean ::= l1::Location l2::Location -{ - -- TODO: We could probably just compare based on filename and index - -- For the moment, though, use line & column instead. - return l1.filename < l2.filename || (l1.filename == l2.filename && - (l1.line < l2.line || (l1.line == l2.line && - (l1.column < l2.column)))); -} - - diff --git a/grammars/core/Math.sv b/grammars/core/Math.sv deleted file mode 100644 index ff18581a0..000000000 --- a/grammars/core/Math.sv +++ /dev/null @@ -1,29 +0,0 @@ -grammar core; - -{-- - - Returns the maximum of two numbers. - -} -function max -Integer ::= l::Integer r::Integer -{ - return if l < r then r else l; -} - -{-- - - Returns the minimum of two numbers. - -} -function min -Integer ::= l::Integer r::Integer -{ - return if l < r then l else r; -} - -{-- - - Returns the greatest common divisor of two numbers. - -} -function gcd -Integer ::= l::Integer r::Integer -{ - return if r == 0 then l else gcd(r, l % r); -} - diff --git a/grammars/core/Maybe.sv b/grammars/core/Maybe.sv deleted file mode 100644 index 288576b95..000000000 --- a/grammars/core/Maybe.sv +++ /dev/null @@ -1,100 +0,0 @@ -grammar core; - -synthesized attribute fromJust :: a; -synthesized attribute isJust :: Boolean; - -nonterminal Maybe with fromJust, isJust; - -abstract production just -top::Maybe ::= v::a -{ - top.fromJust = v; - top.isJust = true; -} - -abstract production nothing -top::Maybe ::= -{ - top.fromJust = error("fromJust accessed on a Maybe that was actually nothing!"); - top.isJust = false; -} - --------------------------------------------------------------------------------- - -{-- - - The corresponding fold for Maybes. - - - - @param otherwise The element to return if 'ifJust' is 'nothing' - - @param ifJust The maybe value to scrutinize - - @return Either the contents of the Maybe (if 'just'), or the otherwise element. - -} -function fromMaybe -a ::= otherwise::a ifJust::Maybe -{ - return if ifJust.isJust then ifJust.fromJust else otherwise; -} - -{-- - - Selects the first existing element, favoring the left. - - - - @param l The first element - - @param r The second element - - @return A wrapped element, if any, favoring 'l' - -} -function orElse -Maybe ::= l::Maybe r::Maybe -{ - return if l.isJust then l else r; -} - -{-- - - Maybe cons a value to a list, or not. - - - - @param h If a value, the value to cons onto the list. - - @param t The list to amend, if there's a value - - @return The list, possibly with a new value at its head. - -} -function consMaybe -[a] ::= h::Maybe t::[a] -{ - return if h.isJust then h.fromJust :: t else t; -} - -{-- - - Turn a list of possible values into a list of values, skipping over - - any 'nothing's. - - - - @param l A list of optional values - - @return The list with all absent values removed, and present values unwrapped. - -} -function catMaybes -[a] ::= l::[Maybe] -{ - return foldr(consMaybe, [], l); -} - -{-- - - Maps a function over the value inside of a Maybe, if it exists. - -} -function mapMaybe -Maybe ::= f::(b ::= a) m::Maybe -{ - return case m of - | just(x) -> just(f(x)) - | nothing() -> nothing() - end; -} - -{-- - - Finds the first value matching a predicate. - -} -function find -Maybe ::= f::(Boolean ::= a) l::[a] -{ - return if null(l) then - nothing() - else if f(head(l)) then - just(head(l)) - else - find(f, tail(l)); -} diff --git a/grammars/core/Project.sv b/grammars/core/Project.sv deleted file mode 100644 index 2a70d5993..000000000 --- a/grammars/core/Project.sv +++ /dev/null @@ -1,4 +0,0 @@ -grammar core; - -imports core:monad; -imports core:reflect; diff --git a/grammars/core/String.sv b/grammars/core/String.sv deleted file mode 100644 index 7274973b0..000000000 --- a/grammars/core/String.sv +++ /dev/null @@ -1,363 +0,0 @@ -grammar core; - -{-- - - Fold a list of strings into one string, by interspersing a separator. - - - - @param sep The separator to place between each string. - - @param lst The list of string to collapse. - - @return The combined string. - -} -function implode -String ::= sep::String lst::[String] -{ - return if null(lst) - then "" - else head(lst) ++ if null(tail(lst)) - then "" - else sep ++ implode(sep, tail(lst)); -} - -{-- - - Split a string into a list of strings by a separator. If the separtor - - is the empty string then the string is split into single character strings. - - - - @param sep The separator between each of the resulting strings. - - @param str The original string. - - @return The list of strings separated by sep in the original string. - -} -function explode -[String] ::= sep::String str::String -{ - return if sep=="" then explodeSingle(str) - else if str == "" then [] - else explodeNormal(sep, str); -} -function explodeNormal -- do not use -[String] ::= sep::String str::String -{ - local attribute i :: Integer; - i = indexOf(sep, str); - - return if i == -1 - then [str] - else substring(0, i, str) :: - explodeNormal(sep, substring(i+length(sep), length(str), str)); -} -function explodeSingle -- do not use -[String] ::= str::String -{ - return if length(str) == 0 - then [] - else substring(0,1,str) :: - explodeSingle (substring(1,length(str),str)); -} - -{-- - - Find the index of a needle in the haystack. (Indices are 0-based.) - - - - @param needle The string to find. - - @param haystack The string to find it in. - - @return The index the string occurs at, or -1 if not found. - -} -function indexOf -Integer ::= needle::String haystack::String -{ - return error("Not Yet Implemented: indexOf"); -} foreign { - "java" : return "Integer.valueOf(%haystack%.toString().indexOf(%needle%.toString()))"; -} - -{-- - - Find the LAST index of a needle in the haystack. (Indices are 0-based.) - - - - @param needle The string to find. - - @param haystack The string to find it in. - - @return The index the string occurs at, or -1 if not found. - -} -function lastIndexOf -Integer ::= needle::String haystack::String -{ - return error("Not Yet Implemented: lastIndexOf"); -} foreign { - "java" : return "Integer.valueOf(%haystack%.toString().lastIndexOf(%needle%.toString()))"; -} - -{-- - - Return a substring of the original. Indices are 0-based. - - - - @param start The 0-based index to start at. Inclusive. - - @param endl The 0-based index to end before. (Exclusive.) - - @param str The original string. - - @return The resulting substring. - -} -function substring -String ::= start::Integer endl::Integer str::String -{ - return error("Not Yet Implemented: substring"); -} foreign { - "java" : return "(new common.StringCatter(%str%.toString().substring(%start%, %endl%)))"; -} - -{-- - - Tests if one string is a prefix of another - - - - @param pre The prefix. - - @param s The string to check the prefix of. - - @return true if pre is a prefix of s. false otherwise. - -} -function startsWith -Boolean ::= pre::String s::String -{ - return error("Not Yet Implemented: startsWith"); -} foreign { - "java" : return "Boolean.valueOf(%s%.toString().startsWith(%pre%.toString()))"; -} - -{-- - - Tests if one string is a postfix of another - - - - @param post The postfix. - - @param s The string to check the postfix of. - - @return true if post is a postfix of s. false otherwise. - -} -function endsWith -Boolean ::= post::String s::String -{ - return error("Not Yet Implemented: endsWith"); -} foreign { - "java" : return "Boolean.valueOf(%s%.toString().endsWith(%post%.toString()))"; -} - -{-- - - Replaces all instances of 'search' with 'replace' in 'str' - - - - @param search The string to replace - - @param replace The string to substitute in - - @param str The string to operate on - - @return The modified form of 'str' - -} -function substitute -String ::= search::String replace::String str::String -{ - return error("Not Yet Implemented: substitute"); -} foreign { - "java" : return "new common.StringCatter(%str%.toString().replace((CharSequence)%search%.toString(),(CharSequence)%replace%.toString()))"; -} - -{-- - - Return a string with 's' repeated 'n' times. - - - - @param n The number of times to repeat the string - - @param s The string to repeat - - @return The string with 'n' copies of 's' - -} -function replicate -String ::= n::Integer s::String -{ return error("Not Yet Implemented: replicate"); } -foreign { - "java" : return "new common.StringCatter(new String(new char[%n%.intValue()]).replace(\"\\0\", %s%.toString()))"; -} - - -{-- - - Tests if all characters of a string are digits. Partially unicode aware. - - See java's Character.isDigit(char). - - - - @param str The string to check - - @return true if all characters are digits. false otherwise. - -} -function isDigit -Boolean ::= str::String -{ - return error("Not Yet Implemented: isDigit"); -} foreign { - "java" : return "common.Util.isDigit(%str%.toString())"; -} - -{-- - - Tests if all characters of a string are letters. Partially unicode aware. - - See java's Character.isLetter(char). - - - - @param str The string to check - - @return true if all characters are letters. false otherwise. - -} -function isAlpha -Boolean ::= str::String -{ - return error("Not Yet Implemented: isAlpha"); -} foreign { - "java" : return "common.Util.isAlpha(%str%.toString())"; -} - -{-- - - Tests if all characters of a string are whitespace. Partially unicode aware. - - See java's Character.isWhitespace(char). - - - - Includes space, tab, newline, carriage return, and more. - - - - @param str The string to check - - @return true if all characters are whitespace. false otherwise. - -} -function isSpace -Boolean ::= str::String -{ - return error("Not Yet Implemented: isSpace"); -} foreign { - "java" : return "common.Util.isSpace(%str%.toString())"; -} - -{-- - - Tests if all characters of a string are lower case. Partially unicode aware. - - See java's Character.isLowerCase(char). - - - - @param str The string to check - - @return true if all characters are lower case. false otherwise. - -} -function isLower -Boolean ::= str::String -{ - return error("Not Yet Implemented: isLower"); -} foreign { - "java" : return "common.Util.isLower(%str%.toString())"; -} - -{-- - - Tests if all characters of a string are upper case. Partially unicode aware. - - See java's Character.isUpperCase(char). - - - - @param str The string to check - - @return true if all characters are upper case. false otherwise. - -} -function isUpper -Boolean ::= str::String -{ - return error("Not Yet Implemented: isUpper"); -} foreign { - "java" : return "common.Util.isUpper(%str%.toString())"; -} - -{-- - - Safely converts a string to an integer. - - - - @param str The string to convert - - @return The converted integer wrapped in just, or nothing if the - - conversion failed (e.g. not a number, or the number was too large) - -} -function toIntSafe -Maybe ::= str::String -{ - return error("Not Yet Implemented: toIntSafe"); -} foreign { - "java" : return "common.Util.safetoInt(%str%.toString())"; -} - -{-- - - Concatenates a list of strings. - - - - @param lst A list of strings - - @return The flattened string - -} -function sconcat -String ::= lst::[String] -{ - return foldr(stringConcat, "", lst); -} - -{-- - - Map a function over a list, and then conatenates the results together. - - - - @param f A function to apply to each element of a list, returning a string. - - @param lst A list - - @return The concatenated string - -} -function sflatMap -String ::= f::(String ::= a) lst::[a] -{ - return sconcat(map(f, lst)); -} - -{-- - - A comparison function for strings. - - @return Negative if lr - -} -function compareString -Integer ::= l::String r::String -{ - return if l <= r then if l == r then 0 else -1 else 1; -} foreign { - -- This is temporary until we have better analysis & translation of Silver functions. - "java" : return "Integer.valueOf(%l%.toString().compareTo(%r%.toString()))"; -} - -{-- - - String append. Useful for higher order functions. - -} -function stringConcat -String ::= s1::String s2::String -{ return s1 ++ s2; } - -{-- - - String equality test. Useful for some "...By" higher order functions. - -} -function stringEq -Boolean ::= s1::String s2::String -{ - return s1 == s2; -} - -{-- - - String <= test. Useful for some "...By" higher order functions. (like sortBy) - -} -function stringLte -Boolean ::= s1::String s2::String -{ - return s1 <= s2; -} - -{-- - - Converts a list of code points to a string. Note that due to Java's use of - - UCS-2, code points greater than 0xFFFF (i.e. and characters outside the Basic - - Multilingual Plane) aren't supported. - -} -function charsToString -String ::= chars::[Integer] -{ - return error("Foreign Function"); -} foreign { - "java" : return "common.StringCatter.fromChars(%chars%)"; -} - -{-- - - Converts a string to a list of its UCS-2 characters. Note that this means - - that surrogate pairs are (probably?) not supported, and characters outside - - the Basic Multilingual Plane aren't as a consequence. - -} -function stringToChars -[Integer] ::= str::String -{ - return error("Foreign Function"); -} foreign { - "java" : return "%str%.toChars()"; -} - -{-- - - Replace all special characters in a string with their escape sequences. - -} -function escapeString -String ::= s::String -{ - return error("Foreign function"); -} foreign { - "java" : return "(common.Util.escapeString(%s%))"; -} - -{-- - - Replace all escape sequences in a string with corresponding special characters. - -} -function unescapeString -String ::= s::String -{ - return error("Foreign function"); -} foreign { - "java" : return "(common.Util.unescapeString(%s%))"; -} diff --git a/grammars/core/TerminalId.sv b/grammars/core/TerminalId.sv deleted file mode 100644 index 15e2039f6..000000000 --- a/grammars/core/TerminalId.sv +++ /dev/null @@ -1,28 +0,0 @@ -grammar core; - -function terminalIdEq -Boolean ::= t1::TerminalId t2::TerminalId -{ - return t1 == t2; -} - -function terminalIdLte -Boolean ::= t1::TerminalId t2::TerminalId -{ - return t1 <= t2; -} - -function terminalSetEq -Boolean ::= ts1::[TerminalId] ts2::[TerminalId] -{ - return - length(ts1) == length(ts2) && - all(zipWith(terminalIdEq, sortBy(terminalIdLte, ts1), sortBy(terminalIdLte, ts2))); -} - -function terminalSubset -Boolean ::= ts1::[TerminalId] ts2::[TerminalId] -{ - -- Probably more efficient than sorting if ts1 is small? - return all(map(containsBy(terminalIdEq, _, ts2), ts1)); -} diff --git a/grammars/core/Unit.sv b/grammars/core/Unit.sv deleted file mode 100644 index 4a5c085b8..000000000 --- a/grammars/core/Unit.sv +++ /dev/null @@ -1,7 +0,0 @@ -grammar core; - -nonterminal Unit; - -abstract production unit -top::Unit ::= -{} \ No newline at end of file diff --git a/grammars/core/monad/DocConfig.sv b/grammars/core/monad/DocConfig.sv deleted file mode 100644 index dab9302bb..000000000 --- a/grammars/core/monad/DocConfig.sv +++ /dev/null @@ -1,5 +0,0 @@ -grammar core:monad; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Monad support\nmenu_title: Monads\nmenu_weight: 50\n---" -@} diff --git a/grammars/core/monad/Either.sv b/grammars/core/monad/Either.sv deleted file mode 100644 index eb28e43cf..000000000 --- a/grammars/core/monad/Either.sv +++ /dev/null @@ -1,18 +0,0 @@ -grammar core:monad; - --- Here we treat left as 'failure' and right as 'success' - -function bindEither -Either ::= m::Either fn::(Either ::= b) -{ - return case m of - left(x) -> left(x) - | right(x) -> fn(x) - end; -} - -function returnEither -Either ::= x::b -{ - return right(x); -} diff --git a/grammars/core/monad/IO.sv b/grammars/core/monad/IO.sv deleted file mode 100644 index c8d4e578d..000000000 --- a/grammars/core/monad/IO.sv +++ /dev/null @@ -1,189 +0,0 @@ -grammar core:monad; - -nonterminal IOMonad with stateIn, stateOut, stateVal; - -abstract production bindIO -top::IOMonad ::= st::IOMonad fn::(IOMonad ::= a) -{ - st.stateIn = top.stateIn; - local newState::IOMonad = fn(st.stateVal); - newState.stateIn = st.stateOut; - local stateOut::IO = newState.stateOut; - local stateVal::b = newState.stateVal; - - -- Using unsafeTrace here to demand st is evaluated before evaluating fn - top.stateOut = unsafeTrace(stateOut, st.stateOut); - top.stateVal = unsafeTrace(stateVal, st.stateOut); -} - -abstract production returnIO -top::IOMonad ::= x::a -{ - top.stateOut = top.stateIn; - top.stateVal = x; -} - -function runIO -IO ::= st::IOMonad ioIn::IO -{ - return evalIO(st, ioIn).io; -} - -function evalIO -IOVal ::= st::IOMonad ioIn::IO -{ - st.stateIn = ioIn; - return ioval(st.stateOut, st.stateVal); -} - -function unsafeEvalIO -a ::= st::IOMonad -{ - return evalIO(st, unsafeIO()).iovalue; -} - --- Monadic IO wrappers -abstract production printM -top::IOMonad ::= s::String -{ - top.stateOut = print(s, top.stateIn); - top.stateVal = unit(); -} - -abstract production readLineStdinM -top::IOMonad ::= -{ - local res::IOVal = readLineStdin(top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - --- Having a polymorphic return type lets us write code like: --- --- if !null(errs) { --- printM(showErrs(errs)); --- exitM(1); --- } else { --- return value; --- } -abstract production exitM -top::IOMonad ::= val::Integer -{ - top.stateOut = exit(val, top.stateIn); - top.stateVal = error("stateOut should've been evaluated first?"); -} - -abstract production mkdirM -top::IOMonad ::= s::String -{ - local res::IOVal = mkdir(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production systemM -top::IOMonad ::= s::String -{ - local res::IOVal = system(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production writeFileM -top::IOMonad ::= file::String contents::String -{ - top.stateOut = writeFile(file, contents, top.stateIn); - top.stateVal = unit(); -} - -abstract production appendFileM -top::IOMonad ::= file::String contents::String -{ - top.stateOut = appendFile(file, contents, top.stateIn); - top.stateVal = unit(); -} - -abstract production fileTimeM -top::IOMonad ::= s::String -{ - local res::IOVal = fileTime(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production isFileM -top::IOMonad ::= s::String -{ - local res::IOVal = isFile(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production isDirectoryM -top::IOMonad ::= s::String -{ - local res::IOVal = isDirectory(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production readFileM -top::IOMonad ::= s::String -{ - local res::IOVal = readFile(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production cwdM -top::IOMonad ::= -{ - local res::IOVal = cwd(top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production envVarM -top::IOMonad ::= s::String -{ - local res::IOVal = envVar(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production listContentsM -top::IOMonad<[String]> ::= s::String -{ - local res::IOVal<[String]> = listContents(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production deleteFileM -top::IOMonad ::= s::String -{ - local res::IOVal = deleteFile(s, top.stateIn); - top.stateOut = res.io; - top.stateVal = res.iovalue; -} - -abstract production deleteTreeM -top::IOMonad ::= s::String -{ - top.stateOut = deleteTree(s, top.stateIn); - top.stateVal = unit(); -} - -abstract production copyFileM -top::IOMonad ::= src::String dst::String -{ - top.stateOut = copyFile(src, dst, top.stateIn); - top.stateVal = unit(); -} - -abstract production touchFileM -top::IOMonad ::= file::String -{ - top.stateOut = touchFile(file, top.stateIn); - top.stateVal = unit(); -} diff --git a/grammars/core/monad/List.sv b/grammars/core/monad/List.sv deleted file mode 100644 index 4b793533b..000000000 --- a/grammars/core/monad/List.sv +++ /dev/null @@ -1,13 +0,0 @@ -grammar core:monad; - -function bindList -[b] ::= l::[a] fn::([b] ::= a) -{ - return flatMap(fn, l); -} - -function returnList -[a] ::= x::a -{ - return [x]; -} diff --git a/grammars/core/monad/Maybe.sv b/grammars/core/monad/Maybe.sv deleted file mode 100644 index 8266e2fdf..000000000 --- a/grammars/core/monad/Maybe.sv +++ /dev/null @@ -1,17 +0,0 @@ -grammar core:monad; - -function bindMaybe -Maybe ::= m::Maybe fn::(Maybe ::= a) -{ - return case m of - just(x) -> fn(x) - | nothing() -> nothing() - end; -} - ---global returnMaybe::(Maybe ::= a) = just; -- TODO: Why doesn't this work? -function returnMaybe -Maybe ::= x::a -{ - return just(x); -} \ No newline at end of file diff --git a/grammars/core/monad/State.sv b/grammars/core/monad/State.sv deleted file mode 100644 index ff48f97b8..000000000 --- a/grammars/core/monad/State.sv +++ /dev/null @@ -1,52 +0,0 @@ -grammar core:monad; - -nonterminal State with stateIn, stateOut, stateVal; - -inherited attribute stateIn::s; -synthesized attribute stateOut::s; -synthesized attribute stateVal::a; - -abstract production bindState -top::State ::= st::State fn::(State ::= a) -{ - st.stateIn = top.stateIn; - local newState::State = fn(st.stateVal); - newState.stateIn = st.stateOut; - top.stateOut = newState.stateOut; - - top.stateVal = newState.stateVal; -} - -abstract production returnState -top::State ::= x::a -{ - top.stateOut = top.stateIn; - top.stateVal = x; -} - -abstract production getState -top::State ::= -{ - top.stateOut = top.stateIn; - top.stateVal = top.stateIn; -} - -abstract production setState -top::State ::= newState::s -{ - top.stateOut = newState; - top.stateVal = unit(); -} - -function runState -Pair ::= st::State initialState::s -{ - st.stateIn = initialState; - return pair(st.stateOut, st.stateVal); -} - -function evalState -a ::= st::State initialState::s -{ - return runState(st, initialState).snd; -} \ No newline at end of file diff --git a/grammars/core/reflect/AST.sv b/grammars/core/reflect/AST.sv deleted file mode 100644 index d146e0a50..000000000 --- a/grammars/core/reflect/AST.sv +++ /dev/null @@ -1,64 +0,0 @@ -grammar core:reflect; - --- This grammar contains only the defintions of the AST nonterminals, needed by the runtime library --- The full reflection library that users should import is silver:reflect - -nonterminal AST; - -abstract production nonterminalAST -top::AST ::= prodName::String children::ASTs annotations::NamedASTs -{} - -abstract production terminalAST -top::AST ::= terminalName::String lexeme::String location::Location -{} - -abstract production listAST -top::AST ::= vals::ASTs -{} - -abstract production stringAST -top::AST ::= s::String -{} - -abstract production integerAST -top::AST ::= i::Integer -{} - -abstract production floatAST -top::AST ::= f::Float -{} - -abstract production booleanAST -top::AST ::= b::Boolean -{} - -abstract production anyAST -top::AST ::= x::a -{} - -nonterminal ASTs; - -abstract production consAST -top::ASTs ::= h::AST t::ASTs -{} - -abstract production nilAST -top::ASTs ::= -{} - -nonterminal NamedASTs; - -abstract production consNamedAST -top::NamedASTs ::= h::NamedAST t::NamedASTs -{} - -abstract production nilNamedAST -top::NamedASTs ::= -{} - -nonterminal NamedAST; - -abstract production namedAST -top::NamedAST ::= n::String v::AST -{} diff --git a/grammars/ide/IdeProject.sv b/grammars/ide/IdeProject.sv deleted file mode 100644 index 534358952..000000000 --- a/grammars/ide/IdeProject.sv +++ /dev/null @@ -1,58 +0,0 @@ -grammar ide; - -type IdeProject foreign; --- TODO: because we don't give Java translation of foreign types, we only pass them around as "Object" --- This requires our runtime to accept "Object" and then cast it to IProject or whatever. UGLY! - - --- Get the name of the given project. -function getProjectName -IOVal ::= proj::IdeProject i::IO -{ - return error("Not Yet Implemented: getName"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getProjectName(%proj%, %i%)"; -} - --- Refresh the given project down to given depth, for which only pre-defined --- constants (0, 1, indefinite) can be used. -function refreshProject -IO ::= proj::IdeProject i::IO -{ - return error("Not Yet Implemented: refresh"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.refreshProject(%proj%, %i%)"; -} - --- Get the absoluet path of given project. -function getProjectPath -IOVal ::= proj::IdeProject i::IO -{ - return error("Not Yet Implemented: getProjectPath"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getAbsoluteProjectPath(%proj%, %i%)"; -} - --- Get the generated path of given project, relative to root. -function getGeneratedPath -IOVal ::= proj::IdeProject i::IO -{ - return error("Not Yet Implemented: getGeneratedPath"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getGeneratedProjectPath(%proj%, %i%)"; -} - --- Get members (direct sub-resources) of given project. -function getProjectMembers -IOVal> ::= proj::IdeProject i::IO -{ - return error("Not Yet Implemented: getProjectMembers"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getProjectMembers(%proj%, %i%)"; -} - diff --git a/grammars/ide/IdeProperty.sv b/grammars/ide/IdeProperty.sv deleted file mode 100644 index d3a8edd4b..000000000 --- a/grammars/ide/IdeProperty.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar ide; - -synthesized attribute propName :: String; -synthesized attribute propType :: String; -synthesized attribute propValue :: String; - -nonterminal IdeProperty with propName, propType, propValue; - -abstract production makeIdeProperty -top::IdeProperty ::= propName::String propType::String propValue::String -{ - top.propName = propName; - top.propType = propType; - top.propValue = propValue; -} - -{-- lookupIdeProperty - - prop : name of property to lookup - - args : list of IdePropertys - - - - output : if the named property was found, then a just containing the string - - representing the value, otherwise a nothing - -} -function lookupIdeProperty -Maybe ::= prop::String args::[IdeProperty] -{ - return - if null(args) - then nothing() - else if head(args).propName == prop - then just(head(args).propValue) - else lookupIdeProperty(prop, tail(args)); -} diff --git a/grammars/ide/IdeResource.sv b/grammars/ide/IdeResource.sv deleted file mode 100644 index 66419bde0..000000000 --- a/grammars/ide/IdeResource.sv +++ /dev/null @@ -1,154 +0,0 @@ -grammar ide; - -type IdeResource foreign; - --- Delete a resource, returning true if successful. -function delete -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: delete"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.deleteResource(%res%, %i%)"; -} - --- Copy a resource to another location within the project, using a path --- relative to project’s root. Returns true if successful. -function copyTo -IOVal ::= res::IdeResource path::String i::IO -{ - return error("Not Yet Implemented: copyTo"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.copyResource(%res%, %path%, %i%)"; -} - --- Move (shortcut for copy and delete) a resource to another location within --- the project, using a path relative to project’s root. Returns true if successful. -function moveTo -IOVal ::= res::IdeResource path::String i::IO -{ - return error("Not Yet Implemented: moveTo"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.moveResource(%res%, %path%, %i%)"; -} - --- Create a new file with given input as contents. The path is relative to --- project root. Returns the resource if created successfully. -function createFile -IOVal> ::= proj::IdeProject path::String input::String i::IO -{ - return error("Not Yet Implemented: createFile"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.createResource(%proj%, %path%, %input%, %i%)"; -} - --- Create a new folder. Returns the resource if created successfully. -function createFolder -IOVal> ::= proj::IdeProject path::String i::IO -{ - return error("Not Yet Implemented: createFolder"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.createFolderResource(%proj%, %path%, %i%)"; -} - --- Get the path of given resource, relative to project’s root. An empty string would be returned if the resource is linked. -function getRelativePath -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: getRelativePath"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getRelativePath(%res%, %i%)"; -} - --- Get the absolute path of given resource. -function getAbsolutePath -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: getAbsolutePath"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getAbsolutePath(%res%, %i%)"; -} - --- Get members of given resource. Returns an empty list if the resource is a --- file. -function getResourceMembers -IOVal<[IdeResource]> ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: getResourceMembers"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.getResourceMembers(%res%, %i%)"; -} - --- Check if the resource is a folder -function resourceIsFolder -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: isFolder"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceIsFolder(%res%, %i%)"; -} - --- Check if the resource is a file. -function resourceIsFile -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: isFile"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceIsFile(%res%, %i%)"; -} - --- Check whether a resource is linked from outside project’s physical location. --- This is a feature from Eclipse. -function resourceIsLinked -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: isLinked"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceIsLinked(%res%, %i%)"; -} - --- Check whether a resource is hidden in Eclipse. That is, invisible to IDE --- users. -function resourceIsHidden -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: isHidden"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceIsHidden(%res%, %i%)"; -} - --- Determine if a resource is marked as derived. “Derived”, in Eclipse’s --- terminology, means a resource is the result of automatic generation, such --- as compilation, out of another resource(s). Such resource is thus not --- subject to user’s manual modification. -function resourceIsDerived -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: isDerived"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceIsDerived(%res%, %i%)"; -} - --- Check whether the resource does exist. After deleting a resource --- successfully, calling this function should return false. -function resourceExists -IOVal ::= res::IdeResource i::IO -{ - return error("Not Yet Implemented: exists"); -} foreign { - "java" : return - "edu.umn.cs.melt.ide.util.Util.resourceExists(%res%, %i%)"; -} - diff --git a/grammars/ide/IdeUtil.sv b/grammars/ide/IdeUtil.sv deleted file mode 100644 index b9681ada5..000000000 --- a/grammars/ide/IdeUtil.sv +++ /dev/null @@ -1,35 +0,0 @@ -grammar ide; - --- Temporary hack to add dependency in builds -import silver:langutil; - -{-- - Call IDE-embedded ant against a build file. - - buildFile: the full absolute path and file name (example: /home/username/workspace/proj01/build.xml) - arguments: the arguments in a single String (example: -a -b --lc -d"xxx") - target: the target to be invoked in build file. For now only one target is supported. ---} -function ant -IO ::= buildFile::String arguments::String target::String i::IO -{ - return error("Not Yet Implemented: ant"); -} foreign { - "java" : return "common.Util.io(%i%, edu.umn.cs.melt.ide.util.Util.ant(%buildFile%, %arguments%, %target%))"; -} - -{-- - - Gets a path to a resource (directory, file) that was included in the IDE bundle via - - a 'resource' declaration in the ide spec. - - (e.g. resource grammars "../../../grammars"; - - can be obtained via getIdeResource("grammar", io) ) - - NOT TO BE CONFUSED WITH IdeResource. (i.e. files in the project) - -} -function getIdeResource -IOVal ::= resourceid::String i::IO -{ - return error("Not Yet Implemented: getIdeResource"); -} foreign { - "java" : return "edu.umn.cs.melt.ide.util.Util.getIdeResource(%resourceid%, %i%)"; -} - diff --git a/grammars/lib/errors/DocConfig.sv b/grammars/lib/errors/DocConfig.sv deleted file mode 100644 index acac01f11..000000000 --- a/grammars/lib/errors/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:errors; - -{@config - no-doc:"true" -@} - diff --git a/grammars/lib/errors/Errors.sv b/grammars/lib/errors/Errors.sv deleted file mode 100644 index fd8ba2173..000000000 --- a/grammars/lib/errors/Errors.sv +++ /dev/null @@ -1,104 +0,0 @@ -grammar lib:errors ; - --- The 'errors' attribute is used to propogate error messages --- up the AST. --- It is a collection attribute so that aspect productions can --- add new error messages to existing language constructs, as --- needed. --- It type is [ Error ], where Error is a nonterminal type that --- consists of an error message and location information. - -synthesized attribute errors :: [ Error ] with ++ ; - -function mkNoError -[Error] ::= -{ return [ ] ; } - -function mkError -[Error] ::= msg::String -{ return [ msgError(msg) ] ; } - -function mkErrorLoc -[Error] ::= l::Location msg::String -{ return [ msgErrorLoc(l,msg) ] ; } - -function mkErrorLinCol -[Error] ::= l::Integer c::Integer msg::String -{ return [ msgErrorLoc(mkLocation(l,c),msg) ] ; } - -function mkErrorIf -[Error] ::= msg::String b::Boolean -{ return if b then [ msgError(msg) ] else [ ] ; } - - -nonterminal Error with message ; -synthesized attribute message :: String ; - -abstract production msgError -e::Error ::= msg::String -{ - e.message = msg ; -} - -abstract production msgErrorLoc -e::Error ::= l::Location msg::String -{ - e.message = "Error: " ++ l.str_location ++ "\n" ++ format_msg(msg) ; -} - - -function displayErrors -String ::= errs::[ Error ] -{ - return if null(errs) - then "" - else head(errs).message ++ - (if null(tail(errs)) then "" else "\n") ++ - displayErrors(tail(errs)) ; -} - - -function format_msg -String ::= str::String -{ - return if length(str) == 0 - then "" - else - if isSpace(str) - then "" - else - if first_newline_index == -1 - then padding ++ str - else padding ++ substring(0, first_newline_index, str) - ++ (if ! isSpace(rest) - then "\n" ++ format_msg(rest) - else "") ; - - local attribute first_newline_index :: Integer ; - first_newline_index = indexOf("\n", str); - - local attribute rest :: String ; - rest = substring(first_newline_index + 1, length(str), str) ; - - local attribute padding :: String ; - padding = " " ; -} - - - -{- - -We sometimes forward to an "erroneous construct" if some error -condition occurs on a production that needs to forward to something. -The production below is one that should be parameterized by the -nonterminal on the lhs. - -abstract production erroneous_Expr -expr::Expr ::= err_tree::Expr errs::[Error] { - expr.pp = "/* ERRONEOUS EXPRESSION */ " ++ err_tree.pp ; - expr.host_Expr = error ("From erroneous_Expr on " ++ expr.pp ++ " :\n" ++ displayErrors (errs)); - expr.errors := errs ; - expr.typerep = errorType (errs); -} - --} diff --git a/grammars/lib/errors/Location.sv b/grammars/lib/errors/Location.sv deleted file mode 100644 index d901ea282..000000000 --- a/grammars/lib/errors/Location.sv +++ /dev/null @@ -1,44 +0,0 @@ -grammar lib:errors ; - -synthesized attribute location :: Location ; - -nonterminal Location with lin, col, loc_filename, str_location ; - -synthesized attribute lin :: Integer ; -synthesized attribute col :: Integer ; -synthesized attribute loc_filename :: String ; -synthesized attribute str_location :: String ; - -abstract production mkLocation -loc::Location ::= l::Integer c::Integer -{ - loc.lin = l ; - loc.col = c ; - loc.loc_filename = "" ; - loc.str_location = "line: " ++ toString(l) ++ " column: " ++ toString(c) ++ - " file: " ++ loc.loc_filename ++ "." ; -} - - -abstract production mkLocationFile -loc::Location ::= l::Integer c::Integer fn::String -{ - loc.lin = l ; - loc.col = c ; - loc.loc_filename = fn ; - loc.str_location = "line: " ++ toString(l) ++ " column: " ++ toString(c) ++ - " file: " ++ loc.loc_filename ++ "." ; -} - - -{- - -Should location be defined on all terminals ? - -In ableC we have the following function for the Id_t terminal. - -function location_Id -Location ::= id::Identifier_t -{ return mkLocation2 (id.line,id.column,id.filename) ; } - --} diff --git a/grammars/lib/extcore/DocConfig.sv b/grammars/lib/extcore/DocConfig.sv deleted file mode 100644 index 0b4d98b92..000000000 --- a/grammars/lib/extcore/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:extcore; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Extended Core\nmenu_title: Extended Core\nmenu_weight: 100\n---" -@} - diff --git a/grammars/lib/extcore/Equality.sv b/grammars/lib/extcore/Equality.sv deleted file mode 100644 index 63c2ce19f..000000000 --- a/grammars/lib/extcore/Equality.sv +++ /dev/null @@ -1,71 +0,0 @@ -grammar lib:extcore ; - -{- We are accumulating functions in List.sv that require a function to - test for equality. It might be nice to establish a naming - convention for these, and other types of functions. - - EVW proposes that we name functions by a common prefix indicating - the computation followed by the name of the type of the values - involved. - - Thus, for equality testing functions we have names of the form - equalsTYPE - where TYPE is the actual name of the type of the values being tested. - Thus, equalsInteger, equalsString, equalsType, etc. - - Note that the function stringEq in core/String.sv does not follow - this convention. EVW thinks we should change it. --} - --- equals functions -function equalsInteger -Boolean ::= a::Integer b::Integer -{ return a == b ; } - -function equalsFloat -Boolean ::= a::Float b::Float -{ return a == b ; } - -function equalsString -Boolean ::= a::String b::String -{ return a == b ; } - -function equalsBoolean -Boolean ::= a::Boolean b::Boolean -{ return a == b ; } - -function equalsList -Boolean ::= eq::(Boolean ::= a a) l1::[a] l2::[a] -{ return if null(l1) - then null(l2) - else !null(l2) && - eq(head(l1), head(l2)) && - equalsList(eq, tail(l1), tail(l2)) ; -} - --- not equals functions --- (Can be removed if Silver gets curried functions.) -function notEqualsInteger -Boolean ::= a::Integer b::Integer -{ return a != b ; } - -function notEqualsFloat -Boolean ::= a::Float b::Float -{ return a != b ; } - -function notEqualsString -Boolean ::= a::String b::String -{ return a != b ; } - -function notEqualsBoolean -Boolean ::= a::Boolean b::Boolean -{ return a != b ; } - -function notEqualsList -Boolean ::= neq::(Boolean ::= a a) l1::[a] l2::[a] -{ return if null(l1) - then ! null(l2) - else null(l2) || - neq(head(l1), head(l2)) || - notEqualsList(neq, tail(l1), tail(l2)) ; -} diff --git a/grammars/lib/extcore/String.sv b/grammars/lib/extcore/String.sv deleted file mode 100644 index 2deda7e09..000000000 --- a/grammars/lib/extcore/String.sv +++ /dev/null @@ -1,140 +0,0 @@ -grammar lib:extcore ; - -{- We have a need for various "toString" functions. - - EVW proposes that we name functions by a common prefix indicating - the computation followed by the name of the type of the values - involved. - - For printing, toStringTYPE - toStringInteger, toStringType, etc. - - We need functions for many of the built-in types. These are included below. --} - --- toString functions -function toStringFromInteger -String ::= v::Integer -{ return toString(v) ; } - -function toStringFromFloat -String ::= v::Float -{ return toString(v) ; } - -function toStringFromBoolean -String ::= v::Boolean -{ return if v then "true" else "false" ; } - -function toStringFromString -String ::= v::String -{ return v ; } - -function toStringFromList -String ::= toStr::(String ::= a) xs::[a] -{ return "[" ++ toStringFromListHelper(toStr, xs) ++ "]" ; } - -function toStringFromListHelper -String ::= toStr::(String ::= a) xs::[a] -{ return if null(xs) - then "" - else toStr(head(xs)) ++ - if null(tail(xs)) - then "" - else ", " ++ toStringFromListHelper(toStr, tail(xs)) ; -} - -function stripWhiteSpace -String ::= s::String -{ return implode ("", stripWhiteSpaceHelper(explode("",s))) ; } - -function stripWhiteSpaceHelper -[String] ::= ss::[String] -{ return if null(ss) - then [ ] - else - if hd==" " || hd=="\n" || hd=="\t" - then stripWhiteSpaceHelper(tail(ss)) - else hd :: stripWhiteSpaceHelper(tail(ss)) ; - - local attribute hd::String ; - hd = head(ss) ; -} - -function replaceChars -String ::= toReplace::String replaceWith::String str::String -{ - return implode ("", replaceCharsHelper(toReplace, replaceWith, explode("",str)) ) ; -} - -function replaceCharsHelper -[String] ::= toReplace::String replaceWith::String chars::[String] -{ return - if null(chars) - then [ ] - else - if head(chars) == toReplace - then replaceWith :: replaceCharsHelper (toReplace, replaceWith, tail(chars)) - else head(chars) :: replaceCharsHelper (toReplace, replaceWith, tail(chars)) ; -} - ---function stripExtraWhiteSpace ---String ::= s::String ---{ return implode ("", stripExtraWhiteSpaceHelper(explode("",s))) ; } - -function stripExtraWhiteSpace -String ::= str::String -{ return implode ("", stripExtraWhiteSpaceHelper( - woLeadingOrEndingWhiteSpace)) ; - - local attribute woLeadingOrEndingWhiteSpace :: [String] ; - woLeadingOrEndingWhiteSpace - = reverse((dropWhile(isSpace, - reverse(dropWhile(isSpace, explode("",str)))))) ; -} - -function stripExtraWhiteSpaceHelper -[String] ::= ss::[String] -{ return if null(ss) - then [ ] - else - if hd==" " || hd=="\n" || hd=="\t" - then (if null(tail(ss)) - then [ ] - else (if nxt==" " || nxt=="\n" || nxt=="\t" - then stripExtraWhiteSpaceHelper(tail(ss)) -- drop hd - else " " :: stripExtraWhiteSpaceHelper(tail(ss)) - -- replace hd with " " - ) - ) - else hd :: stripExtraWhiteSpaceHelper(tail(ss)) ; - - local attribute hd::String ; - hd = head(ss) ; - - local attribute nxt::String ; - nxt = head(tail(ss)) ; -} - -function isNotWhiteSpace -Boolean ::= str::String -{ return ! isSpace(str) ; } - - - - -function addLineNumbers -String ::= code::String -{ return addLineNums(1, 2, lines) ; - local lines::[String] = explode("\n",code) ; -} - -function addLineNums -String ::= next::Integer width::Integer lines::[String] -{ return if null(lines) - then "" - else pad ++ ln ++ ": " ++ head(lines) ++ "\n" ++ - addLineNums(next+1, width, tail(lines)) ; - local ln::String = toString(next); - local pad::String = implode("", repeat(" ", width - length(ln)) ) ; -} - - diff --git a/grammars/lib/stringmap/DocConfig.sv b/grammars/lib/stringmap/DocConfig.sv deleted file mode 100644 index 41027e2a3..000000000 --- a/grammars/lib/stringmap/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:stringmap; - -{@config - no-doc:"true" -@} - diff --git a/grammars/lib/stringmap/StringMap.sv b/grammars/lib/stringmap/StringMap.sv deleted file mode 100644 index d9a4d3000..000000000 --- a/grammars/lib/stringmap/StringMap.sv +++ /dev/null @@ -1,52 +0,0 @@ -grammar lib:stringmap; - -deprecated "StringMap is inefficient and no longer recommended: use silver:util:treemap instead."; - --- hide the representation of StringMap, as we someday plan to make this a tree -nonterminal StringMap with bindings; - -synthesized attribute bindings :: [ Pair ] ; - - -abstract production emptyStringMap -e::StringMap ::= -{ - e.bindings = [ ] ; -} - -abstract production consStringMap -e::StringMap ::= name::String v::a env::StringMap -{ - e.bindings = cons( pair(name,v), env.bindings ) ; -} - -abstract production appendStringMap -e::StringMap ::= e1::StringMap e2::StringMap -{ - e.bindings = e1.bindings ++ e2.bindings ; -} - -function lookupStringMap -Maybe ::= name::String e::StringMap -{ - return lookup(name,e.bindings); -} - --- This should, perhaps, not be here... -function lookupAll -[a] ::= s::String m::[Pair] -{ - return if null(m) - then [] - else if head(m).fst == s - then head(m).snd :: lookupAll(s, tail(m)) - else lookupAll(s, tail(m)); -} -function lookup -Maybe ::= s::String m::[Pair] -{ - local all :: [a] = lookupAll(s,m); - return if null(all) then nothing() else just(head(all)); -} - - diff --git a/grammars/lib/system/DocConfig.sv b/grammars/lib/system/DocConfig.sv deleted file mode 100644 index cfb45ca63..000000000 --- a/grammars/lib/system/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:system; - -{@config - header:"---\nlayout: sv_wiki\ntitle: System\nmenu_title: System\nmenu_weight: 100\n---" -@} - diff --git a/grammars/lib/system/System.sv b/grammars/lib/system/System.sv deleted file mode 100644 index af8569518..000000000 --- a/grammars/lib/system/System.sv +++ /dev/null @@ -1,4 +0,0 @@ -grammar lib:system ; - -exports lib:system:filenames ; -exports lib:system:ioaction ; diff --git a/grammars/lib/system/filenames/FileNames.sv b/grammars/lib/system/filenames/FileNames.sv deleted file mode 100644 index fbaa9b627..000000000 --- a/grammars/lib/system/filenames/FileNames.sv +++ /dev/null @@ -1,64 +0,0 @@ -grammar lib:system:filenames ; - --- Functions for manipulating file names ------------------------------------------------------------- - --- retrun the base of the filename by removing any extension that --- appears after the last dot (".") in the name. -function baseFileName -String ::= path::String -{ - return if dotIndex == -1 - then path - else reverseString(substring(dotIndex+1,length(path),pr)) ; - - local attribute dotIndex::Integer ; - dotIndex = indexOf(".", pr) ; - - local attribute pr::String ; - pr = reverseString(path) ; -} - --- return the extension of a filename -function fileNameExt -String ::= path::String -{ return if dotIndex == -1 - then "" - else reverseString(substring(0,dotIndex,pr)) ; - local attribute dotIndex::Integer ; - dotIndex = indexOf(".", pr) ; - local attribute pr::String ; - pr = reverseString(path) ; -} - - --- support functions for reversing a string -function reverseString -String ::= s::String -{ return implode(reverseStringList(explode(s))) ; -} - -function reverseStringList -[String] ::= ss::[String] -{ - return if null(ss) - then [] - else reverseStringList (tail(ss)) ++ [ head(ss) ] ; -} - --- convert a string into a list of sinlge character strings -function explode -[String] ::= s::String -{ return if length(s) == 0 - then [] - else [ substring (0,1,s) ] ++ - explode ( substring(1,length(s),s) ) ; -} - --- concat a list of strings into a single string -function implode -String ::= ss::[String] -{ return if null(ss) - then "" - else head(ss) ++ implode(tail(ss)) ; -} diff --git a/grammars/lib/system/ioaction/IOaction.sv b/grammars/lib/system/ioaction/IOaction.sv deleted file mode 100644 index 38bf13ff5..000000000 --- a/grammars/lib/system/ioaction/IOaction.sv +++ /dev/null @@ -1,84 +0,0 @@ -grammar lib:system:ioaction ; - -nonterminal IOaction with ioIn, ioOut ; - -inherited attribute ioIn :: IO ; -synthesized attribute ioOut :: IO ; - - --- individual actions ------------------------------------------------------------- - -abstract production printString -t::IOaction ::= str::String -{ t.ioOut = print (str, t.ioIn); } - -abstract production printInteger -t::IOaction ::= i::Integer -{ t.ioOut = print (toString(i), t.ioIn); } - -abstract production writeString -t::IOaction ::= filename::String str::String -{ t.ioOut = writeFile(filename, str, t.ioIn); } - -abstract production skipIOaction -task::IOaction ::= -{ - task.ioOut = task.ioIn ; -} - --- composite actions ------------------------------------------------------------- -abstract production ioActionSequence -t::IOaction ::= t1::IOaction t2::IOaction -{ - t.ioOut = t2.ioOut ; - t1.ioIn = t.ioIn ; - t2.ioIn = t1.ioOut ; -} - -abstract production optionalAction -t::IOaction ::= cond::Boolean task::IOaction -{ - t.ioOut = if cond - then task.ioOut - else t.ioIn ; - - task.ioIn = t.ioIn ; -} - -abstract production condAction -t::IOaction ::= cond::Boolean t1::IOaction t2::IOaction -{ - t.ioOut = if cond then t1.ioOut else t2.ioOut ; - t1.ioIn = t.ioIn ; - t2.ioIn = t.ioIn ; -} - - --- functions over actions ------------------------------------------------------------- - --- fold many actions into one, in sequence -function foldIOactions -IOaction ::= tasks::[IOaction] -{ - return if null(tasks) - then skipIOaction() - else ioActionSequence(head(tasks), - foldIOactions(tail(tasks))) ; -} - --- perform an IOaction -function performAction -IO ::= task::IOaction token::IO -{ - local attribute instantiatedTask :: IOaction ; - instantiatedTask = task ; - - instantiatedTask.ioIn = token ; - - return instantiatedTask.ioOut ; -} - - diff --git a/grammars/lib/xml/DocConfig.sv b/grammars/lib/xml/DocConfig.sv deleted file mode 100644 index 05c6bed64..000000000 --- a/grammars/lib/xml/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:xml; - -{@config - header:"---\nlayout: sv_wiki\ntitle: XML\nmenu_title: XML\nmenu_weight: 100\n---" -@} - diff --git a/grammars/lib/xml/XMLTools.sv b/grammars/lib/xml/XMLTools.sv deleted file mode 100644 index 5a1eff6dc..000000000 --- a/grammars/lib/xml/XMLTools.sv +++ /dev/null @@ -1,18 +0,0 @@ -grammar lib:xml; - -exports lib:xml:ast; -- don't force users to import anything but lib:xml. - -import lib:xml:ast; - -{-- - - WARNING: this is buggy! we're parsing a file without demanding an IO token!! - - WILL CHANGE IN THE FUTURE. - -} -function parseXMLFileN -ParseResult ::= filename::String -{ - return error("parseXMLFileN not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.parseXMLFileN(%filename%)"; -} - diff --git a/grammars/lib/xml/ast/DocConfig.sv b/grammars/lib/xml/ast/DocConfig.sv deleted file mode 100644 index f56b6507c..000000000 --- a/grammars/lib/xml/ast/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:xml:ast; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Abstract Syntax Tree\nmenu_title: AST\nmenu_weight: 100\n---" -@} - diff --git a/grammars/lib/xml/foreigntypes/DocConfig.sv b/grammars/lib/xml/foreigntypes/DocConfig.sv deleted file mode 100644 index 6da2913d3..000000000 --- a/grammars/lib/xml/foreigntypes/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar lib:xml:foreigntypes; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Foreign Types\nmenu_title: Foreign Types\nmenu_weight: 100\n---" -@} - diff --git a/grammars/lib/xml/foreigntypes/Types.sv b/grammars/lib/xml/foreigntypes/Types.sv deleted file mode 100644 index a19be467c..000000000 --- a/grammars/lib/xml/foreigntypes/Types.sv +++ /dev/null @@ -1,147 +0,0 @@ -grammar lib:xml:foreigntypes; - -import lib:xml:ast; - -type XML_Document foreign; -type XML_NodeList foreign; -type XML_Node foreign; - - -{-- - - WARNING: this is buggy! we're parsing a file without demanding an IO token!! - - WILL CHANGE IN THE FUTURE. - -} -function parseXMLFileF -ParseResult ::= filename::String -{ - return error("parseXMLFileF not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.parseXMLFileF(%filename%)"; -} - --- NATIVE AST response. This is just a helper for -function nodeListXPathQueryN -XMLNodeList ::= query::String doc::XML_Document -{ - return xmlNodeListF2N(nodeListXPathQueryF(query, doc)); -} - --- FOREIGN DOM response -function nodeListXPathQueryF -XML_NodeList ::= query::String doc::XML_Document -{ - return error("nodeListXPathQueryN not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), null)"; -} - -function stringXPathQuery -String ::= query::String doc::XML_Document -{ - return error("stringXPathQuery not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), null)"; -} - --- REQUERYING a previous query result... -function nodeListXPathReQueryF -XML_NodeList ::= query::String doc::XML_Node -{ - return error("nodeListXPathReQueryF not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), null)"; -} -function stringXPathReQuery -String ::= query::String doc::XML_Node -{ - return error("stringXPathReQuery not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), null)"; -} - ------ namespace variants ------------------------------------------------------ - --- FOREIGN DOM response -function nodeListXPathQueryFns -XML_NodeList ::= query::String ns::[Pair] doc::XML_Document -{ - return error("nodeListXPathQueryN not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), %ns%)"; -} - -function stringXPathQueryns -String ::= query::String ns::[Pair] doc::XML_Document -{ - return error("stringXPathQuery not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), %ns%)"; -} - --- REQUERYING a previous query result... -function nodeListXPathReQueryFns -XML_NodeList ::= query::String ns::[Pair] doc::XML_Node -{ - return error("nodeListXPathReQueryF not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), %ns%)"; -} -function stringXPathReQueryns -String ::= query::String ns::[Pair] doc::XML_Node -{ - return error("stringXPathReQuery not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), %ns%)"; -} - -------------------------------------------------------------------------------- - - -function xmlNodeListF2N -XMLNodeList ::= nl::XML_NodeList -{ - return error("xmlNodeListF2N not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.nodeListF2N((org.w3c.dom.NodeList)%nl%)"; -} - -function nodeListF2NPartial -[XML_Node] ::= nl::XML_NodeList -{ - return error("nodeListF2NPartial not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.nodeListF2NPartial((org.w3c.dom.NodeList)%nl%)"; -} - --- TODO: xmlNodeListN2F? Can accomplish for now by wrapping in a document, perhaps. --- TODO: Node conversions? - -function xmlDocumentF2N -XMLDocument ::= doc::XML_Document -{ - return error("xmlDocumentF2N not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.documentF2N((org.w3c.dom.Document)%doc%)"; -} - -function xmlDocumentN2F -XML_Document ::= doc::XMLDocument -{ - return error("xmlDocumentN2F not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.documentN2F((lib.xml.ast.NXMLDocument)%doc%)"; -} - -function xmlDocumentF2String -String ::= doc::XML_Document -{ - return error("xmlDocumentF2String not yet implemented"); -} foreign { - "java" : return "common.rawlib.RawXML.documentF2String((org.w3c.dom.Document)%doc%)"; -} - -function xmlDocumentN2String -String ::= doc::XMLDocument -{ - return xmlDocumentF2String(xmlDocumentN2F(doc)); -} diff --git a/grammars/silver/analysis/DocConfig.sv b/grammars/silver/analysis/DocConfig.sv deleted file mode 100644 index f35122365..000000000 --- a/grammars/silver/analysis/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:analysis; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Analysis\nmenu_title: Analysis\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/analysis/typechecking/core/AspectDcl.sv b/grammars/silver/analysis/typechecking/core/AspectDcl.sv deleted file mode 100644 index 7f164c4c7..000000000 --- a/grammars/silver/analysis/typechecking/core/AspectDcl.sv +++ /dev/null @@ -1,133 +0,0 @@ -grammar silver:analysis:typechecking:core; - -attribute upSubst, downSubst, finalSubst occurs on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectRHSElem, AspectFunctionSignature, AspectFunctionLHS; - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = ns.finalSubst; - - errCheck1 = check(realSig.typerep, namedSig.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Aspect for '" ++ id.name ++ "' does not have the right signature.\nExpected: " - ++ errCheck1.leftpp ++ "\nActual: " ++ errCheck1.rightpp)] - else - -- dcl is potentially not found, accessing it can crash. - -- so check on dcls for this. - case id.lookupValue.dcls of - | prodDcl (_, _, _, _) :: _ -> [] - | funDcl (_, _, _) :: _ -> [err(top.location, "Production aspect for '" ++ id.name ++ "' should be a 'function' aspect instead.")] - | _ -> [err(id.location, id.name ++ " is not a production.")] - end; - - ns.downSubst = emptySubst(); - errCheck1.downSubst = ns.upSubst; - body.downSubst = errCheck1.upSubst; - - ns.finalSubst = errCheck1.upSubst; -} - - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = ns.finalSubst; - - errCheck1 = check(realSig.typerep, namedSig.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Aspect for '" ++ id.name ++ "' does not have the right signature.\nExpected: " - ++ errCheck1.leftpp ++ "\nActual: " ++ errCheck1.rightpp)] - else - -- must be on dcls because lookup may have failed. - case id.lookupValue.dcls of - | funDcl (_, _, _) :: _ -> [] - | prodDcl (_, _, _, _) :: _ -> [err(top.location, "Function aspect for '" ++ id.name ++ "' should be a 'production' aspect instead.")] - | _ -> [err(id.location, id.name ++ " is not a function.")] - end; - - ns.downSubst = emptySubst(); - errCheck1.downSubst = ns.upSubst; - body.downSubst = errCheck1.upSubst; - - ns.finalSubst = errCheck1.upSubst; -} - --- - -aspect production aspectProductionSignature -top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS -{ - lhs.downSubst = top.downSubst; - rhs.downSubst = lhs.upSubst; - top.upSubst = rhs.upSubst; -} - -aspect production aspectProductionLHSFull -top::AspectProductionLHS ::= id::Name t::Type -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - errCheck1.downSubst = top.downSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(rType, t); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] - else []; -} - -aspect production aspectRHSElemNil -top::AspectRHS ::= -{ - top.upSubst = top.downSubst; -} - -aspect production aspectRHSElemCons -top::AspectRHS ::= h::AspectRHSElem t::AspectRHS -{ - h.downSubst = top.downSubst; - t.downSubst = h.upSubst; - top.upSubst = t.upSubst; -} - -aspect production aspectRHSElemFull -top::AspectRHSElem ::= id::Name t::Type -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - errCheck1.downSubst = top.downSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(rType, t); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] - else []; -} - -aspect production aspectFunctionSignature -top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS -{ - lhs.downSubst = top.downSubst; - rhs.downSubst = lhs.upSubst; - top.upSubst = rhs.upSubst; -} - -aspect production functionLHSType -top::AspectFunctionLHS ::= t::TypeExpr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - errCheck1.downSubst = top.downSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(rType, t.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] - else []; -} - diff --git a/grammars/silver/analysis/typechecking/core/BuiltInFunctions.sv b/grammars/silver/analysis/typechecking/core/BuiltInFunctions.sv deleted file mode 100644 index 7066e2943..000000000 --- a/grammars/silver/analysis/typechecking/core/BuiltInFunctions.sv +++ /dev/null @@ -1,142 +0,0 @@ -grammar silver:analysis:typechecking:core; - -aspect production lengthFunction -top::Expr ::= 'length' '(' e::Expr ')' -{ - e.downSubst = top.downSubst; - forward.downSubst= e.upSubst; -} -aspect production stringLength -top::Expr ::= e::Decorated Expr -{ - top.upSubst = top.downSubst; -} -aspect production errorLength -top::Expr ::= e::Decorated Expr -{ - top.upSubst = top.downSubst; -} - -aspect production toBooleanFunction -top::Expr ::= 'toBoolean' '(' e1::Expr ')' -{ - e1.downSubst = top.downSubst; - top.upSubst = e1.upSubst; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceConvertible - then [] - else [err(top.location, "Operand to toBoolean must be concrete types String, Integer, Float, or Boolean. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production toIntegerFunction -top::Expr ::= 'toInteger' '(' e1::Expr ')' -{ - e1.downSubst = top.downSubst; - top.upSubst = e1.upSubst; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceConvertible - then [] - else [err(top.location, "Operand to toInteger must be concrete types String, Integer, Float, or Boolean. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production toFloatFunction -top::Expr ::= 'toFloat' '(' e1::Expr ')' -{ - e1.downSubst = top.downSubst; - top.upSubst = e1.upSubst; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceConvertible - then [] - else [err(top.location, "Operand to toFloat must be concrete types String, Integer, Float, or Boolean. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production toStringFunction -top::Expr ::= 'toString' '(' e1::Expr ')' -{ - e1.downSubst = top.downSubst; - top.upSubst = e1.upSubst; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceConvertible - then [] - else [err(top.location, "Operand to toString must be concrete types String, Integer, Float, or Boolean. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -function containsSkolem -Boolean ::= ty::Type -{ - return case ty of - | skolemType(_) -> true - | nonterminalType(_, args) -> any(map(containsSkolem, args)) - | decoratedType(ty) -> containsSkolem(ty) - | functionType(out, params, namedParams) -> containsSkolem(out) || any(map(containsSkolem, params)) || any(map((\x::NamedArgType -> containsSkolem(x.argType)), namedParams)) - | _ -> false - end; -} - -aspect production reifyFunctionLiteral -top::Expr ::= 'reify' -{ - top.upSubst = top.downSubst; - - top.errors <- - case performSubstitution(top.typerep, top.finalSubst) of - | functionType(nonterminalType("core:Either", [stringType(), resultType]), [nonterminalType("core:reflect:AST", [])], []) -> - case resultType of - | skolemType(_) -> [err(top.location, "reify invocation attempts to reify to a skolem type - this will never succeed, see https://github.com/melt-umn/silver/issues/368")] - | ty when containsSkolem(ty) -> [wrn(top.location, "reify invocation attempts to reify to a type containing a skolem - this will only succeed in the case that the value does not actually contain an instance of the skolem type, see https://github.com/melt-umn/silver/issues/368")] - | _ -> [] - end - | _ -> error("insane final type for reify implementation") - end; -} - -aspect production newFunction -top::Expr ::= 'new' '(' e1::Expr ')' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - errCheck1.downSubst = e1.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = checkDecorated(e1.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operand to new must be a decorated nonterminal. Instead it is of type " ++ errCheck1.leftpp)] - else []; -} - -aspect production terminalConstructor -top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - es.downSubst = top.downSubst; - el.downSubst = es.upSubst; - errCheck1.downSubst = el.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - top.upSubst = errCheck2.upSubst; - - errCheck1 = check(es.typerep, stringType()); - errCheck2 = check(el.typerep, nonterminalType("core:Location", [])); - top.errors <- - if errCheck1.typeerror - then [err(es.location, "Second operand to 'terminal(type,lexeme,location)' must be a String, instead it is " ++ errCheck1.leftpp)] - else []; - - top.errors <- - if errCheck2.typeerror - then [err(el.location, "Third operand to 'terminal(type,lexeme,location)' must be a Location, instead it is " ++ errCheck2.leftpp)] - else []; - - top.errors <- - if t.typerep.isTerminal || t.typerep.isError - then [] - else [err(t.location, "First operand to 'terminal(type,lexeme,location)' must be a Terminal type, instead it is " ++ prettyType(t.typerep))]; -} - diff --git a/grammars/silver/analysis/typechecking/core/Checking.sv b/grammars/silver/analysis/typechecking/core/Checking.sv deleted file mode 100644 index 90feae861..000000000 --- a/grammars/silver/analysis/typechecking/core/Checking.sv +++ /dev/null @@ -1,55 +0,0 @@ -grammar silver:analysis:typechecking:core; - -import silver:definition:type; - -synthesized attribute leftpp :: String; -synthesized attribute rightpp :: String; -synthesized attribute typeerror :: Boolean; - -nonterminal TypeCheck with upSubst, downSubst, finalSubst, leftpp, rightpp, typeerror; - --- TODO: we could probably change this to 'expected' and 'actual' and generate the error message here? -abstract production check -top::TypeCheck ::= l::Type r::Type -{ - top.upSubst = unifyCheck(l, r, top.downSubst); - - top.typeerror = top.upSubst.failure; - - local finleft :: Type = performSubstitution(l, top.finalSubst); - - local finright :: Type = performSubstitution(r, top.finalSubst); - - local fv :: [TyVar] = setUnionTyVars(finleft.freeVariables, finright.freeVariables); - - top.leftpp = prettyTypeWith(finleft, fv); - top.rightpp = prettyTypeWith(finright, fv); -} - -abstract production checkNonterminal -top::TypeCheck ::= l::Type -{ - local refined :: Type = - performSubstitution(l, top.downSubst); - - top.upSubst = composeSubst(ignoreFailure(top.downSubst), refined.unifyInstanceNonterminal); - - top.typeerror = top.upSubst.failure && !refined.isError; - - top.leftpp = prettyType(performSubstitution(l, top.finalSubst)); - top.rightpp = "a nonterminal"; -} -abstract production checkDecorated -top::TypeCheck ::= l::Type -{ - local refined :: Type = - performSubstitution(l, top.downSubst); - - top.upSubst = composeSubst(ignoreFailure(top.downSubst), refined.unifyInstanceDecorated); - - top.typeerror = top.upSubst.failure && !refined.isError; - - top.leftpp = prettyType(performSubstitution(l, top.finalSubst)); - top.rightpp = "a decorated nonterminal"; -} - diff --git a/grammars/silver/analysis/typechecking/core/Expr.sv b/grammars/silver/analysis/typechecking/core/Expr.sv deleted file mode 100644 index 3b5db95c0..000000000 --- a/grammars/silver/analysis/typechecking/core/Expr.sv +++ /dev/null @@ -1,743 +0,0 @@ -grammar silver:analysis:typechecking:core; - -attribute upSubst, downSubst, finalSubst occurs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoExpr, AnnoAppExprs; - -aspect production errorExpr -top::Expr ::= e::[Message] -{ - top.upSubst = top.downSubst; -} - -aspect production errorReference -top::Expr ::= msg::[Message] q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production childReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production lhsReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production localReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production productionReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production functionReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production forwardReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production globalValueReference -top::Expr ::= q::Decorated QName -{ - top.upSubst = top.downSubst; -} - -aspect production application -top::Expr ::= e::Expr '(' es::AppExprs ',' anns::AnnoAppExprs ')' -{ - e.downSubst = top.downSubst; - forward.downSubst = e.upSubst; -} - -aspect production functionApplication -top::Expr ::= e::Decorated Expr es::AppExprs anns::AnnoAppExprs -{ - es.downSubst = top.downSubst; - anns.downSubst = es.upSubst; - forward.downSubst = anns.upSubst; -} - -aspect production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.upSubst = top.downSubst; -} - -aspect production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.upSubst = top.downSubst; -} - -aspect production errorApplication -top::Expr ::= e::Decorated Expr es::AppExprs anns::AnnoAppExprs -{ - es.downSubst = top.downSubst; - anns.downSubst = es.upSubst; - top.upSubst = anns.upSubst; -} - -aspect production attributeSection -top::Expr ::= '(' '.' q::QName ')' -{ - top.upSubst = top.downSubst; -} - -aspect production access -top::Expr ::= e::Expr '.' q::QNameAttrOccur -{ - e.downSubst = top.downSubst; - forward.downSubst = e.upSubst; -} - -aspect production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} - -aspect production undecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - -- We might have gotten here via a 'ntOrDec' type. So let's make certain we're UNdecorated, - -- ensuring that type's specialization, otherwise we could end up in trouble! - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - errCheck1 = checkNonterminal(e.typerep); - - -- TECHNICALLY, I think the current implementation makes this impossible, - -- But let's leave it since it's the right thing to do. - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Access of " ++ q.name ++ " from a decorated type.")] - else []; - - errCheck1.downSubst = top.downSubst; - top.upSubst = errCheck1.upSubst; -} - -aspect production accessBouncer -top::Expr ::= target::(Expr ::= Decorated Expr Decorated QNameAttrOccur Location) e::Expr q::Decorated QNameAttrOccur -{ - e.downSubst = top.downSubst; - forward.downSubst = e.upSubst; -} - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - errCheck1 = checkDecorated(e.typerep); - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute forward being accessed from an undecorated type.")] - else []; -} - -aspect production decoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - -- We might have gotten here via a 'ntOrDec' type. So let's make certain we're decorated, - -- ensuring that type's specialization, otherwise we could end up in trouble! - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - errCheck1 = checkDecorated(e.typerep); - - -- TECHNICALLY, I think the current implementation makes this impossible, - -- But let's leave it since it's the right thing to do. - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ q.name ++ " being accessed from an undecorated type.")] - else []; - - errCheck1.downSubst = top.downSubst; - top.upSubst = errCheck1.upSubst; -} - -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} -aspect production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} - - -aspect production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} -aspect production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.upSubst = top.downSubst; -} - - - -aspect production trueConst -top::Expr ::= 'true' -{ - top.upSubst = top.downSubst; -} - -aspect production falseConst -top::Expr ::= 'false' -{ - top.upSubst = top.downSubst; -} - -aspect production and -top::Expr ::= e1::Expr '&&' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - top.upSubst = errCheck2.upSubst; - - errCheck1 = check(e1.typerep, boolType()); - errCheck2 = check(e2.typerep, boolType()); - top.errors <- - if errCheck1.typeerror - then [err(e1.location, "First operand to && must be type bool. Got instead type " ++ errCheck1.leftpp)] - else []; - top.errors <- - if errCheck2.typeerror - then [err(e2.location, "First operand to && must be type bool. Got instead type " ++ errCheck2.leftpp)] - else []; -} - -aspect production or -top::Expr ::= e1::Expr '||' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - top.upSubst = errCheck2.upSubst; - - errCheck1 = check(e1.typerep, boolType()); - errCheck2 = check(e2.typerep, boolType()); - top.errors <- - if errCheck1.typeerror - then [err(e1.location, "First operand to || must be type bool. Got instead type " ++ errCheck1.leftpp)] - else []; - top.errors <- - if errCheck2.typeerror - then [err(e2.location, "First operand to || must be type bool. Got instead type " ++ errCheck2.leftpp)] - else []; -} - -aspect production not -top::Expr ::= '!' e1::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - errCheck1.downSubst = e1.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, boolType()); - top.errors <- - if errCheck1.typeerror - then [err(e1.location, "Operand to ! must be type bool. Got instead type " ++ errCheck1.leftpp)] - else []; -} - -aspect production gt -top::Expr ::= e1::Expr '>' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to > must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceOrd - then [] - else [err(top.location, "Operands to > must be concrete types Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production lt -top::Expr ::= e1::Expr '<' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to < must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceOrd - then [] - else [err(top.location, "Operands to < must be concrete types Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - - -aspect production gteq -top::Expr ::= e1::Expr '>=' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to >= must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceOrd - then [] - else [err(top.location, "Operands to >= must be concrete types Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - - -aspect production lteq -top::Expr ::= e1::Expr '<=' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to <= must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceOrd - then [] - else [err(top.location, "Operands to <= must be concrete types Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - - -aspect production eqeq -top::Expr ::= e1::Expr '==' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to == must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceEq - then [] - else [err(top.location, "Operands to == must be concrete types Boolean, Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - - -aspect production neq -top::Expr ::= e1::Expr '!=' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to != must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceEq - then [] - else [err(top.location, "Operands to != must be concrete types Boolean, Integer, Float, String or TerminalId. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - - -aspect production ifThenElse -top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - e3.downSubst = e2.upSubst; - errCheck1.downSubst = e3.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - top.upSubst = errCheck2.upSubst; - - errCheck1 = check(e2.typerep, e3.typerep); - errCheck2 = check(e1.typerep, boolType()); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Then and else branch must have the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - top.errors <- - if errCheck2.typeerror - then [err(e1.location, "Condition must have the type Boolean. Instead it is " ++ errCheck2.leftpp)] - else []; -} - -aspect production intConst -top::Expr ::= i::Int_t -{ - top.upSubst = top.downSubst; -} - -aspect production floatConst -top::Expr ::= f::Float_t -{ - top.upSubst = top.downSubst; -} - -aspect production plus -top::Expr ::= e1::Expr '+' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to + must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operands to + must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production minus -top::Expr ::= e1::Expr '-' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to - must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operands to - must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} -aspect production multiply -top::Expr ::= e1::Expr '*' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to * must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operands to * must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} -aspect production divide -top::Expr ::= e1::Expr '/' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to / must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operands to / must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} -aspect production modulus -top::Expr ::= e1::Expr '%' e2::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e1.typerep, e2.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operands to % must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] - else []; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operands to % must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} -aspect production neg -top::Expr ::= '-' e1::Expr -{ - e1.downSubst = top.downSubst; - top.upSubst = e1.upSubst; - - top.errors <- - if performSubstitution(e1.typerep, top.finalSubst).instanceNum - then [] - else [err(top.location, "Operand to unary - must be concrete types Integer or Float. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; -} - -aspect production stringConst -top::Expr ::= s::String_t -{ - top.upSubst = top.downSubst; -} - --- Already merged into silver:definition:core ---aspect production plusPlus - -aspect production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.upSubst = top.downSubst; -} - -aspect production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.upSubst = top.downSubst; -} - - -aspect production exprsEmpty -top::Exprs ::= -{ - top.upSubst = top.downSubst; -} - -aspect production exprsSingle -top::Exprs ::= e::Expr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} - -aspect production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - top.upSubst = e2.upSubst; -} - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - inh.downSubst = errCheck1.upSubst; - top.upSubst = inh.upSubst; - - errCheck1 = checkNonterminal(e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Operand to decorate must be a nonterminal. Instead it is of type " ++ errCheck1.leftpp)] - else []; -} - -aspect production exprInh -top::ExprInh ::= lhs::ExprLHSExpr '=' e1::Expr ';' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - errCheck1.downSubst = e1.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(lhs.typerep, e1.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, lhs.name ++ " has expected type " ++ errCheck1.leftpp - ++ ", but the expression has type " ++ errCheck1.rightpp)] - else []; -} - -aspect production exprInhsEmpty -top::ExprInhs ::= -{ - top.upSubst = top.downSubst; -} - -aspect production exprInhsOne -top::ExprInhs ::= lhs::ExprInh -{ - lhs.downSubst = top.downSubst; - top.upSubst = lhs.upSubst; -} - -aspect production exprInhsCons -top::ExprInhs ::= lhs::ExprInh inh::ExprInhs -{ - lhs.downSubst = top.downSubst; - inh.downSubst = lhs.upSubst; - top.upSubst = inh.upSubst; -} - -aspect production missingAppExpr -top::AppExpr ::= '_' -{ - top.upSubst = top.downSubst; -} -aspect production presentAppExpr -top::AppExpr ::= e::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e.typerep, top.appExprTyperep); - top.errors <- - if !errCheck1.typeerror then [] - else [err(top.location, "Argument " ++ toString(top.appExprIndex+1) ++ " of function '" ++ - top.appExprApplied ++ "' expected " ++ errCheck1.rightpp ++ - " but argument is of type " ++ errCheck1.leftpp)]; -} -aspect production snocAppExprs -top::AppExprs ::= es::AppExprs ',' e::AppExpr -{ - es.downSubst = top.downSubst; - e.downSubst = es.upSubst; - top.upSubst = e.upSubst; -} -aspect production oneAppExprs -top::AppExprs ::= e::AppExpr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} -aspect production emptyAppExprs -top::AppExprs ::= -{ - top.upSubst = top.downSubst; -} - -aspect production annoExpr -top::AnnoExpr ::= qn::QName '=' e::AppExpr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} -aspect production snocAnnoAppExprs -top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr -{ - es.downSubst = top.downSubst; - e.downSubst = es.upSubst; - top.upSubst = e.upSubst; -} -aspect production oneAnnoAppExprs -top::AnnoAppExprs ::= e::AnnoExpr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} -aspect production emptyAnnoAppExprs -top::AnnoAppExprs ::= -{ - top.upSubst = top.downSubst; -} - - -aspect production exprRef -top::Expr ::= e::Decorated Expr -{ - -- See documentation for major restriction on use of exprRef. - -- Essentially, the referred expression MUST have already been type checked. - top.upSubst = top.downSubst; -} - diff --git a/grammars/silver/analysis/typechecking/core/FunctionDcl.sv b/grammars/silver/analysis/typechecking/core/FunctionDcl.sv deleted file mode 100644 index 063a512fe..000000000 --- a/grammars/silver/analysis/typechecking/core/FunctionDcl.sv +++ /dev/null @@ -1,8 +0,0 @@ -grammar silver:analysis:typechecking:core; - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - body.downSubst = emptySubst(); -} - diff --git a/grammars/silver/analysis/typechecking/core/GlobalDcl.sv b/grammars/silver/analysis/typechecking/core/GlobalDcl.sv deleted file mode 100644 index 6f9565d20..000000000 --- a/grammars/silver/analysis/typechecking/core/GlobalDcl.sv +++ /dev/null @@ -1,19 +0,0 @@ -grammar silver:analysis:typechecking:core; - -aspect production globalValueDclConcrete -top::AGDcl ::= 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - local attribute errCheck1 :: TypeCheck; - - e.downSubst = emptySubst(); - errCheck1.downSubst = e.upSubst; - - errCheck1.finalSubst = errCheck1.upSubst; - e.finalSubst = errCheck1.upSubst; - - errCheck1 = check(e.typerep, t.typerep); - top.errors <- if errCheck1.typeerror - then [err(top.location, "Declaration of global " ++ id.name ++ " with type " ++ errCheck1.rightpp ++ " has initialization expression with type " ++ errCheck1.leftpp)] - else []; -} - diff --git a/grammars/silver/analysis/typechecking/core/ProductionBody.sv b/grammars/silver/analysis/typechecking/core/ProductionBody.sv deleted file mode 100644 index 4fc13d824..000000000 --- a/grammars/silver/analysis/typechecking/core/ProductionBody.sv +++ /dev/null @@ -1,212 +0,0 @@ -grammar silver:analysis:typechecking:core; - -import silver:util; - -attribute upSubst, downSubst, finalSubst occurs on ProductionStmt, ForwardInhs, ForwardInh, ForwardLHSExpr; - -{-- - - These need an initial state only due to aspects (I think? maybe not. Investigate someday.) - - They otherwise confine their contexts to each individual Stmt. - -} -attribute downSubst occurs on ProductionBody, ProductionStmts; - - -aspect production productionBody -top::ProductionBody ::= '{' stmts::ProductionStmts '}' -{ - stmts.downSubst = top.downSubst; -} - -aspect production productionStmtsNil -top::ProductionStmts ::= -{ -} - -aspect production productionStmtsSnoc -top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt -{ - h.downSubst = top.downSubst; - - t.downSubst = top.downSubst; - t.finalSubst = t.upSubst; -} - -aspect production productionStmtAppend -top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt -{ - -- We treat this as though each is independent here as well. - h.downSubst = top.downSubst; - h.finalSubst = h.upSubst; - - t.downSubst = top.downSubst; - t.finalSubst = t.upSubst; - - top.upSubst = error("Shouldn't ever be needed anywhere. (Should only ever be fed back here as top.finalSubst)"); - -- Of course, this means do not use top.finalSubst here! -} - -aspect production errorProductionStmt -top::ProductionStmt ::= e::[Message] -{ - top.upSubst = top.downSubst; -} - -aspect production forwardsTo -top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e.typerep, top.frame.signature.outputElement.typerep); - top.errors <- if errCheck1.typeerror - then [err(e.location, "Forward's expected type is " ++ errCheck1.rightpp ++ ", but the actual type supplied is " ++ errCheck1.leftpp)] - else []; -} - -aspect production forwardingWith -top::ProductionStmt ::= 'forwarding' 'with' '{' inh::ForwardInhs '}' ';' -{ - inh.downSubst = top.downSubst; - top.upSubst = inh.upSubst; -} - -aspect production forwardInhsOne -top::ForwardInhs ::= lhs::ForwardInh -{ - lhs.downSubst = top.downSubst; - top.upSubst = lhs.upSubst; -} - -aspect production forwardInhsCons -top::ForwardInhs ::= lhs::ForwardInh rhs::ForwardInhs -{ - lhs.downSubst = top.downSubst; - rhs.downSubst = lhs.upSubst; - top.upSubst = rhs.upSubst; -} - -aspect production forwardInh -top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - lhs.downSubst = top.downSubst; - e.downSubst = lhs.upSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(lhs.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(e.location, lhs.name ++ " has expected type " ++ errCheck1.leftpp - ++ ", but the expression has type " ++ errCheck1.rightpp)] - else []; -} - -aspect production forwardLhsExpr -top::ForwardLHSExpr ::= q::QNameAttrOccur -{ - top.upSubst = top.downSubst; -} - -aspect production localAttributeDcl -top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' -{ - top.upSubst = top.downSubst; -} - -aspect production returnDef -top::ProductionStmt ::= 'return' e::Expr ';' -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e.typerep, top.frame.signature.outputElement.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Expected return type is " ++ errCheck1.rightpp ++ ", but the expression has actual type " ++ errCheck1.leftpp)] - else []; -} - -aspect production errorAttributeDef -top::ProductionStmt ::= msg::[Message] dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} - -aspect production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} - -aspect production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} - -aspect production childDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.errors <- if top.typerep.isDecorable then [] - else [err(top.location, "Inherited attributes can only be defined on (undecorated) nonterminals.")]; -} - -aspect production localDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.errors <- if top.typerep.isDecorable then [] - else [err(top.location, "Inherited attributes can only be defined on (undecorated) nonterminals.")]; -} - -aspect production localValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(e.typerep, val.lookupValue.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Local " ++ val.name ++ " has type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] - else []; -} - -aspect production errorValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; -} - diff --git a/grammars/silver/analysis/typechecking/core/ProductionDcl.sv b/grammars/silver/analysis/typechecking/core/ProductionDcl.sv deleted file mode 100644 index 2daf21af2..000000000 --- a/grammars/silver/analysis/typechecking/core/ProductionDcl.sv +++ /dev/null @@ -1,8 +0,0 @@ -grammar silver:analysis:typechecking:core; - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - body.downSubst = emptySubst(); -} - diff --git a/grammars/silver/analysis/typechecking/core/Project.sv b/grammars/silver/analysis/typechecking/core/Project.sv deleted file mode 100644 index 92f4fee92..000000000 --- a/grammars/silver/analysis/typechecking/core/Project.sv +++ /dev/null @@ -1,39 +0,0 @@ -grammar silver:analysis:typechecking:core; - -imports silver:definition:core; -imports silver:definition:type:syntax; - -imports silver:definition:env; -imports silver:definition:type; - - - -{-- The resulting substitution context -} -synthesized attribute upSubst :: Substitution; - -{-- The initial substitution context -} -inherited attribute downSubst :: Substitution; - -{-- The complete, final substitution context -} -autocopy attribute finalSubst :: Substitution; - --- We also use typerep. --- Such that performSubstitution(e.typerep, e.upSubst) is the expression's real type (as of that moment) - -{- A NOTE ABOUT THREADING: - The pair of attributes (upSubst/downSubst) represent the state of unification. - downSubst tells a node its current state, and upSubst represents the final state. - finalSubst gives information from elsewhere in the tree. - - The scope of that threading is not universal. - That is, the threading BEGINS generally just above expressions, or individual - production STATEMENTS. Not Production bodies. - - There is a slight hack to this... because aspects need to do type unification - (actually, not sure if we need to preserve this, but let's assume we do!) - ProductionBody and ProductionStmts (plural, *NOT* ProductionStmt) - do not have an upSubst and finalSubst. Only downSubst. - These will take that initial state, but otherwise confine things to each Stmt. - -} - - diff --git a/grammars/silver/analysis/warnings/Project.sv b/grammars/silver/analysis/warnings/Project.sv deleted file mode 100644 index eed6f8c7e..000000000 --- a/grammars/silver/analysis/warnings/Project.sv +++ /dev/null @@ -1,26 +0,0 @@ -grammar silver:analysis:warnings; - -imports silver:util:cmdargs; -imports silver:driver only parseArgs; - -synthesized attribute warnAll :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.warnAll = false; -} -abstract production warnAllFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnAll = true; - forwards to rest; -} - -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--warn-all", flag(warnAllFlag))]; - flagdescs <- ["\t--warn-all : enable all warnings"]; -} - diff --git a/grammars/silver/analysis/warnings/exporting/Graph.sv b/grammars/silver/analysis/warnings/exporting/Graph.sv deleted file mode 100644 index 3ad52e10e..000000000 --- a/grammars/silver/analysis/warnings/exporting/Graph.sv +++ /dev/null @@ -1,65 +0,0 @@ -grammar silver:analysis:warnings:exporting; - -import silver:driver; -import silver:util:cmdargs; - -import silver:definition:core; -import silver:definition:env; - --- This isn't exactly a warning, but it can live here for now... - -synthesized attribute dumpDepGraph :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= _ -{ - top.dumpDepGraph = false; -} -abstract production dumpDepGraphFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.dumpDepGraph = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--dump-import-graph", flag(dumpDepGraphFlag))]; - -- omitting from descriptions deliberately! -} -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - top.postOps <- if top.config.dumpDepGraph then [dumpDepGraphAction(g.grammarList)] else []; -} - -abstract production dumpDepGraphAction -top::DriverAction ::= specs::[Decorated RootSpec] -{ - top.io = writeFile("deps.dot", "digraph deps {\n" ++ generateDotGraph(specs) ++ "}", - print("Generating import graph\n", top.ioIn)); - - top.code = 0; - top.order = 0; -} - -function generateDotGraph -String ::= specs::[Decorated RootSpec] -{ - return case specs of - | [] -> "" - | h::t -> - "\"" ++ h.declaredName ++ "\"[label=\"" ++ h.declaredName ++ "\"];\n" ++ - implode("", map(makeDotArrow(h.declaredName, _), h.moduleNames)) ++ - generateDotGraph(t) - end; -} - -function makeDotArrow -String ::= f::String t::String -{ - -- A heuristic to try to make the graph more readable... - return if t == "core" then "" - else "\"" ++ f ++ "\" -> \"" ++ t ++ "\";\n"; -} - diff --git a/grammars/silver/analysis/warnings/flow/FlowTypeCopyEquation.sv b/grammars/silver/analysis/warnings/flow/FlowTypeCopyEquation.sv deleted file mode 100644 index 8efa3463c..000000000 --- a/grammars/silver/analysis/warnings/flow/FlowTypeCopyEquation.sv +++ /dev/null @@ -1,53 +0,0 @@ -grammar silver:analysis:warnings:flow; - --- Flow type check: the implicitly generated copy equations for synthesized --- attributes due to forwarding may exceed their flow type. - --- This can only occur with *host-language attributes* as extension --- attribute are required to have ft(syn) > ft(fwd). - --- The flow environment can give us the authoritative list of those attributes to check. --- These may be from `options` and so requires the flowEnv. - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local transitiveDeps :: [FlowVertex] = expandGraph([forwardEqVertex()], findProductionGraph(fName, myGraphs)); - local fwdFlowDeps :: set:Set = onlyLhsInh(transitiveDeps); - - local lhsNt :: String = namedSig.outputElement.typerep.typeName; - local hostSyns :: [String] = getHostSynsFor(lhsNt, top.flowEnv); - - -- Possible refactoring: Consider moving this check from this production to forwarding equation? - - top.errors <- - if null(body.errors ++ ns.errors) - && (top.config.warnAll || top.config.warnMissingInh) - -- Must be a forwarding production - && !null(body.uniqueSignificantExpression) - then flatMap(raiseImplicitFwdEqFlowTypes(top.location, lhsNt, fName, _, top.flowEnv, fwdFlowDeps, myFlow), hostSyns) - else []; -} - - -function raiseImplicitFwdEqFlowTypes -[Message] ::= l::Location lhsNt::String prod::String attr::String e::Decorated FlowEnv fwdFlowDeps::set:Set myFlow::EnvTree -{ - -- The flow type for `attr` on `lhsNt` - local depsForThisAttr :: set:Set = inhDepsForSyn(attr, lhsNt, myFlow); - -- Actual forwards equation deps not in the flow type for `attr` - local diff :: [String] = set:toList(set:difference(fwdFlowDeps, depsForThisAttr)); - - return case lookupSyn(prod, attr, e) of - | eq :: _ -> [] - | [] -> - if null(diff) then [] - else - [wrn(l, s"In production ${prod}, the implicit copy equation for ${attr} (due to forwarding) would exceed the attribute's flow type because the production forward equation depends on ${implode(", ", diff)}")] - end; -} - diff --git a/grammars/silver/analysis/warnings/flow/Inh.sv b/grammars/silver/analysis/warnings/flow/Inh.sv deleted file mode 100644 index 6c6502ed5..000000000 --- a/grammars/silver/analysis/warnings/flow/Inh.sv +++ /dev/null @@ -1,624 +0,0 @@ -grammar silver:analysis:warnings:flow; - -synthesized attribute warnMissingInh :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.warnMissingInh = false; -} -abstract production warnMissingInhFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnMissingInh = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--warn-missing-inh", flag(warnMissingInhFlag))]; -} - --------------------------------------------------------------------------------- - --- In this file: - --- CHECK 1: Exceeds flowtype --- - Examine overall dependencies of an equation, and see if they use LHS inh --- that are not permissible, given the equation's flow type. --- - Accomplished by explicit calculations in each production. - --- CHECK 1b: Reference set exceeded checks --- - Direct accesses from references need to be checked, they don't emit dependencies otherwise --- - Attribute sections need special checking, too --- - Pattern matching can create dependencies on references, too - --- CHECK 2: Effective Completeness --- - Ensure each inherited attribute that's used actually has an equation --- in existence. --- - Consists of calls to `checkAllEqDeps` --- - Pattern variable accesses can induced *remote* inherited equation checks - --------------------------------------------------------------------------------- - - -{-- - - This is a glorified lambda function, to help look for equations. - - Literally, we're just checking for null here. - - - - @param f The lookup function for the appropriate type of equation - - e.g. `lookupInh(prod, rhs, _, env)` - - @param attr The attribute to look up. - -} -function isEquationMissing -Boolean ::= f::([FlowDef] ::= String) attr::String -{ - return null(f(attr)); -} - -{-- - - False if 'attr' occurs on 'lhsNt' and is an autocopy attribute, - - true otherwise. Used in conjunction with 'filter' to get - - remove "missing equations" that are actually implicit autocopies. - -} -function ignoreIfAutoCopyOnLhs -Boolean ::= lhsNt::String env::Decorated Env attr::String -{ - return !(isAutocopy(attr, env) && !null(getOccursDcl(attr, lhsNt, env))); -} - -{-- - - Given a name of a child, return whether it has an undecorated - - nonterminal type. False if nonsensicle. - -} -function sigNotAReference -Boolean ::= sigName::String e::Decorated Env -{ - local d :: [DclInfo] = getValueDcl(sigName, e); - - -- TODO BUG: it's actually possible for this to to fail to lookup - -- due to aspects renaming the sig name!! We're conservative here and return true if that happens - -- but this could lead to spurious errors. - - -- Suggested fix: maybe we can directly look at the signature, instead of looking - -- up the name in the environment? - - return if null(d) then true else head(d).typerep.isDecorable; -} - -{-- - - Used as a stop-gap measure to ensure equations exist. - - Given a needed equation (represented by FlowVertex 'v'), - - ensure such an equation exists, accounting for: - - 1. Defaults - - 2. Forwards - - 3. Autocopy - - 4. Reference accesses - - - - This gives rise to 'missing transitive dependency' errors. - - The reason this exists is to handle 'taking a reference' - - actions needing to ensure equations were actually provided for - - things we reference. - - - - @param v A value we need an equation for. - - @param l Where to report an error, if it's missing - - @param prodName The full name of the production we're in - - @param prodNt The nonterminal is production belongs to. (For functions, a dummy value is ok) - - @param flowEnv The local flow environment - - @param realEnv The local real environment - - @returns Errors for missing equations - -} -function checkEqDeps -[Message] ::= v::FlowVertex l::Location prodName::String prodNt::String flowEnv::Decorated FlowEnv realEnv::Decorated Env anonResolve::[Pair] -{ - -- We're concerned with missing inherited equations on RHS, LOCAL, and ANON. (Implicitly, FORWARD.) - - return case v of - -- A dependency on an LHS.INH is a flow issue: these equations do not exist - -- locally, so we cannot check them. - | lhsInhVertex(_) -> [] - -- A dependency on an LHS.SYN can be checked locally, but we do not do so here. - -- All productions must have all SYN equations, so those errors are raised elsewhere. - | lhsSynVertex(attrName) -> [] - -- A dependency on an RHS.ATTR. SYN are always present, so we only care about INH here. - -- Filter missing equations for autocopy or for RHS that are references. - | rhsVertex(sigName, attrName) -> - if isInherited(attrName, realEnv) - then if !null(lookupInh(prodName, sigName, attrName, flowEnv)) - || !ignoreIfAutoCopyOnLhs(prodNt, realEnv, attrName) - || !sigNotAReference(sigName, realEnv) - then [] - else [wrn(l, "Equation has transitive dependency on child " ++ sigName ++ "'s inherited attribute for " ++ attrName ++ " but this equation appears to be missing.")] - else [] - -- A dependency on a LOCAL. Technically, local equations may not exist! - -- But let's just assume they do, since `local name :: type = expr;` is the prefered syntax. - | localEqVertex(fName) -> [] - -- A dependency on a LOCAL.ATTR. SYN always exist again, so we only care about INH here. - -- Ignore the FORWARD (a special case of LOCAL), which always has both SYN/INH. - -- And again ignore references. Autocopy isn't relevant to locals, though. - | localVertex(fName, attrName) -> - if isInherited(attrName, realEnv) - then if !null(lookupLocalInh(prodName, fName, attrName, flowEnv)) - || fName == "forward" - || !sigNotAReference(fName, realEnv) - then [] - else [wrn(l, "Equation has transitive dependency on local " ++ fName ++ "'s inherited attribute for " ++ attrName ++ " but this equation appears to be missing.")] - else [] - -- A dependency on a ANON. This do always exist (`decorate expr with..` always has expr.) - | anonEqVertex(fName) -> [] - -- A dependency on ANON.ATTR. Again, SYN are safe. We need to check only for INH. - -- If the equation is missing, then we again filter down to just those equations - -- missing within THIS overall equation. - -- i.e. `top.syn1 = ... missing ...; top.syn2 = top.syn1;` should only raise - -- the missing in the first equation. - | anonVertex(fName, attrName) -> - if isInherited(attrName, realEnv) - then if !null(lookupLocalInh(prodName, fName, attrName, flowEnv)) - then [] - else let - anonl :: Maybe = lookupBy(stringEq, fName, anonResolve) - in if anonl.isJust - then [wrn(anonl.fromJust, "Decoration requires inherited attribute for " ++ attrName ++ ".")] - else [] -- If it's not in the list, then it's a transitive dep from a DIFFERENT equation (and thus reported there) - end - else [] - end; -} -function checkAllEqDeps -[Message] ::= v::[FlowVertex] l::Location prodName::String prodNt::String flowEnv::Decorated FlowEnv realEnv::Decorated Env anonResolve::[Pair] -{ - return flatMap(checkEqDeps(_, l, prodName, prodNt, flowEnv, realEnv, anonResolve), v); -} - - --------------------------------------------------------------------------------- - - -aspect production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); - - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ - if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} - -aspect production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - -- TODO: if LHS is forward, we have to check that we aren't exceeding flow type!! (BUG) - - -- check transitive deps only. Nothing to check for flow types - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) - else []; -} - ------ WARNING TODO BEGIN MASSIVE COPY & PASTE SESSION -aspect production synBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); - - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ - if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} -aspect production synAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); - - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ - if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} -aspect production inhBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - -- check transitive deps only. Nothing to be done for flow types - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) - else []; -} -aspect production inhAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - -- check transitive deps only. Nothing to be done for flow types - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) - else []; -} ------- END AWFUL COPY & PASTE SESSION - -aspect production forwardsTo -top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn("forward", top.frame.lhsNtName, myFlow))); - - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ - if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Forward equation exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} -aspect production forwardInh -top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - -- problem = lhsinh deps - fwd flow type - this inh attribute - local lhsInhExceedsFlowType :: [String] = - set:toList( - set:removeAll( - [case lhs of - | forwardLhsExpr(q) -> q.attrDcl.fullName - end], - set:difference( - lhsInhDeps, - inhDepsForSyn("forward", top.frame.lhsNtName, myFlow)))); - - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ - if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Forward inherited equation exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} - -aspect production localValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - -- check transitive deps only. No worries about flow types. - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) - else []; -} - -aspect production returnDef -top::ProductionStmt ::= 'return' e::Expr ';' -{ - local transitiveDeps :: [FlowVertex] = - expandGraph(e.flowDeps, top.frame.flowGraph); - - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - then checkAllEqDeps(transitiveDeps, top.location, top.frame.fullName, top.frame.lhsNtName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) - else []; -} - --- Skipping `baseCollectionValueDef`: it forwards to `localValueDef` --- Partially skipping `appendCollectionValueDef`: it likewise forwards --- But we do have a special "exceeds check" to do here: -aspect production appendCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - local productionFlowGraph :: ProductionGraph = top.frame.flowGraph; - local transitiveDeps :: [FlowVertex] = expandGraph(e.flowDeps, productionFlowGraph); - - local originalEqDeps :: [FlowVertex] = - expandGraph([localEqVertex(val.lookupValue.fullName)], productionFlowGraph); - - local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); - - local originalEqLhsInhDeps :: set:Set = onlyLhsInh(originalEqDeps); - - local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, originalEqLhsInhDeps)); - - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - -- We can ignore functions. We're checking LHS inhs here... functions don't have any! - && top.frame.hasFullSignature - then if null(lhsInhExceedsFlowType) then [] - else [wrn(top.location, "Local contribution (" ++ val.name ++ " <-) equation exceeds flow dependencies with: " ++ implode(", ", lhsInhExceedsFlowType))] - else []; -} - - --------------------------------------------------------------------------------- - - -{- -Step 2: Let's go check on expressions. This has two purposes: -1. Better error messages for missing equations than the "transitive dependency" ones. - But technically, unneeded and transititve dependencies are covering this. -2. We have to ensure that each individual access from a reference fits within the blessed set. - This is not covered by any other checks. --} - - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - -- TODO? -} - -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ --- This aspect is in two parts. First: we *must* check that any accesses --- on a unknown decorated tree are in the ref-set. - - -- oh no again - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - local eTypeName :: String = performSubstitution(e.typerep, e.upSubst).typeName; - local diff :: [String] = - set:toList(set:removeAll( - inhsForTakingRef(eTypeName, top.flowEnv), -- blessed inhs for a reference - inhDepsForSyn(q.attrDcl.fullName, eTypeName, myFlow))); -- needed inhs - - -- CASE 1: References. This check is necessary and won't be caught elsewhere. - top.errors <- - if null(e.errors) - && (top.config.warnAll || top.config.warnMissingInh) - then - case e.flowVertexInfo of - | hasVertex(_) -> [] -- no check to make, as it was done transitively - -- without a vertex, we're accessing from a reference, and so... - | noVertex() -> - if null(diff) then [] - else [wrn(top.location, "Access of " ++ q.name ++ " from reference requires inherited attributes not known to be supplied to references: " ++ implode(", ", diff))] - end - else []; - ----------------- - - -- CASE 2: More specific errors for things already caught by `checkAllEqDeps`. - -- Equation has transition dep on `i`, but here we can say where this dependency - -- originated: from an syn acces. - top.errors <- - if null(e.errors) - && (top.config.warnAll || top.config.warnMissingInh) - then - case e of - | childReference(lq) -> - if lq.lookupValue.typerep.isDecorable - then - let inhs :: [String] = - -- N.B. we're filtering out autocopies here - filter( - ignoreIfAutoCopyOnLhs(top.frame.lhsNtName, top.env, _), - filter( - isEquationMissing( - lookupInh(top.frame.fullName, lq.lookupValue.fullName, _, top.flowEnv), - _), - set:toList(inhDepsForSyn(q.attrDcl.fullName, eTypeName, myFlow)))) - in if null(inhs) then [] - else [wrn(top.location, "Access of syn attribute " ++ q.name ++ " on " ++ e.unparse ++ " requires missing inherited attributes " ++ implode(", ", inhs) ++ " to be supplied")] - end - else [] - | localReference(lq) -> - if lq.lookupValue.typerep.isDecorable - then - let inhs :: [String] = - filter( - isEquationMissing( - lookupLocalInh(top.frame.fullName, lq.lookupValue.fullName, _, top.flowEnv), - _), - set:toList(inhDepsForSyn(q.attrDcl.fullName, eTypeName, myFlow))) - in if null(inhs) then [] - else [wrn(top.location, "Access of syn attribute " ++ q.name ++ " on " ++ e.unparse ++ " requires missing inherited attributes " ++ implode(", ", inhs) ++ " to be supplied")] - end - else [] - | _ -> [] - end - else []; -} - -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - -- In this case, ONLY check for references. - -- The transitive deps error will be less difficult to figure out when there's - -- an explicit access to the attributes. - top.errors <- - if null(e.errors) - && (top.config.warnAll || top.config.warnMissingInh) - then - case e.flowVertexInfo of - | hasVertex(_) -> [] -- no check to make, as it was done transitively - -- without a vertex, we're accessing from a reference, and so... - | noVertex() -> - if contains(q.attrDcl.fullName, inhsForTakingRef(performSubstitution(e.typerep, e.upSubst).typeName, top.flowEnv)) - then [] - else [wrn(top.location, "Access of inherited attribute " ++ q.name ++ " from a reference is not permitted, as references are not known to be decorated with this attribute.")] - end - else []; -} - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - -- Do nothing. Everything gets taken care of with anonResolve and checkEqDeps at the top-level of the equation -} - - -aspect production attributeSection -top::Expr ::= '(' '.' q::QName ')' -{ - -- oh no again - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - - -- We need to check that the flow sets are acceptable to what we're doing - -- undecorated accesses: flow type for attribute has to be empty - -- decorated accesses: FT has to be subset of refset - local acceptable :: [String] = - if inputType.isDecorated then inhsForTakingRef(inputType.typeName, top.flowEnv) else []; - - top.errors <- - if q.lookupAttribute.found - && (top.config.warnAll || top.config.warnMissingInh) - then - let inhs :: [String] = - filter( - \ x::String -> !contains(x, acceptable), - set:toList(inhDepsForSyn(q.lookupAttribute.fullName, inputType.typeName, myFlow))) - in if null(inhs) then [] - else [wrn(top.location, s"Attribute section (.${q.name}) requires attributes not known to be on '${prettyType(inputType)}': ${implode(", ", inhs)}")] - end - else []; -} - - -{-- - - For pattern matching, we have an obligation to check: - - 1. If we invented an anon vertex type for the scrutinee, then it's a sink, and - - we need to check that nothing more than the ref set was depended upon. - -} -aspect production matchPrimitiveReal -top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr -{ - -- slightly awkward way to recover the name and whether/not it was invented - local sinkVertexName :: Maybe = - case e.flowVertexInfo, pr.scrutineeVertexType of - | noVertex(), anonVertexType(n) -> just(n) - | _, _ -> nothing() - end; - - -- These should be the only ones that can reference our anon sink - local transitiveDeps :: [FlowVertex] = - expandGraph(top.flowDeps, top.frame.flowGraph); - - pr.receivedDeps = transitiveDeps; - - -- just the deps on inhs of our sink - local inhDeps :: [String] = - toAnonInhs(transitiveDeps, sinkVertexName.fromJust, top.env); - - -- Subtract the ref set from our deps - local diff :: [String] = - set:toList(set:removeAll( - inhsForTakingRef(e.typerep.typeName, top.flowEnv), - set:add(inhDeps, set:empty(compareString)))); - - top.errors <- - if null(e.errors) - && (top.config.warnAll || top.config.warnMissingInh) - && sinkVertexName.isJust - && !null(diff) - then [wrn(e.location, "Pattern match on reference has transitive dependencies on " ++ implode(", ", diff) ++ " which are not known to be supplied to references")] - else []; - -} - -function toAnonInhs -[String] ::= v::[FlowVertex] vertex::String env::Decorated Env -{ - return - case v of - | anonVertex(n, inh) :: tl -> - if vertex == n && isInherited(inh, env) - then inh :: toAnonInhs(tl, vertex, env) - else toAnonInhs(tl, vertex, env) - | _ :: tl -> toAnonInhs(tl, vertex, env) - | [] -> [] - end; -} - -autocopy attribute receivedDeps :: [FlowVertex] occurs on VarBinders, VarBinder, PrimPatterns, PrimPattern; - -aspect production varVarBinder -top::VarBinder ::= n::Name -{ - -- fName is our invented vertex name for the pattern variable - local requiredInhs :: [String] = - toAnonInhs(top.receivedDeps, fName, top.env); - - -- Check for equation's existence: - -- Prod: top.matchingAgainst.fromJust.fullName - -- Child: top.bindingName - -- Inh: each of requiredInhs - local missingInhs :: [String] = - filter(remoteProdMissingEq(top.matchingAgainst.fromJust, top.bindingName, _, top.env, top.flowEnv), requiredInhs); - - top.errors <- - if (top.config.warnAll || top.config.warnMissingInh) - && top.bindingType.isDecorable - && top.matchingAgainst.isJust - && !null(missingInhs) - then [wrn(top.location, s"Pattern variable '${n.name}' has transitive dependencies with missing remote equations.\n\tRemote production: ${top.matchingAgainst.fromJust.fullName}\n\tChild: ${top.bindingName}\n\tMissing inherited equations for: ${implode(", ", missingInhs)}")] - else []; -} - -function remoteProdMissingEq -Boolean ::= prod::DclInfo sigName::String attrName::String realEnv::Decorated Env flowEnv::Decorated FlowEnv -{ - return - null(lookupInh(prod.fullName, sigName, attrName, flowEnv)) && -- no equation - ignoreIfAutoCopyOnLhs(prod.namedSignature.outputElement.typerep.typeName, realEnv, attrName); -- not autocopy (and on lhs) -} - --------------------------------------------------------------------------------- - --- TODO: There are a few final places where we need to `checkEqDeps` for the sake of `anonVertex`s - --- global declarations, action blocks (production, terminal, disam, etc) - --- But we don't create flowEnv information for these locations so they can't be checked... oops --- (e.g. `checkEqDeps` wants a production fName to look things up about.) - - diff --git a/grammars/silver/analysis/warnings/flow/MWDA.sv b/grammars/silver/analysis/warnings/flow/MWDA.sv deleted file mode 100644 index 5b3236db5..000000000 --- a/grammars/silver/analysis/warnings/flow/MWDA.sv +++ /dev/null @@ -1,81 +0,0 @@ -grammar silver:analysis:warnings:flow; - --- data structures -imports silver:util; -imports silver:util:raw:treeset as set; - --- driver stuff -imports silver:util:cmdargs; -imports silver:driver only parseArgs; -imports silver:driver:util only isExportedBy; -imports silver:analysis:warnings; - --- silver -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:definition:env; - --- flow analysis -imports silver:definition:flow:ast; -imports silver:definition:flow:driver only ProductionGraph, FlowType, flowVertexEq, prod, inhDepsForSyn, findProductionGraph, expandGraph, onlyLhsInh; - --- the modifications we need to be aware of -imports silver:modification:autocopyattr only isAutocopy; -imports silver:modification:collection; -imports silver:modification:defaultattr; -imports silver:modification:primitivepattern; -imports silver:modification:copper only parserAttributeDefLHS; - -function isOccursSynthesized -Boolean ::= occs::DclInfo e::Decorated Env -{ - return case getAttrDcl(occs.attrOccurring, e) of - | at :: _ -> at.isSynthesized - | _ -> false - end; -} - --- TODO: this should probably not be a thing I have to write here -function isAutocopy -Boolean ::= attr::String e::Decorated Env -{ - return case getAttrDclAll(attr, e) of - | at :: _ -> at.isAutocopy - | _ -> false - end; -} --- TODO: why is this a thing I have to write here. Sheesh. FIX THIS. --- The real fix is for our vertexes to remember whether they are syn/inh. -function isInherited -Boolean ::= a::String e::Decorated Env -{ - return case getAttrDclAll(a, e) of - | at :: _ -> at.isInherited - | _ -> false - end; -} - -function isLhsInh -Boolean ::= v::FlowVertex -{ - return case v of - | lhsInhVertex(_) -> true - | _ -> false - end; -} - - - --- TODO: better way of generating warnings. We ad-hoc check for errors before --- raising these warnings, but this is inherently fragile and results in crash --- bugs when running the MWDA on erroneous grammars. --- (easily fixed by running regular build first, but still.) --- (Possible solution approach: raise these with a different attribute than --- `errors`, but we'd probably want "monoid attributes" to make that ergonomic.) - --- TODO: are we ever checking the flow types for default equations? --- These shouldn't need checking as part of inference, but default equations can --- exceed *explicit* flow types, and I don't think anything is checking that yet. - - diff --git a/grammars/silver/analysis/warnings/flow/OrphanedEquation.sv b/grammars/silver/analysis/warnings/flow/OrphanedEquation.sv deleted file mode 100644 index 54df23122..000000000 --- a/grammars/silver/analysis/warnings/flow/OrphanedEquation.sv +++ /dev/null @@ -1,168 +0,0 @@ -grammar silver:analysis:warnings:flow; - -synthesized attribute warnEqdef :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.warnEqdef = false; -} -abstract production warnEqdefFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnEqdef = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--warn-eqdef", flag(warnEqdefFlag))]; -} - -aspect production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local exportedBy :: [String] = - if top.frame.hasPartialSignature - then [top.frame.sourceGrammar, attr.dcl.sourceGrammar] - else [attr.dcl.sourceGrammar]; -- defaults can only be listed together with occurs. - - -- Orphaned equation check - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnEqdef) - && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) - then [wrn(top.location, "Orphaned equation: " ++ attr.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] - else []; - - -- Duplicate equation check - top.errors <- - if length(dl.lookupEqDefLHS) > 1 - then [wrn(top.location, "Duplicate equation for " ++ attr.name ++ " in production " ++ top.frame.fullName)] - else []; -} - -aspect production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local exportedBy :: [String] = - case dl of - -- Exported by the declaration of the thing we're giving inh to, or to the occurs - | localDefLHS(q) -> [q.lookupValue.dcl.sourceGrammar, attr.dcl.sourceGrammar] - -- For rhs or forwards, that's the production. - | _ -> [top.frame.sourceGrammar, attr.dcl.sourceGrammar] - end; - - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnEqdef) - && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) - then [wrn(top.location, "Orphaned equation: " ++ attr.name ++ " on " ++ dl.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] - -- Now, check for duplicate equations! - else []; - - top.errors <- - if length(dl.lookupEqDefLHS) > 1 - then [wrn(top.location, "Duplicate equation for " ++ attr.name ++ " on " ++ dl.name ++ " in production " ++ top.frame.fullName)] - else []; -} - - ---- FROM COLLECTIONS - -aspect production synBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local exportedBy :: [String] = - if top.frame.hasPartialSignature - then [top.frame.sourceGrammar, attr.dcl.sourceGrammar] - else [attr.dcl.sourceGrammar]; -- defaults can only be listed together with occurs. - - -- Orphaned equation check - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnEqdef) - && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) - then [wrn(top.location, "Orphaned equation: " ++ attr.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] - else []; - - -- Duplicate equation check - top.errors <- - if length(dl.lookupEqDefLHS) > 1 - then [wrn(top.location, "Duplicate equation for " ++ attr.name ++ " in production " ++ top.frame.fullName)] - else []; -} -aspect production inhBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local exportedBy :: [String] = - case dl of - -- Exported by the declaration of the thing we're giving inh to, or to the occurs - | localDefLHS(q) -> [q.lookupValue.dcl.sourceGrammar, attr.dcl.sourceGrammar] - -- For rhs or forwards, that's the production. - | _ -> [top.frame.sourceGrammar, attr.dcl.sourceGrammar] - end; - - top.errors <- - if dl.found && attr.found - && (top.config.warnAll || top.config.warnEqdef) - && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) - then [wrn(top.location, "Orphaned equation: " ++ attr.name ++ " on " ++ dl.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] - -- Now, check for duplicate equations! - else []; - - top.errors <- - if length(dl.lookupEqDefLHS) > 1 - then [wrn(top.location, "Duplicate equation for " ++ attr.name ++ " on " ++ dl.name ++ " in production " ++ top.frame.fullName)] - else []; -} - - ---- For our DefLHSs: - -{-- - - A lookup for other instances of this equation on this DefLHS. - -} -synthesized attribute lookupEqDefLHS :: [FlowDef] occurs on DefLHS; - -aspect production childDefLHS -top::DefLHS ::= q::Decorated QName -{ - -- prod, child, attr - top.lookupEqDefLHS = lookupInh(top.frame.fullName, q.lookupValue.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv); -} -aspect production lhsDefLHS -top::DefLHS ::= q::Decorated QName -{ - -- prod, attr - top.lookupEqDefLHS = lookupSyn(top.frame.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv); -} -aspect production localDefLHS -top::DefLHS ::= q::Decorated QName -{ - -- prod, local, attr - top.lookupEqDefLHS = lookupLocalInh(top.frame.fullName, q.lookupValue.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv); -} -aspect production forwardDefLHS -top::DefLHS ::= q::Decorated QName -{ - -- prod, attr - top.lookupEqDefLHS = lookupFwdInh(top.frame.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv); -} -aspect production defaultLhsDefLHS -top::DefLHS ::= q::Decorated QName -{ - -- nt, attr - top.lookupEqDefLHS = lookupDef(top.frame.lhsNtName, top.defLHSattr.attrDcl.fullName, top.flowEnv); -} -aspect production errorDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.lookupEqDefLHS = []; -} -aspect production parserAttributeDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.lookupEqDefLHS = []; -- TODO: maybe error? -} - diff --git a/grammars/silver/analysis/warnings/flow/OrphanedOccurs.sv b/grammars/silver/analysis/warnings/flow/OrphanedOccurs.sv deleted file mode 100644 index f51c3d7f8..000000000 --- a/grammars/silver/analysis/warnings/flow/OrphanedOccurs.sv +++ /dev/null @@ -1,47 +0,0 @@ -grammar silver:analysis:warnings:flow; - -synthesized attribute warnOrphaned :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.warnOrphaned = false; -} -abstract production warnOrphanedFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnOrphaned = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--warn-orphaned", flag(warnOrphanedFlag))]; -} - -aspect production attributionDcl -top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - local isClosedNt :: Boolean = - case nt.lookupType.dcls of - | ntDcl(_, _, _, _, _, closed) :: _ -> closed - | _ -> false -- default, if the lookup fails - end; - - top.errors <- - if nt.lookupType.found && at.lookupAttribute.found - && (top.config.warnAll || top.config.warnOrphaned) - && !isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar, at.lookupAttribute.dcl.sourceGrammar], top.compiledGrammars) - then [wrn(top.location, "Orphaned occurs declaration: " ++ at.lookupAttribute.fullName ++ " on " ++ nt.lookupType.fullName)] - -- If this is a non-closed NT, or not a synthesized attribute, then we're done. - else []; - - top.errors <- - if !nt.lookupType.found || !at.lookupAttribute.found || !isClosedNt || !at.lookupAttribute.dcl.isSynthesized then [] - -- For closed nt, either we're exported by only the nt, OR there MUST be a default! - else if !isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars) - && null(lookupDef(nt.lookupType.fullName, at.lookupAttribute.fullName, top.flowEnv)) - then [wrn(top.location, at.lookupAttribute.fullName ++ " cannot occur on " ++ nt.lookupType.fullName ++ " because that nonterminal is closed, and this attribute does not have a default equation.")] - else []; -} - diff --git a/grammars/silver/analysis/warnings/flow/OrphanedProduction.sv b/grammars/silver/analysis/warnings/flow/OrphanedProduction.sv deleted file mode 100644 index 79c86444d..000000000 --- a/grammars/silver/analysis/warnings/flow/OrphanedProduction.sv +++ /dev/null @@ -1,46 +0,0 @@ -grammar silver:analysis:warnings:flow; - -synthesized attribute warnFwd :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.warnFwd = false; -} -abstract production warnFwdFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnFwd = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--warn-fwd", flag(warnFwdFlag))]; -} - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - local ntDefGram :: String = - substring(0, lastIndexOf(":", namedSig.outputElement.typerep.typeName), namedSig.outputElement.typerep.typeName); - - local isClosedNt :: Boolean = - case getTypeDclAll(namedSig.outputElement.typerep.typeName, top.env) of - | ntDcl(_, _, _, _, _, closed) :: _ -> closed - | _ -> false -- default, if the lookup fails - end; - - top.errors <- - if null(body.errors ++ ns.errors) - && (top.config.warnAll || top.config.warnFwd) - -- If this production does not forward - && null(body.uniqueSignificantExpression) - -- AND this is not a closed nonterminal - && !isClosedNt - -- AND this production is not exported by the nonterminal definition grammar... even including options - && !isExportedBy(top.grammarName, [ntDefGram], top.compiledGrammars) - then [wrn(top.location, "Orphaned production: " ++ id.name ++ " on " ++ namedSig.outputElement.typerep.typeName)] - else []; -} - diff --git a/grammars/silver/compiler/analysis/typechecking/core/Annotation.sv b/grammars/silver/compiler/analysis/typechecking/core/Annotation.sv new file mode 100644 index 000000000..6dae4c3d9 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/Annotation.sv @@ -0,0 +1,7 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production annotationDcl +top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.errors <- te.errorsKindStar; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/AspectDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/AspectDcl.sv new file mode 100644 index 000000000..2a7dc1b3f --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/AspectDcl.sv @@ -0,0 +1,122 @@ +grammar silver:compiler:analysis:typechecking:core; + +attribute upSubst, downSubst, finalSubst occurs on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectRHSElem, AspectFunctionSignature, AspectFunctionLHS; + +aspect production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = ns.finalSubst; + + errCheck1 = check(realSig.typeScheme.typerep, namedSig.typeScheme.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Aspect for '" ++ id.name ++ "' does not have the right signature.\nExpected: " + ++ errCheck1.leftpp ++ "\nActual: " ++ errCheck1.rightpp)] + else + -- dcl is potentially not found, accessing it can crash. + -- so check on dcls for this. + case id.lookupValue.dcls of + | prodDcl (_, _) :: _ -> [] + | funDcl (_) :: _ -> [err(top.location, "Production aspect for '" ++ id.name ++ "' should be a 'function' aspect instead.")] + | _ -> [err(id.location, id.name ++ " is not a production.")] + end; + + ns.downSubst = emptySubst(); + thread downSubst, upSubst on ns, errCheck1, body; + + ns.finalSubst = errCheck1.upSubst; +} + + +aspect production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = ns.finalSubst; + + errCheck1 = check(realSig.typeScheme.typerep, namedSig.typeScheme.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Aspect for '" ++ id.name ++ "' does not have the right signature.\nExpected: " + ++ errCheck1.leftpp ++ "\nActual: " ++ errCheck1.rightpp)] + else + -- must be on dcls because lookup may have failed. + case id.lookupValue.dcls of + | funDcl (_) :: _ -> [] + | prodDcl (_, _) :: _ -> [err(top.location, "Function aspect for '" ++ id.name ++ "' should be a 'production' aspect instead.")] + | _ -> [err(id.location, id.name ++ " is not a function.")] + end; + + ns.downSubst = emptySubst(); + thread downSubst, upSubst on ns, errCheck1, body; + + ns.finalSubst = errCheck1.upSubst; +} + +-- + +aspect production aspectProductionSignature +top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS +{ + propagate downSubst, upSubst; +} + +aspect production aspectProductionLHSFull +top::AspectProductionLHS ::= id::Name t::Type +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, errCheck1, top; + + errCheck1 = check(rType, t); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] + else []; +} + +aspect production aspectRHSElemNil +top::AspectRHS ::= +{ + propagate downSubst, upSubst; +} + +aspect production aspectRHSElemCons +top::AspectRHS ::= h::AspectRHSElem t::AspectRHS +{ + propagate downSubst, upSubst; +} + +aspect production aspectRHSElemFull +top::AspectRHSElem ::= id::Name t::Type +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, errCheck1, top; + + errCheck1 = check(rType, t); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] + else []; +} + +aspect production aspectFunctionSignature +top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS +{ + propagate downSubst, upSubst; +} + +aspect production functionLHSType +top::AspectFunctionLHS ::= t::TypeExpr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, errCheck1, top; + + errCheck1 = check(rType, t.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Type incorrect in aspect signature. Expected: " ++ errCheck1.leftpp ++ " Got: " ++ errCheck1.rightpp)] + else []; +} + diff --git a/grammars/silver/compiler/analysis/typechecking/core/AttributeDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/AttributeDcl.sv new file mode 100644 index 000000000..ef6021ef2 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/AttributeDcl.sv @@ -0,0 +1,14 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production attributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.errors <- te.errorsKindStar; +} + +aspect production attributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.errors <- te.errorsKindStar; +} + diff --git a/grammars/silver/compiler/analysis/typechecking/core/Checking.sv b/grammars/silver/compiler/analysis/typechecking/core/Checking.sv new file mode 100644 index 000000000..5c07fe5d6 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/Checking.sv @@ -0,0 +1,76 @@ +grammar silver:compiler:analysis:typechecking:core; + +import silver:compiler:definition:type; + +synthesized attribute leftpp :: String; +synthesized attribute rightpp :: String; +synthesized attribute typeerror :: Boolean; + +nonterminal TypeCheck with upSubst, downSubst, finalSubst, leftpp, rightpp, typeerror; + +-- TODO: we could probably change this to 'expected' and 'actual' and generate the error message here? +abstract production check +top::TypeCheck ::= l::Type r::Type +{ + top.upSubst = unifyCheck(l, r, top.downSubst); + + top.typeerror = top.upSubst.failure; + + local finleft :: Type = performSubstitution(l, top.finalSubst); + + local finright :: Type = performSubstitution(r, top.finalSubst); + + local fv :: [TyVar] = setUnionTyVars(finleft.freeVariables, finright.freeVariables); + + top.leftpp = prettyTypeWith(finleft, fv); + top.rightpp = prettyTypeWith(finright, fv); +} + +abstract production checkNonterminal +top::TypeCheck ::= e::Decorated Env allowDecorableSkolems::Boolean l::Type +{ + local refined :: Type = + performSubstitution(l, top.downSubst); + + top.upSubst = composeSubst(ignoreFailure(top.downSubst), refined.unifyInstanceNonterminal); + + top.typeerror = + case performSubstitution(refined, top.upSubst) of + | skolemType(_) -> !allowDecorableSkolems || null(searchEnvTree(refined.typeName, e.occursTree)) + | _ -> top.upSubst.failure && !refined.isError + end; + + top.leftpp = prettyType(performSubstitution(l, top.finalSubst)); + top.rightpp = "a nonterminal"; +} +abstract production checkDecorated +top::TypeCheck ::= l::Type +{ + local refined :: Type = + performSubstitution(l, top.downSubst); + + top.upSubst = composeSubst(ignoreFailure(top.downSubst), refined.unifyInstanceDecorated); + + top.typeerror = top.upSubst.failure && !refined.isError; + + top.leftpp = prettyType(performSubstitution(l, top.finalSubst)); + top.rightpp = "a decorated nonterminal"; +} +abstract production checkDecorable +top::TypeCheck ::= e::Decorated Env l::Type +{ + local refined :: Type = + performSubstitution(l, top.downSubst); + + top.upSubst = composeSubst(ignoreFailure(top.downSubst), refined.unifyInstanceDecorable); + + top.typeerror = + case performSubstitution(refined, top.upSubst) of + | skolemType(_) -> null(searchEnvTree(refined.typeName, e.occursTree)) + | _ -> top.upSubst.failure && !refined.isError + end; + + top.leftpp = prettyType(performSubstitution(l, top.finalSubst)); + top.rightpp = "a decorable type"; +} + diff --git a/grammars/silver/compiler/analysis/typechecking/core/ClassDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/ClassDcl.sv new file mode 100644 index 000000000..5308fa60c --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/ClassDcl.sv @@ -0,0 +1,18 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production defaultConstraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr '=' e::Expr ';' +{ + top.errors <- ty.errorsKindStar; + + local errCheck1::TypeCheck = check(ty.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(e.location, s"Member ${id.name} has expected type ${errCheck1.leftpp}, but the expression has actual type ${errCheck1.rightpp}")] + else []; + + e.downSubst = emptySubst(); + errCheck1.downSubst = e.upSubst; + e.finalSubst = errCheck1.upSubst; + errCheck1.finalSubst = e.finalSubst; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/Context.sv b/grammars/silver/compiler/analysis/typechecking/core/Context.sv new file mode 100644 index 000000000..81c17d709 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/Context.sv @@ -0,0 +1,190 @@ +grammar silver:compiler:analysis:typechecking:core; + +import silver:compiler:analysis:warnings:flow only inhDepsForSynOnType; +import silver:util:treeset as set; + +inherited attribute contextLoc::Location occurs on Contexts, Context; +inherited attribute contextSource::String occurs on Contexts, Context; +monoid attribute contextErrors::[Message] occurs on Contexts, Context; +attribute downSubst, upSubst occurs on Contexts, Context; +propagate contextLoc, contextSource, contextErrors, downSubst, upSubst on Contexts; + +flowtype contextErrors {contextLoc, contextSource, env, frame, grammarName, compiledGrammars, config} on Context; + +aspect production instContext +top::Context ::= cls::String t::Type +{ + requiredContexts.contextLoc = top.contextLoc; + requiredContexts.contextSource = s"the instance for ${prettyContext(top)}, arising from ${top.contextSource}"; + + -- Duplicates are checked at the instance declaration + top.contextErrors := + -- Check for ambiguous type variables. + -- Since we've already computed the final substitution, if t has any, + -- they could unify with something more specific in instance resolution here, + -- and unify with something else in solving another instance later on. + if !null(t.freeFlexibleVars) + then map( + \ tv::TyVar -> err(top.contextLoc, s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved."), + t.freeFlexibleVars) + else if null(top.resolved) + then [err(top.contextLoc, s"Could not find an instance for ${prettyContext(top)} (arising from ${top.contextSource})")] + else requiredContexts.contextErrors; + + production substT::Type = performSubstitution(t, top.downSubst); + top.upSubst = + case substT of + -- Only refine to the *undecorated* type based on instances, since if left + -- unrefined the type will be treated as decorated, anyway (and leaving it + -- unrefined will result in less confusing behavior.) + -- If there is an instance for the undecorated type, and all instances for + -- the decorated type are typeError instances (or there are none), + -- then specialize to the undecorated type, since the decorated type will + -- always give a type error. + | ntOrDecType(nt, _, _) when + !null(getInstanceDcl(cls, nt, top.env)) && + all(map((.isTypeError), getInstanceDcl(cls, substT.defaultSpecialization, top.env))) -> + composeSubst(top.downSubst, substT.unifyInstanceNonterminal) + | _ -> top.downSubst + end; +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + requiredContexts.contextLoc = top.contextLoc; + requiredContexts.contextSource = s"the instance for ${prettyContext(top)}, arising from ${top.contextSource}"; + + top.contextErrors := + -- Check for ambiguous type variables. + -- Since we've already computed the final substitution, if ntty has any, + -- they could unify with something more specific in instance resolution here, + -- and unify with something else in solving another instance later on. + if !null(ntty.freeFlexibleVars) + then map( + \ tv::TyVar -> err(top.contextLoc, s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved."), + ntty.freeFlexibleVars) + -- atty should never have free type variables if ntty does not, except in case of errors elsewhere. + else {-if !null(atty.freeFlexibleVars) + then error(s"got atty with free vars") + else-} if null(top.resolvedOccurs) + then [err(top.contextLoc, s"Could not find an instance for ${prettyContext(top)} (arising from ${top.contextSource})")] + else requiredContexts.contextErrors; + + -- Not refining based on occurs-on constraints for now, since the appropriate inference should happen on the associated decoration + top.upSubst = top.downSubst; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + requiredContexts.contextLoc = top.contextLoc; + requiredContexts.contextSource = s"the instance for ${prettyContext(top)}, arising from ${top.contextSource}"; + + top.contextErrors := + -- Check for ambiguous type variables. + -- Since we've already computed the final substitution, if t has any, + -- they could unify with something more specific in instance resolution here, + -- and unify with something else in solving another instance later on. + if !null(ntty.freeFlexibleVars) || (!ntty.isNonterminal && !null(inhs.freeFlexibleVars)) + then map( + \ tv::TyVar -> err(top.contextLoc, s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved."), + ntty.freeFlexibleVars ++ inhs.freeFlexibleVars) + -- Give a more helpful error message when there are flexible type vars in inhs but not in ntty, + -- when we might be able to resolve the ambiguity via flow types. + else if !null(inhs.freeFlexibleVars) + then map( + \ tv::TyVar -> err( + top.contextLoc, + s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved. Note: this ambiguity might be resolved by specifying an explicit flowtype for ${attr} on ${ntty.typeName}"), + inhs.freeFlexibleVars) + -- atty should never have free type variables if ntty does not, except in case of errors elsewhere. + else {-if !null(atty.freeFlexibleVars) + then error(s"got atty with free vars") + else-} if null(top.resolvedOccurs) + then [err(top.contextLoc, s"Could not find an instance for ${prettyContext(top)} (arising from ${top.contextSource})")] + else requiredContexts.contextErrors; + + production substNtty::Type = performSubstitution(ntty, top.downSubst); + production substInhs::Type = performSubstitution(inhs, top.downSubst); + top.upSubst = + -- If the nonterminal type is known but the flow type inh set is unspecialized, + -- specialize it to the specified flow type of the attribute on the nonterminal. + -- This is a bit of a hack, since we don't properly support functional dependencies. + if null(substNtty.freeFlexibleVars) && !null(substInhs.freeFlexibleVars) && substNtty.isNonterminal + then + case lookup(attr, getFlowTypeSpecFor(substNtty.typeName, top.flowEnv)) of + | just((specInhs, _)) -> composeSubst(top.downSubst, unify(substInhs, inhSetType(sort(specInhs)))) + | _ -> top.downSubst + end + else top.downSubst; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + requiredContexts.contextLoc = top.contextLoc; + requiredContexts.contextSource = s"the instance for ${prettyContext(top)}, arising from ${top.contextSource}"; + + top.contextErrors := + -- Check for ambiguous type variables. + -- Since we've already computed the final substitution, if t has any, + -- they could unify with something more specific in instance resolution here, + -- and unify with something else in solving another instance later on. + if !null(ntty.freeFlexibleVars) + then map( + \ tv::TyVar -> err(top.contextLoc, s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved."), + ntty.freeFlexibleVars) + else if null(top.resolvedOccurs) + then [err(top.contextLoc, s"Could not find an instance for ${prettyContext(top)} (arising from ${top.contextSource})")] + else requiredContexts.contextErrors; + + -- Not refining based on occurs-on constraints for now, since the appropriate inference should happen on the associated decoration + top.upSubst = top.downSubst; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + requiredContexts.contextLoc = top.contextLoc; + requiredContexts.contextSource = s"the instance for ${prettyContext(top)}, arising from ${top.contextSource}"; + + -- Note that ambiguous type variables are permitted here, + -- since they can be consistently type-checked at runtime. + top.contextErrors := + if !t.isTypeable && null(top.resolved) + then [err(top.contextLoc, s"Could not find an instance for ${prettyContext(top)} (arising from ${top.contextSource})")] + else requiredContexts.contextErrors; + + top.upSubst = top.downSubst; -- No effect on decoratedness +} + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.contextErrors := + -- Check for ambiguous type variables. + -- Since we've already computed the final substitution, if i1 or i2 has any, + -- they could unify with something more specific in instance resolution here, + -- and unify with something else in solving another instance later on. + if !null(i1.freeFlexibleVars ++ i2.freeFlexibleVars) + then map( + \ tv::TyVar -> err(top.contextLoc, s"Ambiguous type variable ${findAbbrevFor(tv, top.freeVariables)} (arising from ${top.contextSource}) prevents the constraint ${prettyContext(top)} from being solved."), + i1.freeFlexibleVars ++ i2.freeFlexibleVars) + else + case getMaxInhSetMembers([], i1, top.env), getMinInhSetMembers([], i2, top.env) of + | (just(inhs1), _), (inhs2, _) when all(map(contains(_, inhs2), inhs1)) -> [] + | (_, tvs1), (_, tvs2) when any(map(contains(_, tvs2), tvs1)) -> [] + | _, _ -> [err(top.contextLoc, s"${prettyTypeWith(i1, top.freeVariables)} is not a subset of ${prettyTypeWith(i2, top.freeVariables)} (arising from ${top.contextSource})")] + end; + + top.upSubst = top.downSubst; -- No effect on decoratedness +} + +aspect production typeErrorContext +top::Context ::= msg::String +{ + top.contextErrors := [err(top.contextLoc, msg ++ " (arising from " ++ top.contextSource ++ ")")]; + + top.upSubst = top.downSubst; -- No effect on decoratedness +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/Expr.sv b/grammars/silver/compiler/analysis/typechecking/core/Expr.sv new file mode 100644 index 000000000..3e7c9d443 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/Expr.sv @@ -0,0 +1,398 @@ +grammar silver:compiler:analysis:typechecking:core; + +attribute upSubst, downSubst, finalSubst occurs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoExpr, AnnoAppExprs; + +propagate upSubst, downSubst + on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoExpr, AnnoAppExprs + excluding + undecoratedAccessHandler, forwardAccess, decoratedAccessHandler, + and, or, notOp, ifThenElse, plus, minus, multiply, divide, modulus, + decorateExprWith, exprInh, presentAppExpr, + terminalConstructor, noteAttachment; + +attribute contexts occurs on Expr; +aspect default production +top::Expr ::= +{ + top.contexts = []; +} + +aspect production productionReference +top::Expr ::= q::PartiallyDecorated QName +{ + contexts.contextLoc = q.location; + contexts.contextSource = "the use of " ++ q.name; + top.errors <- contexts.contextErrors; + top.contexts = typeScheme.contexts; +} + +aspect production functionReference +top::Expr ::= q::PartiallyDecorated QName +{ + contexts.contextLoc = q.location; + contexts.contextSource = "the use of " ++ q.name; + top.errors <- contexts.contextErrors; + top.contexts = typeScheme.contexts; +} + +aspect production globalValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + contexts.contextLoc = q.location; + contexts.contextSource = "the use of " ++ q.name; + top.errors <- contexts.contextErrors; + top.contexts = typeScheme.contexts; +} + +aspect production classMemberReference +top::Expr ::= q::PartiallyDecorated QName +{ + instHead.contextLoc = q.location; + instHead.contextSource = "the use of " ++ q.name; + top.errors <- instHead.contextErrors; + + contexts.contextLoc = q.location; + contexts.contextSource = "the use of " ++ q.name; + top.errors <- contexts.contextErrors; + + top.contexts = typeScheme.contexts; +} + +aspect production application +top::Expr ::= e::Expr '(' es::AppExprs ',' anns::AnnoAppExprs ')' +{ + -- If e's contexts include unrefined ntOrDecTypes at this point (arising from + -- es' types, presumably), then refine these ntOrDecTypes types using e's + -- contexts in the environment. + production infContexts::Contexts = foldContexts(e.contexts); + infContexts.env = top.env; + infContexts.flowEnv = top.flowEnv; + + thread downSubst, upSubst on top, e, es, anns, infContexts, forward; +} + +aspect production access +top::Expr ::= e::Expr '.' q::QNameAttrOccur +{ + propagate upSubst, downSubst; +} + +aspect production undecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- We might have gotten here via a 'ntOrDec' type. So let's make certain we're UNdecorated, + -- ensuring that type's specialization, otherwise we could end up in trouble! + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + errCheck1 = checkNonterminal(top.env, true, e.typerep); + + -- TECHNICALLY, I think the current implementation makes this impossible, + -- But let's leave it since it's the right thing to do. + top.errors <- + if errCheck1.typeerror && q.found + then [err(top.location, "Access of " ++ q.name ++ " from a decorated type.")] + else []; + + thread downSubst, upSubst on top, errCheck1, forward; +} + +aspect production accessBouncer +top::Expr ::= target::(Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) e::Expr q::PartiallyDecorated QNameAttrOccur +{ + propagate upSubst, downSubst; +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + errCheck1 = checkDecorated(e.typerep); + + thread downSubst, upSubst on top, e, errCheck1, top; + + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute forward being accessed from an undecorated type.")] + else []; +} + +aspect production decoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- We might have gotten here via a 'ntOrDec' type. So let's make certain we're decorated, + -- ensuring that type's specialization, otherwise we could end up in trouble! + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + errCheck1 = checkDecorated(e.typerep); + + -- TECHNICALLY, I think the current implementation makes this impossible, + -- But let's leave it since it's the right thing to do. + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ q.name ++ " being accessed from an undecorated type.")] + else []; + + thread downSubst, upSubst on top, errCheck1, forward; +} + + +aspect production noteAttachment +top::Expr ::= 'attachNote' note::Expr 'on' e::Expr 'end' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, note, e, errCheck1, top; + + errCheck1 = check(note.typerep, nonterminalType("silver:core:OriginNote", [], false)); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "First argument to attachNote must be OriginNote, was " ++ errCheck1.leftpp)] + else []; +} + +aspect production and +top::Expr ::= e1::Expr '&&' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, errCheck2, top; + + errCheck1 = check(e1.typerep, boolType()); + errCheck2 = check(e2.typerep, boolType()); + top.errors <- + if errCheck1.typeerror + then [err(e1.location, "First operand to && must be type bool. Got instead type " ++ errCheck1.leftpp)] + else []; + top.errors <- + if errCheck2.typeerror + then [err(e2.location, "First operand to && must be type bool. Got instead type " ++ errCheck2.leftpp)] + else []; +} + +aspect production or +top::Expr ::= e1::Expr '||' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, errCheck2, top; + + errCheck1 = check(e1.typerep, boolType()); + errCheck2 = check(e2.typerep, boolType()); + top.errors <- + if errCheck1.typeerror + then [err(e1.location, "First operand to || must be type bool. Got instead type " ++ errCheck1.leftpp)] + else []; + top.errors <- + if errCheck2.typeerror + then [err(e2.location, "First operand to || must be type bool. Got instead type " ++ errCheck2.leftpp)] + else []; +} + +aspect production notOp +top::Expr ::= '!' e1::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, errCheck1, top; + + errCheck1 = check(e1.typerep, boolType()); + top.errors <- + if errCheck1.typeerror + then [err(e1.location, "Operand to ! must be type bool. Got instead type " ++ errCheck1.leftpp)] + else []; +} + +aspect production ifThenElse +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, e3, errCheck1, errCheck2, top; + + errCheck1 = check(e2.typerep, e3.typerep); + errCheck2 = check(e1.typerep, boolType()); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Then and else branch must have the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + top.errors <- + if errCheck2.typeerror + then [err(e1.location, "Condition must have the type Boolean. Instead it is " ++ errCheck2.leftpp)] + else []; +} + +aspect production plus +top::Expr ::= e1::Expr '+' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, top; + + errCheck1 = check(e1.typerep, e2.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operands to + must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operands to + must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} + +aspect production minus +top::Expr ::= e1::Expr '-' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, top; + + errCheck1 = check(e1.typerep, e2.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operands to - must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operands to - must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} +aspect production multiply +top::Expr ::= e1::Expr '*' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, top; + + errCheck1 = check(e1.typerep, e2.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operands to * must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operands to * must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} +aspect production divide +top::Expr ::= e1::Expr '/' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, top; + + errCheck1 = check(e1.typerep, e2.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operands to / must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operands to / must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} +aspect production modulus +top::Expr ::= e1::Expr '%' e2::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, e2, errCheck1, top; + + errCheck1 = check(e1.typerep, e2.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operands to % must be the same type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)] + else []; + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operands to % must be concrete types Integer or Float. Instead they are of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} +aspect production neg +top::Expr ::= '-' e1::Expr +{ + + top.errors <- + if performSubstitution(e1.typerep, top.finalSubst).instanceNum + then [] + else [err(top.location, "Operand to unary - must be concrete types Integer or Float. Instead it is of type " ++ prettyType(performSubstitution(e1.typerep, top.finalSubst)))]; +} + +aspect production terminalConstructor +top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, es, el, errCheck1, errCheck2, top; + + errCheck1 = check(es.typerep, stringType()); + errCheck2 = check(el.typerep, nonterminalType("silver:core:Location", [], false)); + top.errors <- + if errCheck1.typeerror + then [err(es.location, "Second operand to 'terminal(type,lexeme,location)' must be a String, instead it is " ++ errCheck1.leftpp)] + else []; + + top.errors <- + if errCheck2.typeerror + then [err(el.location, "Third operand to 'terminal(type,lexeme,location)' must be a Location, instead it is " ++ errCheck2.leftpp)] + else []; + + top.errors <- + if t.typerep.isTerminal || t.typerep.isError + then [] + else [err(t.location, "First operand to 'terminal(type,lexeme,location)' must be a Terminal type, instead it is " ++ prettyType(t.typerep))]; +} + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, inh, top; + + errCheck1 = checkDecorable(top.env, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Operand to decorate must be a nonterminal or partially decorated type. Instead it is of type " ++ errCheck1.leftpp)] + else []; +} + +aspect production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e1::Expr ';' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e1, errCheck1, top; + + errCheck1 = check(lhs.typerep, e1.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, lhs.name ++ " has expected type " ++ errCheck1.leftpp + ++ ", but the expression has type " ++ errCheck1.rightpp)] + else []; +} + +aspect production presentAppExpr +top::AppExpr ::= e::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(e.typerep, top.appExprTyperep); + top.errors <- + if !errCheck1.typeerror then [] + else [err(top.location, "Argument " ++ toString(top.appExprIndex+1) ++ " of function '" ++ + top.appExprApplied ++ "' expected " ++ errCheck1.rightpp ++ + " but argument is of type " ++ errCheck1.leftpp)]; +} + +-- See documentation for major restriction on use of exprRef. +-- Essentially, the referred expression MUST have already been type checked. diff --git a/grammars/silver/compiler/analysis/typechecking/core/FunctionDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/FunctionDcl.sv new file mode 100644 index 000000000..733bec81c --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/FunctionDcl.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + body.downSubst = emptySubst(); +} + +aspect production functionLHS +top::FunctionLHS ::= t::TypeExpr +{ + top.errors <- t.errorsKindStar; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/GlobalDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/GlobalDcl.sv new file mode 100644 index 000000000..86d5d8d45 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/GlobalDcl.sv @@ -0,0 +1,21 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production globalValueDclConcrete +top::AGDcl ::= 'global' id::Name '::' cl::ConstraintList '=>' t::TypeExpr '=' e::Expr ';' +{ + top.errors <- t.errorsKindStar; + + local attribute errCheck1 :: TypeCheck; + + e.downSubst = emptySubst(); + errCheck1.downSubst = e.upSubst; + + errCheck1.finalSubst = errCheck1.upSubst; + e.finalSubst = errCheck1.upSubst; + + errCheck1 = check(e.typerep, t.typerep); + top.errors <- if errCheck1.typeerror + then [err(top.location, "Declaration of global " ++ id.name ++ " with type " ++ errCheck1.rightpp ++ " has initialization expression with type " ++ errCheck1.leftpp)] + else []; +} + diff --git a/grammars/silver/compiler/analysis/typechecking/core/InstanceDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/InstanceDcl.sv new file mode 100644 index 000000000..81722e228 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/InstanceDcl.sv @@ -0,0 +1,28 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production instanceDcl +top::AGDcl ::= 'instance' cl::ConstraintList '=>' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + top.errors <- + if ty.typerep.kindrep == id.lookupType.typeScheme.typerep.kindrep then [] + else [err(ty.location, s"${ty.unparse} has kind ${prettyKind(ty.typerep.kindrep)}, but the class ${id.name} expected kind ${prettyKind(id.lookupType.typeScheme.typerep.kindrep)}")]; + + superContexts.contextLoc = id.location; + superContexts.contextSource = "instance superclasses"; + top.errors <- superContexts.contextErrors; +} + +aspect production instanceBodyItem +top::InstanceBodyItem ::= id::QName '=' e::Expr ';' +{ + local errCheck1::TypeCheck = check(typeScheme.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(e.location, s"Member ${id.name} has expected type ${errCheck1.leftpp}, but the expression has actual type ${errCheck1.rightpp}")] + else []; + + e.downSubst = instSubst; + errCheck1.downSubst = e.upSubst; + e.finalSubst = errCheck1.upSubst; + errCheck1.finalSubst = errCheck1.upSubst; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/ProductionBody.sv b/grammars/silver/compiler/analysis/typechecking/core/ProductionBody.sv new file mode 100644 index 000000000..3b84102fc --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/ProductionBody.sv @@ -0,0 +1,177 @@ +grammar silver:compiler:analysis:typechecking:core; + + +attribute upSubst, downSubst, finalSubst occurs on ProductionStmt, ForwardInhs, ForwardInh, ForwardLHSExpr; +propagate upSubst, downSubst on ProductionStmt, ForwardInhs, ForwardInh, ForwardLHSExpr + excluding productionStmtAppend, attachNoteStmt, forwardsTo, forwardInh, returnDef, synthesizedAttributeDef, inheritedAttributeDef, localValueDef; + +{-- + - These need an initial state only due to aspects (I think? maybe not. Investigate someday.) + - They otherwise confine their contexts to each individual Stmt. + -} +attribute downSubst occurs on ProductionBody, ProductionStmts; +-- downSubst is NOT propagated here - we give ever stmt the same downSubst, rather than threading like usual + +aspect production productionBody +top::ProductionBody ::= '{' stmts::ProductionStmts '}' +{ + stmts.downSubst = top.downSubst; +} + +aspect production productionStmtsNil +top::ProductionStmts ::= +{ +} + +aspect production productionStmtsSnoc +top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt +{ + h.downSubst = top.downSubst; + + t.downSubst = top.downSubst; + t.finalSubst = t.upSubst; +} + +aspect production productionStmtAppend +top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt +{ + -- We treat this as though each is independent here as well. + h.downSubst = top.downSubst; + h.finalSubst = h.upSubst; + + t.downSubst = top.downSubst; + t.finalSubst = t.upSubst; + + top.upSubst = error("Shouldn't ever be needed anywhere. (Should only ever be fed back here as top.finalSubst)"); + -- Of course, this means do not use top.finalSubst here! +} + +aspect production forwardsTo +top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(e.typerep, top.frame.signature.outputElement.typerep); + top.errors <- if errCheck1.typeerror + then [err(e.location, "Forward's expected type is " ++ errCheck1.rightpp ++ ", but the actual type supplied is " ++ errCheck1.leftpp)] + else []; +} + +aspect production forwardInh +top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, lhs, e, errCheck1, top; + + errCheck1 = check(lhs.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(e.location, lhs.name ++ " has expected type " ++ errCheck1.leftpp + ++ ", but the expression has type " ++ errCheck1.rightpp)] + else []; +} + +aspect production attachNoteStmt +top::ProductionStmt ::= 'attachNote' e::Expr ';' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(e.typerep, nonterminalType("silver:core:OriginNote", [], false)); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Origin note must have type silver:core:OriginNote, but the expression has actual type " ++ errCheck1.leftpp)] + else []; +} + +aspect production returnDef +top::ProductionStmt ::= 'return' e::Expr ';' +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(e.typerep, top.frame.signature.outputElement.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Expected return type is " ++ errCheck1.rightpp ++ ", but the expression has actual type " ++ errCheck1.leftpp)] + else []; +} + +aspect production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} + +aspect production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} + +aspect production errorAttributeDef +top::ProductionStmt ::= msg::[Message] dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + propagate downSubst, upSubst; +} + +aspect production childDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.errors <- if isDecorable(top.typerep, top.env) then [] + else [err(top.location, s"Inherited attributes can only be defined on (undecorated) nonterminal and partially decorated types, not ${prettyType(top.typerep)}.")]; +} + +aspect production localDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.errors <- if isDecorable(top.typerep, top.env) then [] + else [err(top.location, s"Inherited attributes can only be defined on (undecorated) nonterminal and partially decorated types, not ${prettyType(top.typerep)}.")]; +} + +aspect production localAttributeDcl +top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' +{ + top.errors <- te.errorsKindStar; +} + +aspect production productionAttributeDcl +top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr ';' +{ + top.errors <- te.errorsKindStar; +} + +aspect production localValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + errCheck1 = check(e.typerep, val.lookupValue.typeScheme.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Local " ++ val.name ++ " has type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] + else []; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/ProductionDcl.sv b/grammars/silver/compiler/analysis/typechecking/core/ProductionDcl.sv new file mode 100644 index 000000000..5bb2e657e --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/ProductionDcl.sv @@ -0,0 +1,28 @@ +grammar silver:compiler:analysis:typechecking:core; + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + body.downSubst = emptySubst(); +} + +aspect production productionLHS +top::ProductionLHS ::= id::Name '::' t::TypeExpr +{ + top.errors <- t.errorsKindStar; + + local checkNT::TypeCheck = checkNonterminal(top.env, false, t.typerep); + checkNT.downSubst = emptySubst(); + checkNT.finalSubst = emptySubst(); + + top.errors <- + if checkNT.typeerror + then [err(top.location, "Production LHS type must be a nonterminal. Instead it is of type " ++ checkNT.leftpp)] + else []; +} + +aspect production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + top.errors <- t.errorsKindStar; +} diff --git a/grammars/silver/compiler/analysis/typechecking/core/Project.sv b/grammars/silver/compiler/analysis/typechecking/core/Project.sv new file mode 100644 index 000000000..a2f3c6c78 --- /dev/null +++ b/grammars/silver/compiler/analysis/typechecking/core/Project.sv @@ -0,0 +1,34 @@ +grammar silver:compiler:analysis:typechecking:core; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +{-- The initial and resulting substitution contexts -} +threaded attribute downSubst, upSubst :: Substitution; + +{-- The complete, final substitution context -} +autocopy attribute finalSubst :: Substitution; + +-- We also use typerep. +-- Such that performSubstitution(e.typerep, e.upSubst) is the expression's real type (as of that moment) + +{- A NOTE ABOUT THREADING: + The pair of attributes (upSubst/downSubst) represent the state of unification. + downSubst tells a node its current state, and upSubst represents the final state. + finalSubst gives information from elsewhere in the tree. + + The scope of that threading is not universal. + That is, the threading BEGINS generally just above expressions, or individual + production STATEMENTS. Not Production bodies. + + There is a slight hack to this... because aspects need to do type unification + (actually, not sure if we need to preserve this, but let's assume we do!) + ProductionBody and ProductionStmts (plural, *NOT* ProductionStmt) + do not have an upSubst and finalSubst. Only downSubst. + These will take that initial state, but otherwise confine things to each Stmt. + -} + + diff --git a/grammars/silver/compiler/analysis/warnings/Project.sv b/grammars/silver/compiler/analysis/warnings/Project.sv new file mode 100644 index 000000000..f724b17d4 --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/Project.sv @@ -0,0 +1,20 @@ +grammar silver:compiler:analysis:warnings; + +imports silver:util:cmdargs; +imports silver:compiler:driver only parseArgs; + +abstract production warnAllFlag +top::CmdArgs ::= rest::CmdArgs +{ + -- This prod should be aspected to turn on all relevant warning flags + forwards to rest; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [flagSpec(name="--warn-all", paramString=nothing(), + help="enable all warnings", + flagParser=flag(warnAllFlag))]; +} + diff --git a/grammars/silver/compiler/analysis/warnings/exporting/Graph.sv b/grammars/silver/compiler/analysis/warnings/exporting/Graph.sv new file mode 100644 index 000000000..19cb711e5 --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/exporting/Graph.sv @@ -0,0 +1,70 @@ +grammar silver:compiler:analysis:warnings:exporting; + +import silver:compiler:driver; +import silver:util:cmdargs; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; + +-- This isn't exactly a warning, but it can live here for now... + +synthesized attribute dumpDepGraph :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= _ +{ + top.dumpDepGraph = false; +} +abstract production dumpDepGraphFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.dumpDepGraph = true; + forwards to rest; +} +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--dump-import-graph", paramString=nothing(), + help="dump a graph of dependencies between grammars as a Graphviz file", + flagParser=flag(dumpDepGraphFlag))]; + -- not omitting from descriptions deliberately! +} +aspect production compilation +top::Compilation ::= g::Grammars _ _ benv::BuildEnv +{ + top.postOps <- if top.config.dumpDepGraph then [dumpDepGraphAction(g.grammarList)] else []; +} + +abstract production dumpDepGraphAction +top::DriverAction ::= specs::[Decorated RootSpec] +{ + top.run = do { + eprintln("Generating import graph"); + writeFile("deps.dot", "digraph deps {\n" ++ generateDotGraph(specs) ++ "}"); + return 0; + }; + + top.order = 0; +} + +function generateDotGraph +String ::= specs::[Decorated RootSpec] +{ + return case specs of + | [] -> "" + | h::t -> + "\"" ++ h.declaredName ++ "\"[label=\"" ++ h.declaredName ++ "\"];\n" ++ + implode("", map(makeDotArrow(h.declaredName, _), h.moduleNames)) ++ + generateDotGraph(t) + end; +} + +function makeDotArrow +String ::= f::String t::String +{ + -- A heuristic to try to make the graph more readable... + return if t == "silver:core" then "" + else "\"" ++ f ++ "\" -> \"" ++ t ++ "\";\n"; +} + diff --git a/grammars/silver/compiler/analysis/warnings/flow/FlowTypeCopyEquation.sv b/grammars/silver/compiler/analysis/warnings/flow/FlowTypeCopyEquation.sv new file mode 100644 index 000000000..7e60d50dc --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/FlowTypeCopyEquation.sv @@ -0,0 +1,52 @@ +grammar silver:compiler:analysis:warnings:flow; + +-- Flow type check: the implicitly generated copy equations for synthesized +-- attributes due to forwarding may exceed their flow type. + +-- This can only occur with *host-language attributes* as extension +-- attribute are required to have ft(syn) > ft(fwd). + +-- The flow environment can give us the authoritative list of those attributes to check. +-- These may be from `options` and so requires the flowEnv. + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local transitiveDeps :: [FlowVertex] = expandGraph([forwardEqVertex()], findProductionGraph(fName, myGraphs)); + local fwdFlowDeps :: set:Set = onlyLhsInh(transitiveDeps); + + local lhsNt :: String = namedSig.outputElement.typerep.typeName; + local hostSyns :: [String] = getHostSynsFor(lhsNt, top.flowEnv); + + -- Possible refactoring: Consider moving this check from this production to forwarding equation? + + top.errors <- + if null(body.errors ++ ns.errors) + && top.config.warnMissingInh + -- Must be a forwarding production + && !null(body.uniqueSignificantExpression) + then flatMap(raiseImplicitFwdEqFlowTypes(top.config, top.location, lhsNt, fName, _, top.flowEnv, fwdFlowDeps, myFlow), hostSyns) + else []; +} + + +function raiseImplicitFwdEqFlowTypes +[Message] ::= config::Decorated CmdArgs l::Location lhsNt::String prod::String attr::String e::FlowEnv fwdFlowDeps::set:Set myFlow::EnvTree +{ + -- The flow type for `attr` on `lhsNt` + local depsForThisAttr :: set:Set = inhDepsForSyn(attr, lhsNt, myFlow); + -- Actual forwards equation deps not in the flow type for `attr` + local diff :: [String] = set:toList(set:difference(fwdFlowDeps, depsForThisAttr)); + + return case lookupSyn(prod, attr, e) of + | eq :: _ -> [] + | [] -> + if null(diff) then [] + else [mwdaWrn(config, l, s"In production ${prod}, the implicit copy equation for ${attr} (due to forwarding) would exceed the attribute's flow type because the production forward equation depends on ${implode(", ", diff)}")] + end; +} + diff --git a/grammars/silver/compiler/analysis/warnings/flow/Inh.sv b/grammars/silver/compiler/analysis/warnings/flow/Inh.sv new file mode 100644 index 000000000..9b429910f --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/Inh.sv @@ -0,0 +1,792 @@ +grammar silver:compiler:analysis:warnings:flow; + +import silver:compiler:modification:list; + +synthesized attribute warnMissingInh :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.warnMissingInh = false; +} +abstract production warnMissingInhFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnMissingInh = true; + forwards to rest; +} +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--warn-missing-inh", paramString=nothing(), + help="warn about any of several MWDA violations involving demanding inhs", + flagParser=flag(warnMissingInhFlag))]; +} + +-------------------------------------------------------------------------------- + +-- In this file: + +-- CHECK 1: Exceeds flowtype +-- - Examine overall dependencies of an equation, and see if they use LHS inh +-- that are not permissible, given the equation's flow type. +-- - Accomplished by explicit calculations in each production. + +-- CHECK 1b: Reference set exceeded checks +-- - Direct accesses from references need to be checked, they don't emit dependencies otherwise +-- - Attribute sections need special checking, too +-- - Pattern matching can create dependencies on references, too + +-- CHECK 2: Effective Completeness +-- - Ensure each inherited attribute that's used actually has an equation +-- in existence. +-- - Consists of calls to `checkAllEqDeps` +-- - Pattern variable accesses can induced *remote* inherited equation checks + +-------------------------------------------------------------------------------- + + +{-- + - This is a glorified lambda function, to help look for equations. + - Literally, we're just checking for null here. + - + - @param f The lookup function for the appropriate type of equation + - e.g. `lookupInh(prod, rhs, _, env)` + - @param attr The attribute to look up. + -} +function isEquationMissing +Boolean ::= f::([FlowDef] ::= String) attr::String +{ + return null(f(attr)); +} + +{-- + - False if 'attr' is an autocopy attribute, occurs on the LHS nonterminal, + - and child 'sigName' is a nonterminal (not a type var with an occurs-on context); + - true otherwise. Used in conjunction with 'filter' to get + - remove "missing equations" that are actually implicit autocopies. + -} +function ignoreIfAutoCopyOnLhs +Boolean ::= sigName::String ns::NamedSignature env::Decorated Env attr::String +{ + return !(isAutocopy(attr, env) && !null(getOccursDcl(attr, ns.outputElement.typerep.typeName, env)) && + -- Only ignore autocopies if the sig item is a nonterminal and not a type variable with an occurs-on context + findNamedSigElemType(sigName, ns.inputElements).isNonterminal); +} + +{-- + - Given a name of a child, return whether it has a fully decorated nonterminal + - type (covered by the more specific checks on accesses from references) or a + - partially decorated nonterminal type decorated with the attr. + - True if nonsensicle. + -} +function sigAttrViaReference +Boolean ::= sigName::String attrName::String ns::NamedSignature e::Decorated Env +{ + local ty :: Type = findNamedSigElemType(sigName, ns.inputElements); + return !isDecorable(ty, e) || contains(attrName, getMinRefSet(ty, e)); +} + +{-- + - Given a name of a local, return whether it has a fully decorated nonterminal + - type (covered by the more specific checks on accesses from references) or a + - partially decorated nonterminal type decorated with the attr. + - True if nonsensicle. + -} +function localAttrViaReference +Boolean ::= sigName::String attrName::String e::Decorated Env +{ + local d :: [ValueDclInfo] = getValueDcl(sigName, e); + local ty :: Type = head(d).typeScheme.typerep; + + return null(d) || !isDecorable(ty, e) || contains(attrName, getMinRefSet(ty, e)); +} + +{-- + - Used as a stop-gap measure to ensure equations exist. + - Given a needed equation (represented by FlowVertex 'v'), + - ensure such an equation exists, accounting for: + - 1. Defaults + - 2. Forwards + - 3. Autocopy + - 4. Reference accesses + - + - This gives rise to 'missing transitive dependency' errors. + - The reason this exists is to handle 'taking a reference' + - actions needing to ensure equations were actually provided for + - things we reference. + - + - @param v A value we need an equation for. + - @param config Command-line arguments, that affect error reporting + - @param l Where to report an error, if it's missing + - @param prodName The full name of the production we're in + - @param prodNt The nonterminal this production belongs to. (For functions, a dummy value is ok) + - @param flowEnv The local flow environment + - @param realEnv The local real environment + - @returns Errors for missing equations + -} +function checkEqDeps +[Message] ::= v::FlowVertex config::Decorated CmdArgs l::Location prodName::String flowEnv::FlowEnv realEnv::Decorated Env anonResolve::[Pair] +{ + -- We're concerned with missing inherited equations on RHS, LOCAL, and ANON. (Implicitly, FORWARD.) + + local prodDcl :: [ValueDclInfo] = getValueDcl(prodName, realEnv); + local ns :: NamedSignature = + case prodDcl of + | d :: _ -> d.namedSignature + | [] -> bogusNamedSignature() + end; + + return case v of + -- A dependency on an LHS.INH is a flow issue: these equations do not exist + -- locally, so we cannot check them. + | lhsInhVertex(_) -> [] + -- A dependency on an LHS.SYN can be checked locally, but we do not do so here. + -- All productions must have all SYN equations, so those errors are raised elsewhere. + | lhsSynVertex(attrName) -> [] + -- A dependency on an RHS.ATTR. SYN are always present, so we only care about INH here. + -- Filter missing equations for autocopy or for RHS that are references. + | rhsVertex(sigName, attrName) -> + if isInherited(attrName, realEnv) + then if !null(lookupInh(prodName, sigName, attrName, flowEnv)) + || !ignoreIfAutoCopyOnLhs(sigName, ns, realEnv, attrName) + || sigAttrViaReference(sigName, attrName, ns, realEnv) + then [] + else [mwdaWrn(config, l, "Equation has transitive dependency on child " ++ sigName ++ "'s inherited attribute for " ++ attrName ++ " but this equation appears to be missing.")] + else [] + -- A dependency on a LOCAL. Technically, local equations may not exist! + -- But let's just assume they do, since `local name :: type = expr;` is the prefered syntax. + | localEqVertex(fName) -> [] + -- A dependency on a LOCAL.ATTR. SYN always exist again, so we only care about INH here. + -- Ignore the FORWARD (a special case of LOCAL), which always has both SYN/INH. + -- And again ignore references. Autocopy isn't relevant to locals, though. + | localVertex(fName, attrName) -> + if isInherited(attrName, realEnv) + then if !null(lookupLocalInh(prodName, fName, attrName, flowEnv)) + || fName == "forward" + || localAttrViaReference(fName, attrName, realEnv) + then [] + else [mwdaWrn(config, l, "Equation has transitive dependency on local " ++ fName ++ "'s inherited attribute for " ++ attrName ++ " but this equation appears to be missing.")] + else [] + -- A dependency on a ANON. This do always exist (`decorate expr with..` always has expr.) + | anonEqVertex(fName) -> [] + -- A dependency on ANON.ATTR. Again, SYN are safe. We need to check only for INH. + -- If the equation is missing, then we again filter down to just those equations + -- missing within THIS overall equation. + -- i.e. `top.syn1 = ... missing ...; top.syn2 = top.syn1;` should only raise + -- the missing in the first equation. + | anonVertex(fName, attrName) -> + if isInherited(attrName, realEnv) + then if !null(lookupLocalInh(prodName, fName, attrName, flowEnv)) + then [] + else let + anonl :: Maybe = lookup(fName, anonResolve) + in if anonl.isJust + then [mwdaWrn(config, anonl.fromJust, "Decoration requires inherited attribute for " ++ attrName ++ ".")] + else [] -- If it's not in the list, then it's a transitive dep from a DIFFERENT equation (and thus reported there) + end + else [] + end; +} +function checkAllEqDeps +[Message] ::= v::[FlowVertex] config::Decorated CmdArgs l::Location prodName::String flowEnv::FlowEnv realEnv::Decorated Env anonResolve::[Pair] +{ + return flatMap(checkEqDeps(_, config, l, prodName, flowEnv, realEnv, anonResolve), v); +} + +{-- + - Look up flow types, either from the flow environment (for a nonterminal) or the occurs-on contexts (for a type var). + - @param syn A synthesized attribute's full name + - @param t The type to look up this attribute on + - @param flow The flow type environment (NOTE: TODO: this is currently 'myFlow' or something, NOT top.flowEnv) + - @param ns The named signature of the enclosing production + - @param env The regular (type) environment + - @return A set of inherited attributes (if the inh dependencies for the attribute are bounded) and a list of type variables of kind InhSet, + - needed to compute this synthesized attribute on this type. + -} +function inhDepsForSynOnType +(Maybe>, [TyVar]) ::= syn::String t::Type flow::EnvTree ns::NamedSignature env::Decorated Env +{ + local contexts::Contexts = foldContexts(ns.contexts); + contexts.env = env; + + return + if t.isNonterminal || (t.isDecorated && t.decoratedType.isNonterminal) + then (just(inhDepsForSyn(syn, t.typeName, flow)), []) + else ( + map(set:fromList, lookup(syn, lookupAll(t.typeName, contexts.occursContextInhDeps))), + concat(lookupAll(syn, lookupAll(t.typeName, contexts.occursContextInhSetDeps)))); +} + + +-------------------------------------------------------------------------------- + + +aspect production globalValueDclConcrete +top::AGDcl ::= 'global' id::Name '::' cl::ConstraintList '=>' t::TypeExpr '=' e::Expr ';' +{ + local transitiveDeps :: [FlowVertex] = expandGraph(e.flowDeps, e.frame.flowGraph); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, fName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +aspect production defaultConstraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr '=' e::Expr ';' +{ + local transitiveDeps :: [FlowVertex] = expandGraph(e.flowDeps, e.frame.flowGraph); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, fName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +aspect production instanceBodyItem +top::InstanceBodyItem ::= id::QName '=' e::Expr ';' +{ + local transitiveDeps :: [FlowVertex] = expandGraph(e.flowDeps, e.frame.flowGraph); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, id.lookupValue.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +aspect production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); + + top.errors <- + if dl.found && attr.found && top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ + if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} + +aspect production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + -- TODO: if LHS is forward, we have to check that we aren't exceeding flow type!! (BUG) + + -- check transitive deps only. Nothing to check for flow types + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +----- WARNING TODO BEGIN MASSIVE COPY & PASTE SESSION +aspect production synBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); + + top.errors <- + if dl.found && attr.found && top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ + if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} +aspect production synAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn(attr.attrDcl.fullName, top.frame.lhsNtName, myFlow))); + + top.errors <- + if dl.found && attr.found && top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ + if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Synthesized equation " ++ attr.name ++ " exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} +aspect production inhBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + -- check transitive deps only. Nothing to be done for flow types + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} +aspect production inhAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + -- check transitive deps only. Nothing to be done for flow types + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} +------ END AWFUL COPY & PASTE SESSION + +aspect production forwardsTo +top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, inhDepsForSyn("forward", top.frame.lhsNtName, myFlow))); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ + if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Forward equation exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} +aspect production forwardInh +top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + -- problem = lhsinh deps - fwd flow type - this inh attribute + local lhsInhExceedsFlowType :: [String] = + set:toList( + set:removeAll( + [case lhs of + | forwardLhsExpr(q) -> q.attrDcl.fullName + end], + set:difference( + lhsInhDeps, + inhDepsForSyn("forward", top.frame.lhsNtName, myFlow)))); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) ++ + if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Forward inherited equation exceeds flow type with dependencies on " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} + +aspect production localValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + -- check transitive deps only. No worries about flow types. + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +aspect production returnDef +top::ProductionStmt ::= 'return' e::Expr ';' +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +aspect production attachNoteStmt +top::ProductionStmt ::= 'attachNote' e::Expr ';' +{ + local transitiveDeps :: [FlowVertex] = + expandGraph(e.flowDeps, top.frame.flowGraph); + + top.errors <- + if top.config.warnMissingInh + then checkAllEqDeps(transitiveDeps, top.config, top.location, top.frame.fullName, top.flowEnv, top.env, collectAnonOrigin(e.flowDefs)) + else []; +} + +-- Skipping `baseCollectionValueDef`: it forwards to `localValueDef` +-- Partially skipping `appendCollectionValueDef`: it likewise forwards +-- But we do have a special "exceeds check" to do here: +aspect production appendCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + local productionFlowGraph :: ProductionGraph = top.frame.flowGraph; + local transitiveDeps :: [FlowVertex] = expandGraph(e.flowDeps, productionFlowGraph); + + local originalEqDeps :: [FlowVertex] = + expandGraph([localEqVertex(val.lookupValue.fullName)], productionFlowGraph); + + local lhsInhDeps :: set:Set = onlyLhsInh(transitiveDeps); + + local originalEqLhsInhDeps :: set:Set = onlyLhsInh(originalEqDeps); + + local lhsInhExceedsFlowType :: [String] = set:toList(set:difference(lhsInhDeps, originalEqLhsInhDeps)); + + top.errors <- + if top.config.warnMissingInh + -- We can ignore functions. We're checking LHS inhs here... functions don't have any! + && top.frame.hasFullSignature + then if null(lhsInhExceedsFlowType) then [] + else [mwdaWrn(top.config, top.location, "Local contribution (" ++ val.name ++ " <-) equation exceeds flow dependencies with: " ++ implode(", ", lhsInhExceedsFlowType))] + else []; +} + + +-------------------------------------------------------------------------------- + + +{- +Step 2: Let's go check on expressions. This has two purposes: +1. Better error messages for missing equations than the "transitive dependency" ones. + But technically, unneeded and transititve dependencies are covering this. +2. We have to ensure that each individual access from a reference fits within the inferred reference set. + Additionally we must check that wherever we take a reference, the required reference set is bounded. + This is not covered by any other checks. +-} + +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + if top.config.warnMissingInh + && isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if refSet.isJust then [] + else [mwdaWrn(top.config, top.location, s"Cannot take a reference of type ${prettyType(finalTy)}, as the reference set is not bounded.")] + else []; +} +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + if top.config.warnMissingInh + then if refSet.isJust then [] + else [mwdaWrn(top.config, top.location, s"Cannot take a reference of type ${prettyType(finalTy)}, as the reference set is not bounded.")] + else []; +} +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + if top.config.warnMissingInh + && isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if refSet.isJust then [] + else [mwdaWrn(top.config, top.location, s"Cannot take a reference of type ${prettyType(finalTy)}, as the reference set is not bounded.")] + else []; +} +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + if top.config.warnMissingInh + then if refSet.isJust then [] + else [mwdaWrn(top.config, top.location, s"Cannot take a reference of type ${prettyType(finalTy)}, as the reference set is not bounded.")] + else []; +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + -- TODO? +} + +aspect production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- oh no again + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + local finalTy :: Type = performSubstitution(e.typerep, e.upSubst); + local deps :: (Maybe>, [TyVar]) = + inhDepsForSynOnType(q.attrDcl.fullName, finalTy, myFlow, top.frame.signature, top.env); + local inhDeps :: set:Set = fromMaybe(set:empty(), deps.1); -- Need to check that we have bounded inh deps, i.e. deps.1 == just(...) + +-- This aspect is in two parts. First: we *must* check that any accesses +-- on a unknown decorated tree are in the ref-set. + local acceptable :: ([String], [TyVar]) = + case finalTy of + | decoratedType(_, i) -> getMinInhSetMembers([], i, top.env) + | _ -> ([], []) + end; + local diff :: [String] = + set:toList(set:removeAll(acceptable.1, -- blessed inhs for a reference + inhDeps)); -- needed inhs + + -- CASE 1: References. This check is necessary and won't be caught elsewhere. + top.errors <- + if null(e.errors) + && top.config.warnMissingInh + then + case e.flowVertexInfo of + -- We don't track dependencies on inh sets transitively, so we need to check that the inh deps are bounded here; + -- an access with unbounded inh deps only ever makes sense on a reference. + | hasVertex(_) -> + if deps.1.isJust then [] + else [mwdaWrn(top.config, top.location, "Access of " ++ q.name ++ " from " ++ prettyType(finalTy) ++ " requires an unbounded set of inherited attributes")] + -- without a vertex, we're accessing from a reference, and so... + | noVertex() -> + if any(map(contains(_, deps.2), acceptable.2)) then [] -- The deps are supplied as a common InhSet var + -- We didn't find the deps as an InhSet var + else if null(diff) + then if deps.fst.isJust then [] -- We have a bound on the inh deps, and they are all present + -- We don't have a bound on the inh deps, flag the unsatisfied InhSet deps + else if null(acceptable.2) + then [mwdaWrn(top.config, top.location, "Access of " ++ q.name ++ " from " ++ prettyType(finalTy) ++ " requires an unbounded set of inherited attributes")] + else [mwdaWrn(top.config, top.location, "Access of " ++ q.name ++ " from reference of type " ++ prettyType(finalTy) ++ " requires one of the following sets of inherited attributes not known to be supplied to this reference: " ++ implode(", ", map(findAbbrevFor(_, top.frame.signature.freeVariables), deps.snd)))] + -- We didn't find the inh deps + else [mwdaWrn(top.config, top.location, "Access of " ++ q.name ++ " from reference of type " ++ prettyType(finalTy) ++ " requires inherited attributes not known to be supplied to this reference: " ++ implode(", ", diff))] + end + else []; + +---------------- + + -- CASE 2: More specific errors for things already caught by `checkAllEqDeps`. + -- Equation has transition dep on `i`, but here we can say where this dependency + -- originated: from an syn acces. + top.errors <- + if null(e.errors) && top.config.warnMissingInh + then + case e of + | childReference(lq) -> + if isDecorable(lq.lookupValue.typeScheme.typerep, top.env) + then + let inhs :: [String] = + -- N.B. we're filtering out autocopies here + filter( + ignoreIfAutoCopyOnLhs(lq.name, top.frame.signature, top.env, _), + filter( + isEquationMissing( + lookupInh(top.frame.fullName, lq.lookupValue.fullName, _, top.flowEnv), + _), + removeAll(getMinRefSet(lq.lookupValue.typeScheme.typerep, top.env), + set:toList(inhDeps)))) + in if null(inhs) then [] + else [mwdaWrn(top.config, top.location, "Access of syn attribute " ++ q.name ++ " on " ++ e.unparse ++ " requires missing inherited attributes " ++ implode(", ", inhs) ++ " to be supplied")] + end + else [] + | localReference(lq) -> + if isDecorable(lq.lookupValue.typeScheme.typerep, top.env) + then + let inhs :: [String] = + filter( + isEquationMissing( + lookupLocalInh(top.frame.fullName, lq.lookupValue.fullName, _, top.flowEnv), + _), + removeAll(getMinRefSet(lq.lookupValue.typeScheme.typerep, top.env), + set:toList(inhDeps))) + in if null(inhs) then [] + else [mwdaWrn(top.config, top.location, "Access of syn attribute " ++ q.name ++ " on " ++ e.unparse ++ " requires missing inherited attributes " ++ implode(", ", inhs) ++ " to be supplied")] + end + else [] + | _ -> [] + end + else []; +} + +aspect production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- In this case, ONLY check for references. + -- The transitive deps error will be less difficult to figure out when there's + -- an explicit access to the attributes. + local finalTy::Type = performSubstitution(e.typerep, e.upSubst); + top.errors <- + if null(e.errors) && top.config.warnMissingInh + then + case e.flowVertexInfo of + | hasVertex(_) -> [] -- no check to make, as it was done transitively + -- without a vertex, we're accessing from a reference, and so... + | noVertex() -> + if contains(q.attrDcl.fullName, getMinRefSet(finalTy, top.env)) + then [] + else [mwdaWrn(top.config, top.location, "Access of inherited attribute " ++ q.name ++ " on reference of type " ++ prettyType(finalTy) ++ " is not permitted")] + end + else []; +} + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + -- Do nothing. Everything gets taken care of with anonResolve and checkEqDeps at the top-level of the equation +} + +{-- + - For pattern matching, we have an obligation to check: + - 1. If we invented an anon vertex type for the scrutinee, then it's a sink, and + - we need to check that nothing more than the ref set was depended upon. + -} +aspect production matchPrimitiveReal +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + -- slightly awkward way to recover the name and whether/not it was invented + local sinkVertexName :: Maybe = + case e.flowVertexInfo, pr.scrutineeVertexType of + | noVertex(), anonVertexType(n) -> just(n) + | _, _ -> nothing() + end; + + -- These should be the only ones that can reference our anon sink + local transitiveDeps :: [FlowVertex] = + expandGraph(top.flowDeps, top.frame.flowGraph); + + pr.receivedDeps = transitiveDeps; + + -- just the deps on inhs of our sink + local inhDeps :: [String] = + toAnonInhs(transitiveDeps, sinkVertexName.fromJust, top.env); + + -- Subtract the ref set from our deps + local diff :: [String] = + set:toList(set:removeAll(getMinRefSet(scrutineeType, top.env), set:add(inhDeps, set:empty()))); + + top.errors <- + if null(e.errors) + && top.config.warnMissingInh + && sinkVertexName.isJust + && !null(diff) + then [mwdaWrn(top.config, e.location, "Pattern match on reference of type " ++ prettyType(scrutineeType) ++ " has transitive dependencies on " ++ implode(", ", diff))] + else []; + +} + +function toAnonInhs +[String] ::= v::[FlowVertex] vertex::String env::Decorated Env +{ + return + case v of + | anonVertex(n, inh) :: tl -> + if vertex == n && isInherited(inh, env) + then inh :: toAnonInhs(tl, vertex, env) + else toAnonInhs(tl, vertex, env) + | _ :: tl -> toAnonInhs(tl, vertex, env) + | [] -> [] + end; +} + +inherited attribute receivedDeps :: [FlowVertex] occurs on VarBinders, VarBinder, PrimPatterns, PrimPattern; +propagate receivedDeps on VarBinders, VarBinder, PrimPatterns, PrimPattern; + +aspect production varVarBinder +top::VarBinder ::= n::Name +{ + -- Check that we're not taking an unbounded reference + top.errors <- + if top.config.warnMissingInh + && isDecorable(top.bindingType, top.env) + then if refSet.isJust then [] + else [mwdaWrn(top.config, top.location, s"Cannot take a reference of type ${prettyType(finalTy)}, as the reference set is not bounded.")] + else []; + + -- fName is our invented vertex name for the pattern variable + local requiredInhs :: [String] = + toAnonInhs(top.receivedDeps, fName, top.env); + + -- Check for equation's existence: + -- Prod: top.matchingAgainst.fromJust.fullName + -- Child: top.bindingName + -- Inh: each of requiredInhs + local missingInhs :: [String] = + filter(remoteProdMissingEq(top.matchingAgainst.fromJust, top.bindingName, _, top.env, top.flowEnv), + removeAll(getMinRefSet(top.bindingType, top.env), requiredInhs)); + + top.errors <- + if top.config.warnMissingInh + && isDecorable(top.bindingType, top.env) + && top.matchingAgainst.isJust + && !null(missingInhs) + then [mwdaWrn(top.config, top.location, s"Pattern variable '${n.name}' has transitive dependencies with missing remote equations.\n\tRemote production: ${top.matchingAgainst.fromJust.fullName}\n\tChild: ${top.bindingName}\n\tMissing inherited equations for: ${implode(", ", missingInhs)}")] + else []; +} + +function remoteProdMissingEq +Boolean ::= prod::ValueDclInfo sigName::String attrName::String realEnv::Decorated Env flowEnv::FlowEnv +{ + return + null(lookupInh(prod.fullName, sigName, attrName, flowEnv)) && -- no equation + ignoreIfAutoCopyOnLhs(sigName, prod.namedSignature, realEnv, attrName); -- not autocopy (and on lhs) +} + +-- In places where we solve a synthesized attribute occurs-on context, +-- check that the actual deps for the attribute do not exceed the one specified for the context. +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + -- oh no again + production myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + + -- The logic here mirrors the reference case in synDecoratedAccessHandler + local deps :: (Maybe>, [TyVar]) = + inhDepsForSynOnType(attr, ntty, myFlow, top.frame.signature, top.env); + local inhDeps :: set:Set = fromMaybe(set:empty(), deps.1); -- Need to check that we have bounded inh deps, i.e. deps.1 == just(...) + + local acceptable :: ([String], [TyVar]) = getMinInhSetMembers([], inhs, top.env); + local diff :: [String] = set:toList(set:removeAll(acceptable.1, inhDeps)); + + top.contextErrors <- + if top.config.warnMissingInh + && null(ntty.freeFlexibleVars) && null(inhs.freeFlexibleVars) + && !null(top.resolvedOccurs) + then + if any(map(contains(_, deps.2), acceptable.2)) then [] -- The deps are supplied as a common InhSet var + -- We didn't find the deps as an InhSet var + else if null(diff) + then if deps.1.isJust then [] -- We have a bound on the inh deps, and they are all present + -- We don't have a bound on the inh deps, flag the unsatisfied InhSet deps + else if null(acceptable.2) + then [mwdaWrn(top.config, top.contextLoc, s"The instance for ${prettyContext(top)} (arising from ${top.contextSource}) depends on an unbounded set of inherited attributes")] + else [mwdaWrn(top.config, top.contextLoc, s"The instance for ${prettyContext(top)} (arising from ${top.contextSource}) exceeds the flow type constraint with dependencies on one of the following sets of inherited attributes: " ++ implode(", ", map(findAbbrevFor(_, top.frame.signature.freeVariables), deps.2)))] + -- We didn't find the inh deps + else [mwdaWrn(top.config, top.contextLoc, s"The instance for ${prettyContext(top)} (arising from ${top.contextSource}) has a flow type exceeding the constraint with dependencies on " ++ implode(", ", diff))] + else []; +} + +-------------------------------------------------------------------------------- + +-- TODO: There are a few final places where we need to `checkEqDeps` for the sake of `anonVertex`s + +-- action blocks (production, terminal, disam, etc) + +-- But we don't create flowEnv information for these locations so they can't be checked... oops +-- (e.g. `checkEqDeps` wants a production fName to look things up about.) + + diff --git a/grammars/silver/compiler/analysis/warnings/flow/MWDA.sv b/grammars/silver/compiler/analysis/warnings/flow/MWDA.sv new file mode 100644 index 000000000..69403195a --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/MWDA.sv @@ -0,0 +1,80 @@ +grammar silver:compiler:analysis:warnings:flow; + +-- data structures +imports silver:util:treeset as set; + +-- driver stuff +imports silver:util:cmdargs; +imports silver:compiler:driver only parseArgs; +imports silver:compiler:driver:util only isExportedBy; +imports silver:compiler:analysis:warnings; + +-- silver +imports silver:compiler:definition:core; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:definition:env; + +-- flow analysis +imports silver:compiler:definition:flow:ast; +imports silver:compiler:definition:flow:driver only ProductionGraph, FlowType, prod, inhDepsForSyn, findProductionGraph, expandGraph, onlyLhsInh; + +-- the modifications we need to be aware of +imports silver:compiler:modification:autocopyattr only isAutocopy; +imports silver:compiler:modification:collection; +imports silver:compiler:modification:defaultattr; +imports silver:compiler:modification:primitivepattern; +imports silver:compiler:modification:copper only parserAttributeDefLHS; + +function isOccursSynthesized +Boolean ::= occs::OccursDclInfo e::Decorated Env +{ + return case getAttrDcl(occs.attrOccurring, e) of + | at :: _ -> at.isSynthesized + | _ -> false + end; +} + +-- TODO: this should probably not be a thing I have to write here +function isAutocopy +Boolean ::= attr::String e::Decorated Env +{ + return case getAttrDclAll(attr, e) of + | at :: _ -> at.isAutocopy + | _ -> false + end; +} +-- TODO: why is this a thing I have to write here. Sheesh. FIX THIS. +-- The real fix is for our vertexes to remember whether they are syn/inh. +function isInherited +Boolean ::= a::String e::Decorated Env +{ + return case getAttrDclAll(a, e) of + | at :: _ -> at.isInherited + | _ -> false + end; +} + +function isLhsInh +Boolean ::= v::FlowVertex +{ + return case v of + | lhsInhVertex(_) -> true + | _ -> false + end; +} + + + +-- TODO: better way of generating warnings. We ad-hoc check for errors before +-- raising these warnings, but this is inherently fragile and results in crash +-- bugs when running the MWDA on erroneous grammars. +-- (easily fixed by running regular build first, but still.) +-- (Possible solution approach: raise these with a different attribute than +-- `errors`, but we'd probably want "monoid attributes" to make that ergonomic.) + +-- TODO: are we ever checking the flow types for default equations? +-- These shouldn't need checking as part of inference, but default equations can +-- exceed *explicit* flow types, and I don't think anything is checking that yet. + + diff --git a/grammars/silver/analysis/warnings/flow/MissingSynEq.sv b/grammars/silver/compiler/analysis/warnings/flow/MissingSynEq.sv similarity index 79% rename from grammars/silver/analysis/warnings/flow/MissingSynEq.sv rename to grammars/silver/compiler/analysis/warnings/flow/MissingSynEq.sv index 93ad2c14c..e1780ad8c 100644 --- a/grammars/silver/analysis/warnings/flow/MissingSynEq.sv +++ b/grammars/silver/compiler/analysis/warnings/flow/MissingSynEq.sv @@ -1,4 +1,4 @@ -grammar silver:analysis:warnings:flow; +grammar silver:compiler:analysis:warnings:flow; synthesized attribute warnMissingSyn :: Boolean occurs on CmdArgs; @@ -16,7 +16,10 @@ top::CmdArgs ::= rest::CmdArgs aspect function parseArgs Either ::= args::[String] { - flags <- [pair("--warn-missing-syn", flag(warnMissingSynFlag))]; + flags <- [ + flagSpec(name="--warn-missing-syn", paramString=nothing(), + help="warn about missing synthesized equations", + flagParser=flag(warnMissingSynFlag))]; } {- This is the primary check for missing synthesized equations. @@ -26,7 +29,7 @@ aspect production attributionDcl top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' { -- All non-forwarding productions for this nonterminal: - local nfprods :: [String] = getNonforwardingProds(nt.lookupType.typerep.typeName, top.flowEnv); + local nfprods :: [String] = getNonforwardingProds(nt.lookupType.typeScheme.typeName, top.flowEnv); -- The check we're writing in this aspect can find all instances of missing -- synthesized equations, but in the interest of improved error messages, we @@ -38,7 +41,7 @@ top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' n top.errors <- if nt.lookupType.found && at.lookupAttribute.found - && (top.config.warnAll || top.config.warnMissingSyn) + && top.config.warnMissingSyn -- We only care about synthesized attributes: && at.lookupAttribute.dcl.isSynthesized -- This error message won't be redundant: @@ -46,7 +49,7 @@ top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' n -- And we can ignore any attribute that has a default equation: && null(lookupDef(nt.lookupType.fullName, at.lookupAttribute.fullName, top.flowEnv)) -- Otherwise, examine them all: - then flatMap(raiseMissingProds(top.location, at.lookupAttribute.fullName, _, top.flowEnv), nfprods) + then flatMap(raiseMissingProds(top.config, top.location, at.lookupAttribute.fullName, _, top.flowEnv), nfprods) else []; } @@ -61,14 +64,14 @@ top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' n - @returns An error message from the attribute occurrence's perspective, if any -} function raiseMissingProds -[Message] ::= l::Location attr::String prod::String e::Decorated FlowEnv +[Message] ::= config::Decorated CmdArgs l::Location attr::String prod::String e::FlowEnv { -- Because the location is of the attribute occurrence, deliberately use the attribute's shortname local shortName :: String = substring(lastIndexOf(":", attr) + 1, length(attr), attr); return case lookupSyn(prod, attr, e) of | _ :: _ -> [] -- equation exists - | [] -> [wrn(l, "attribute " ++ shortName ++ " missing equation for production " ++ prod)] + | [] -> [mwdaWrn(config, l, "attribute " ++ shortName ++ " missing equation for production " ++ prod)] end; } @@ -83,17 +86,17 @@ top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::Pr { -- All locally known synthesized attributes. This does not need to be exhaustive, -- because this error message is a courtesy, not the basis of the analysis. - local attrs :: [DclInfo] = + local attrs :: [OccursDclInfo] = filter(isOccursSynthesized(_, top.env), getAttrsOn(namedSig.outputElement.typerep.typeName, top.env)); top.errors <- if null(body.errors ++ ns.errors) - && (top.config.warnAll || top.config.warnMissingSyn) + && top.config.warnMissingSyn -- Forwarding productions do no have missing synthesized equations: && null(body.uniqueSignificantExpression) -- Otherwise, examine them all: - then flatMap(raiseMissingAttrs(top.location, fName, _, top.flowEnv), attrs) + then flatMap(raiseMissingAttrs(top.config, top.location, fName, _, top.flowEnv), attrs) else []; } @@ -102,13 +105,13 @@ top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::Pr - for the synthesized attribute `attr`. - - @param l Location to raise the error message (of the production) - - @param attr Full name of a synthesized attribute - - @param prod DclInfo for the non-forwarding production in question + - @param attr DclInfo of a synthesized attribute occurrence + - @param prod Full name of the non-forwarding production in question - @param e The local flow environment - @returns An error message from the production's perspective, if any -} function raiseMissingAttrs -[Message] ::= l::Location prod::String attr::DclInfo e::Decorated FlowEnv +[Message] ::= config::Decorated CmdArgs l::Location prod::String attr::OccursDclInfo e::FlowEnv { -- Because the location is of the production, deliberately use the production's shortname local shortName :: String = substring(lastIndexOf(":", prod) + 1, length(prod), prod); @@ -123,7 +126,7 @@ function raiseMissingAttrs end; return if lacks_default_equation && missing_explicit_equation - then [wrn(l, "production " ++ shortName ++ " lacks synthesized equation for " ++ attr.attrOccurring)] + then [mwdaWrn(config, l, "production " ++ shortName ++ " lacks synthesized equation for " ++ attr.attrOccurring)] else []; } diff --git a/grammars/silver/compiler/analysis/warnings/flow/MwdaFlag.sv b/grammars/silver/compiler/analysis/warnings/flow/MwdaFlag.sv new file mode 100644 index 000000000..d52a03ead --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/MwdaFlag.sv @@ -0,0 +1,49 @@ +grammar silver:compiler:analysis:warnings:flow; + +synthesized attribute errorMwda :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.errorMwda = false; +} +abstract production mwdaFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.errorMwda = true; + top.warnMissingInh = true; + top.warnMissingSyn = true; + top.warnEqdef = true; + top.warnOrphaned = true; + top.warnFwd = true; + forwards to rest; +} + +-- Also add these under --warn-all +aspect production warnAllFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnMissingInh = true; + top.warnMissingSyn = true; + top.warnEqdef = true; + top.warnOrphaned = true; + top.warnFwd = true; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--mwda", paramString=nothing(), + help="report MWDA violations as errors", + flagParser=flag(mwdaFlag))]; +} + +abstract production mwdaWrn +top::Message ::= config::Decorated CmdArgs l::Location m::String +{ + forwards to + if config.errorMwda + then err(l, m) + else wrn(l, m); +} diff --git a/grammars/silver/compiler/analysis/warnings/flow/OrphanedEquation.sv b/grammars/silver/compiler/analysis/warnings/flow/OrphanedEquation.sv new file mode 100644 index 000000000..ad58127b2 --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/OrphanedEquation.sv @@ -0,0 +1,280 @@ +grammar silver:compiler:analysis:warnings:flow; + +import silver:compiler:modification:let_fix only lexicalLocalReference; + +synthesized attribute warnEqdef :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.warnEqdef = false; +} +abstract production warnEqdefFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnEqdef = true; + forwards to rest; +} +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--warn-eqdef", paramString=nothing(), + help="warn about orphaned equations", + flagParser=flag(warnEqdefFlag))]; +} + +aspect production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local exportedBy :: [String] = + if top.frame.hasPartialSignature + then [top.frame.sourceGrammar, attr.dcl.sourceGrammar] + else [attr.dcl.sourceGrammar]; -- defaults can only be listed together with occurs. + + -- Orphaned equation check + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned equation: " ++ attr.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] + else []; + + -- Duplicate equation check + top.errors <- + if length(dl.lookupEqDefLHS) > 1 + then [mwdaWrn(top.config, top.location, "Duplicate equation for " ++ attr.name ++ " in production " ++ top.frame.fullName)] + else []; +} + +aspect production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local exportedBy :: [String] = + case dl of + -- Exported by the declaration of the thing we're giving inh to, or to the occurs + | localDefLHS(q) -> [q.lookupValue.dcl.sourceGrammar, attr.dcl.sourceGrammar] + -- For rhs or forwards, that's the production. + | _ -> [top.frame.sourceGrammar, attr.dcl.sourceGrammar] + end; + + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned equation: " ++ attr.name ++ " on " ++ dl.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] + -- Now, check for duplicate equations! + else []; + + top.errors <- + if length(dl.lookupEqDefLHS) > 1 || contains(dl.defLHSattr.attrDcl.fullName, getMinRefSet(dl.typerep, top.env)) + then [mwdaWrn(top.config, top.location, "Duplicate equation for " ++ attr.name ++ " on " ++ dl.name ++ " in production " ++ top.frame.fullName)] + else []; + + -- Check that if there is a partially decorated reference taken to this decoration site, + -- we aren't defining an equation that isn't in that reference type (Decorated Foo with only {...}). + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + then flatMap( + \ refSite::(String, Location, [String]) -> + if contains(attr.attrDcl.fullName, refSite.3) then [] + else [mwdaWrn(top.config, top.location, "Attribute " ++ attr.name ++ " with an equation on " ++ dl.name ++ " is not in the partially decorated reference taken at " ++ refSite.1 ++ ":" ++ refSite.2.unparse ++ " with only " ++ implode(", ", refSite.3))], + case dl of + | childDefLHS(q) -> getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv) + | localDefLHS(q) -> getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv) + | _ -> [] + end) + else []; +} + + +--- FROM COLLECTIONS + +aspect production synBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local exportedBy :: [String] = + if top.frame.hasPartialSignature + then [top.frame.sourceGrammar, attr.dcl.sourceGrammar] + else [attr.dcl.sourceGrammar]; -- defaults can only be listed together with occurs. + + -- Orphaned equation check + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned equation: " ++ attr.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] + else []; + + -- Duplicate equation check + top.errors <- + if length(dl.lookupEqDefLHS) > 1 + then [mwdaWrn(top.config, top.location, "Duplicate equation for " ++ attr.name ++ " in production " ++ top.frame.fullName)] + else []; +} +aspect production inhBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local exportedBy :: [String] = + case dl of + -- Exported by the declaration of the thing we're giving inh to, or to the occurs + | localDefLHS(q) -> [q.lookupValue.dcl.sourceGrammar, attr.dcl.sourceGrammar] + -- For rhs or forwards, that's the production. + | _ -> [top.frame.sourceGrammar, attr.dcl.sourceGrammar] + end; + + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + && !isExportedBy(top.grammarName, exportedBy, top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned equation: " ++ attr.name ++ " on " ++ dl.name ++ " (occurs from " ++ attr.dcl.sourceGrammar ++ ") in production " ++ top.frame.fullName)] + -- Now, check for duplicate equations! + else []; + + top.errors <- + if length(dl.lookupEqDefLHS) > 1 || contains(dl.defLHSattr.attrDcl.fullName, getMinRefSet(dl.typerep, top.env)) + then [mwdaWrn(top.config, top.location, "Duplicate equation for " ++ attr.name ++ " on " ++ dl.name ++ " in production " ++ top.frame.fullName)] + else []; + + -- Check that if there is a partially decorated reference taken to this decoration site, + -- we aren't defining an equation that isn't in that reference type (Decorated Foo with only {...}). + top.errors <- + if dl.found && attr.found + && top.config.warnEqdef + then flatMap( + \ refSite::(String, Location, [String]) -> + if contains(attr.attrDcl.fullName, refSite.3) then [] + else [mwdaWrn(top.config, top.location, "Attribute " ++ attr.name ++ " with an equation for " ++ dl.name ++ " is not in the partially decorated reference taken at " ++ refSite.1 ++ ":" ++ refSite.2.unparse ++ " with only " ++ implode(", ", refSite.3))], + case dl of + | childDefLHS(q) -> getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv) + | localDefLHS(q) -> getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv) + | _ -> [] + end) + else []; +} + +aspect production exprLhsExpr +top::ExprLHSExpr ::= attr::QNameAttrOccur +{ + -- Duplicate equation check + top.errors <- + if attr.attrFound && length(filter(eq(attr.attrDcl.fullName, _), top.allSuppliedInhs)) > 1 + then [mwdaWrn(top.config, top.location, "Duplicate equation for " ++ attr.name)] + else []; +} + +-- These checks live here for now, since they are related to duplicate equations: +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + local partialRefs::[(String, Location, [String])] = getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv); + top.errors <- + case finalTy, refSet of + | partiallyDecoratedType(_, _), just(inhs) when top.config.warnEqdef && q.lookupValue.found -> + case getMaxRefSet(q.lookupValue.typeScheme.typerep, top.env) of + | just(origInhs) -> + if all(map(contains(_, inhs), origInhs)) then [] + else [mwdaWrn(top.config, top.location, s"Partially decorated reference of type ${prettyType(finalTy)} does not contain all attributes in the reference set of ${q.name}'s type ${prettyType(q.lookupValue.typeScheme.monoType)}")] + | nothing() -> [mwdaWrn(top.config, top.location, s"Cannot take a partially decorated reference to ${q.name} of type ${prettyType(q.lookupValue.typeScheme.monoType)}, as the reference set is not bounded")] + end ++ + -- Check that we are exported by the decoration site. + if q.lookupValue.found && top.config.warnEqdef + && !isExportedBy(top.grammarName, [q.lookupValue.dcl.sourceGrammar], top.compiledGrammars) + then [mwdaWrn(top.config, top.location, s"Orphaned partially decorated reference to ${q.lookupValue.fullName} in production ${top.frame.fullName} (reference has type ${prettyType(finalTy)}).")] + -- Check that there is at most one partial reference taken to this decoration site. + -- TODO: This check isn't actually sufficent for well-definedness (e.g. wrapping this ref in + -- a term and decorating that more than once), need some sort of "linearity analysis". + else if length(partialRefs) > 1 + then [mwdaWrn(top.config, top.location, s"Multiple partially decorated references taken to ${q.name} in production ${top.frame.fullName} (reference has type ${prettyType(finalTy)}).")] + else [] + | _, _ -> [] + end; +} +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + local partialRefs::[(String, Location, [String])] = getPartialRefs(top.frame.fullName, q.lookupValue.fullName, top.flowEnv); + top.errors <- + case finalTy, refSet of + | partiallyDecoratedType(_, _), just(inhs) when top.config.warnEqdef && q.lookupValue.found -> + case getMaxRefSet(q.lookupValue.typeScheme.typerep, top.env) of + | just(origInhs) -> + if all(map(contains(_, inhs), origInhs)) then [] + else [mwdaWrn(top.config, top.location, s"Partially decorated reference of type ${prettyType(finalTy)} does not contain all attributes in the reference set of ${q.name}'s type ${prettyType(q.lookupValue.typeScheme.monoType)}")] + | nothing() -> [mwdaWrn(top.config, top.location, s"Cannot take a partially decorated reference to ${q.name} of type ${prettyType(q.lookupValue.typeScheme.monoType)}, as the reference set is not bounded")] + end ++ + -- Check that we are exported by the decoration site/ + if q.lookupValue.found && top.config.warnEqdef + && !isExportedBy(top.grammarName, [q.lookupValue.dcl.sourceGrammar], top.compiledGrammars) + then [mwdaWrn(top.config, top.location, s"Orphaned partially decorated reference to ${q.lookupValue.fullName} in production ${top.frame.fullName} (reference has type ${prettyType(finalTy)}).")] + -- Check that there is at most one partial reference taken to this decoration site. + -- TODO: This check isn't actually sufficent for well-definedness (e.g. wrapping this ref in + -- a term and decorating that more than once), need some sort of "linearity analysis". + else if length(partialRefs) > 1 + then [mwdaWrn(top.config, top.location, s"Multiple partially decorated references taken to ${q.name} in production ${top.frame.fullName} (reference has type ${prettyType(finalTy)}).")] + else [] + | _, _ -> [] + end; +} +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + case finalTy of + | partiallyDecoratedType(_, _) when top.config.warnEqdef -> + [mwdaWrn(top.config, top.location, s"Cannot take a partially decorated reference of type ${prettyType(finalTy)} to ${q.name}.")] + | _ -> [] + end; +} +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + case finalTy of + | partiallyDecoratedType(_, _) when top.config.warnEqdef -> + [mwdaWrn(top.config, top.location, s"Cannot take a partially decorated reference of type ${prettyType(finalTy)} to the forward tree.")] + | _ -> [] + end; +} +aspect production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName fi::ExprVertexInfo fd::[FlowVertex] +{ + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + top.errors <- + case finalTy, q.lookupValue.typeScheme.monoType of + | partiallyDecoratedType(_, _), partiallyDecoratedType(_, _) -> [] -- TODO: Need linearity analysis... + | partiallyDecoratedType(_, _), _ when top.config.warnEqdef -> + [mwdaWrn(top.config, top.location, s"${q.name} was not bound as a partially decorated reference, but here it is used with type ${prettyType(finalTy)}.")] + | _, _ -> [] + end; +} + + +--- For our DefLHSs: + +{-- + - A lookup for other instances of this equation on this DefLHS. + -} +synthesized attribute lookupEqDefLHS :: [FlowDef] occurs on DefLHS; +flowtype lookupEqDefLHS {decorate} on DefLHS; + +aspect lookupEqDefLHS on top::DefLHS of + -- prod, child, attr +| childDefLHS(q) -> lookupInh(top.frame.fullName, q.lookupValue.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv) + -- prod, attr +| lhsDefLHS(q) -> lookupSyn(top.frame.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv) + -- prod, local, attr +| localDefLHS(q) -> lookupLocalInh(top.frame.fullName, q.lookupValue.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv) + -- prod, attr +| forwardDefLHS(q) -> lookupFwdInh(top.frame.fullName, top.defLHSattr.attrDcl.fullName, top.flowEnv) + -- nt, attr +| defaultLhsDefLHS(q) -> lookupDef(top.frame.lhsNtName, top.defLHSattr.attrDcl.fullName, top.flowEnv) + +| errorDefLHS(q) -> [] +| parserAttributeDefLHS(q) -> [] -- TODO: maybe error? +end; diff --git a/grammars/silver/compiler/analysis/warnings/flow/OrphanedOccurs.sv b/grammars/silver/compiler/analysis/warnings/flow/OrphanedOccurs.sv new file mode 100644 index 000000000..9bd5d912b --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/OrphanedOccurs.sv @@ -0,0 +1,50 @@ +grammar silver:compiler:analysis:warnings:flow; + +synthesized attribute warnOrphaned :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.warnOrphaned = false; +} +abstract production warnOrphanedFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnOrphaned = true; + forwards to rest; +} +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--warn-orphaned", paramString=nothing(), + help="warn about orphaned attribute occurrences", + flagParser=flag(warnOrphanedFlag))]; +} + +aspect production attributionDcl +top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' +{ + local isClosedNt :: Boolean = + case nt.lookupType.dcls of + | ntDcl(_, _, closed, _) :: _ -> closed + | _ -> false -- default, if the lookup fails + end; + + top.errors <- + if nt.lookupType.found && at.lookupAttribute.found + && top.config.warnOrphaned + && !isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar, at.lookupAttribute.dcl.sourceGrammar], top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned occurs declaration: " ++ at.lookupAttribute.fullName ++ " on " ++ nt.lookupType.fullName)] + -- If this is a non-closed NT, or not a synthesized attribute, then we're done. + else []; + + top.errors <- + if !nt.lookupType.found || !at.lookupAttribute.found || !isClosedNt || !at.lookupAttribute.dcl.isSynthesized then [] + -- For closed nt, either we're exported by only the nt, OR there MUST be a default! + else if !isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars) + && null(lookupDef(nt.lookupType.fullName, at.lookupAttribute.fullName, top.flowEnv)) + then [mwdaWrn(top.config, top.location, at.lookupAttribute.fullName ++ " cannot occur on " ++ nt.lookupType.fullName ++ " because that nonterminal is closed, and this attribute does not have a default equation.")] + else []; +} + diff --git a/grammars/silver/compiler/analysis/warnings/flow/OrphanedProduction.sv b/grammars/silver/compiler/analysis/warnings/flow/OrphanedProduction.sv new file mode 100644 index 000000000..f097489ff --- /dev/null +++ b/grammars/silver/compiler/analysis/warnings/flow/OrphanedProduction.sv @@ -0,0 +1,49 @@ +grammar silver:compiler:analysis:warnings:flow; + +synthesized attribute warnFwd :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.warnFwd = false; +} +abstract production warnFwdFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnFwd = true; + forwards to rest; +} +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--warn-fwd", paramString=nothing(), + help="warn about orphaned productions", + flagParser=flag(warnFwdFlag))]; +} + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + local ntDefGram :: String = + substring(0, lastIndexOf(":", namedSig.outputElement.typerep.typeName), namedSig.outputElement.typerep.typeName); + + local isClosedNt :: Boolean = + case getTypeDclAll(namedSig.outputElement.typerep.typeName, top.env) of + | ntDcl(_, _, closed, _) :: _ -> closed + | _ -> false -- default, if the lookup fails + end; + + top.errors <- + if null(body.errors ++ ns.errors) + && top.config.warnFwd + -- If this production does not forward + && null(body.uniqueSignificantExpression) + -- AND this is not a closed nonterminal + && !isClosedNt + -- AND this production is not exported by the nonterminal definition grammar... even including options + && !isExportedBy(top.grammarName, [ntDefGram], top.compiledGrammars) + then [mwdaWrn(top.config, top.location, "Orphaned production: " ++ id.name ++ " on " ++ namedSig.outputElement.typerep.typeName)] + else []; +} + diff --git a/grammars/silver/compiler/composed/Default/Main.sv b/grammars/silver/compiler/composed/Default/Main.sv new file mode 100644 index 000000000..d99bbb7c8 --- /dev/null +++ b/grammars/silver/compiler/composed/Default/Main.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:composed:Default; + +import silver:compiler:host; + +parser svParse::Root { + silver:compiler:host; +} + +function main +IOVal ::= args::[String] ioin::IOToken +{ + return evalIO(cmdLineRun(args, svParse), ioin); +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/NonTerminalDcl.sv b/grammars/silver/compiler/definition/concrete_syntax/NonTerminalDcl.sv new file mode 100644 index 000000000..1be9d45ea --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/NonTerminalDcl.sv @@ -0,0 +1,29 @@ +grammar silver:compiler:definition:concrete_syntax; + +import silver:compiler:driver only noOrigins, forceOrigins; + +aspect production nonterminalDcl +top::AGDcl ::= quals::NTDeclQualifiers 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' +{ + -- TODO: We are building this for every nonterminal declaration, when it should + -- be the same for all nonterminals in the grammar + local med :: ModuleExportedDefs = + moduleExportedDefs(top.location, top.compiledGrammars, top.grammarDependencies, [top.grammarName], []); + local syntax::Syntax = foldr(consSyntax, nilSyntax(), med.syntaxAst); + + production isThisTracked::Boolean = top.config.forceOrigins || ((!top.config.noOrigins) && quals.tracked); + local exportedLayoutTerms::[String] = syntax.allIgnoreTerminals; + local exportedProds::[String] = syntax.allProductionNames; + + top.syntaxAst := + [ syntaxNonterminal( + nonterminalType(fName, map((.kindrep), tl.types), isThisTracked), + nilSyntax(), exportedProds, exportedLayoutTerms, + foldr(consNonterminalMod, nilNonterminalMod(), nm.nonterminalModifiers), + location=top.location, sourceGrammar=top.grammarName) + ]; +} + +monoid attribute nonterminalModifiers :: [SyntaxNonterminalModifier]; +attribute nonterminalModifiers occurs on NonterminalModifiers, NonterminalModifierList, NonterminalModifier; +propagate nonterminalModifiers on NonterminalModifiers, NonterminalModifierList; diff --git a/grammars/silver/compiler/definition/concrete_syntax/ParserSpec.sv b/grammars/silver/compiler/definition/concrete_syntax/ParserSpec.sv new file mode 100644 index 000000000..6f6039f7f --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ParserSpec.sv @@ -0,0 +1,80 @@ +grammar silver:compiler:definition:concrete_syntax; + +import silver:compiler:driver:util only computeDependencies; -- TODO this is a bad dependency!! + +{-- + - An abstract representation of a parser declaration. + -} +nonterminal ParserSpec with + sourceGrammar, location, fullName, compareTo, isEqual, + compiledGrammars, + cstAst, startNT, moduleNames; + +{-- + - Given compiledGrammars, gives back the SyntaxRoot representing this parser. + -} +synthesized attribute cstAst :: SyntaxRoot; + +{-- + - The full name of the start nonterminal of this parser spec. + -} +synthesized attribute startNT :: String; + +{-- + - Prefixes to inject onto terminals in the composed parser. + -} +monoid attribute terminalPrefixes :: [Pair]; + +{-- + - Prefixes to inject onto the marking terminals of grammars in the composed parser. + -} +monoid attribute grammarTerminalPrefixes :: [Pair]; + + +abstract production parserSpec +top::ParserSpec ::= + fn::String snt::String grams::[String] + customStartLayout::Maybe<[String]> + terminalPrefixes::[Pair] grammarTerminalPrefixes::[Pair] + addedDcls::[SyntaxDcl] +{ + propagate isEqual; + top.fullName = fn; + top.startNT = snt; + top.moduleNames := grams; + + -- We've decided we're using only the grammars in this parser to compute dependencies, as opposed + -- to all grammars imported in the env. + -- This could affect which conditional imports get triggered, and thus what gets included in the parser + production deps :: [String] = computeDependencies(grams, top.compiledGrammars); + production med :: ModuleExportedDefs = moduleExportedDefs(top.location, top.compiledGrammars, deps, grams, []); + + -- Compute the list of terminal prefixes for marking terminals here, because the set of marking + -- terminals exported by a grammar depends on conditional imports that may be triggered by other + -- grammars included in the parser. + -- Also works around a build bug (https://github.com/melt-umn/silver/issues/36) - the grammar + -- containing the parser spec (and thus the ParserSpec itself) does not get rebuilt when only its + -- dependencies are modified, thus the set of marking terminals exported by a grammar when the + -- ParserSpec is built may be out of date. + -- componentGrammarMarkingTerminals is also needed to help determine the prefix seperator. + production componentGrammarMarkingTerminals::[Pair] = + map( + \ g::String -> + pair(g, + foldr(consSyntax, nilSyntax(), + moduleExportedDefs(top.location, top.compiledGrammars, deps, [g], []).syntaxAst).allMarkingTerminals), + grams); + production markingTerminalPrefixes::[Pair] = + flatMap( + \ gp::Pair -> + map(pair(_, gp.snd), lookup(gp.fst, componentGrammarMarkingTerminals).fromJust), + grammarTerminalPrefixes); + + top.cstAst = + cstRoot( + fn, snt, foldr(consSyntax, nilSyntax(), addedDcls ++ med.syntaxAst), + customStartLayout, terminalPrefixes ++ markingTerminalPrefixes, + componentGrammarMarkingTerminals, location=top.location, + sourceGrammar=top.sourceGrammar); +} + diff --git a/grammars/silver/compiler/definition/concrete_syntax/ProductionDcl.sv b/grammars/silver/compiler/definition/concrete_syntax/ProductionDcl.sv new file mode 100644 index 000000000..854d35249 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ProductionDcl.sv @@ -0,0 +1,165 @@ +grammar silver:compiler:definition:concrete_syntax; + +import silver:compiler:modification:copper only actionDefs; + +autocopy attribute productionSig :: NamedSignature; + +concrete production concreteProductionDcl +top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody +{ + top.unparse = "concrete production " ++ id.unparse ++ "\n" ++ ns.unparse ++ " " ++ pm.unparse ++ "\n" ++ body.unparse; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production namedSig :: NamedSignature = ns.namedSignature; + + ns.signatureName = fName; + ns.env = newScopeEnv(ns.defs, top.env); + pm.productionSig = ns.namedSignature; + pm.env = newScopeEnv(ns.actionDefs, top.env); + + top.errors <- pm.errors; + top.errors <- ns.concreteSyntaxTypeErrors; + + top.syntaxAst := + [ syntaxProduction(namedSig, + foldr(consProductionMod, nilProductionMod(), pm.productionModifiers), + location=top.location, sourceGrammar=top.grammarName) + ]; + + forwards to productionDcl('abstract', $2, id, ns, body, location=top.location); +} action { + insert semantic token IdFnProdDcl_t at id.location; + sigNames = []; +} + +nonterminal ProductionModifiers with config, location, unparse, productionModifiers, errors, env, productionSig; -- 0 or some +nonterminal ProductionModifierList with config, location, unparse, productionModifiers, errors, env, productionSig; -- 1 or more +closed nonterminal ProductionModifier with config, location, unparse, productionModifiers, errors, env, productionSig; -- 1 + +monoid attribute productionModifiers :: [SyntaxProductionModifier]; + +propagate productionModifiers on ProductionModifiers, ProductionModifierList; +propagate errors on ProductionModifiers, ProductionModifierList, ProductionModifier; + +concrete production productionModifiersNone +top::ProductionModifiers ::= +{ + top.unparse = ""; +} +concrete production productionModifierSome +top::ProductionModifiers ::= pm::ProductionModifierList +{ + top.unparse = pm.unparse; +} + +concrete production productionModifierSingle +top::ProductionModifierList ::= pm::ProductionModifier +{ + top.unparse = pm.unparse; +} +concrete production productionModifiersCons +top::ProductionModifierList ::= h::ProductionModifier ',' t::ProductionModifierList +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + + +concrete production productionModifierPrecedence +top::ProductionModifier ::= 'precedence' '=' i::Int_t +{ + top.unparse = "precedence = " ++ i.lexeme; + + top.productionModifiers := [prodPrecedence(toInteger(i.lexeme))]; +} + +terminal Operator_kwd 'operator' lexer classes {MODIFIER,RESERVED}; + +concrete production productionModifierOperator +top::ProductionModifier ::= 'operator' '=' n::QNameType +{ + top.unparse = "operator = " ++ n.unparse; + + top.productionModifiers := [prodOperator(n.lookupType.fullName)]; + + top.errors <- n.lookupType.errors ++ + if !n.lookupType.typeScheme.isTerminal + then [err(n.location, n.unparse ++ " is not a terminal.")] + else []; +} + +-------------------------------------------------------------------------------- +-- Type sanity checking on concrete productions + +monoid attribute concreteSyntaxTypeErrors :: [Message]; +attribute concreteSyntaxTypeErrors occurs on ProductionSignature, ProductionRHS, ProductionRHSElem; +propagate concreteSyntaxTypeErrors on ProductionSignature, ProductionRHS, ProductionRHSElem; + +flowtype concreteSyntaxTypeErrors {grammarName, env, flowEnv, deterministicCount} on ProductionRHSElem; + +aspect production productionSignature +top::ProductionSignature ::= cl::ConstraintList '=>' lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + local fstType :: Type = head(top.namedSignature.inputElements).typerep; + local lstType :: Type = last(top.namedSignature.inputElements).typerep; + + local checkFirst :: Boolean = + fstType.isTerminal || !null(getOccursDcl("silver:core:location", fstType.typeName, top.env)) || fstType.tracked; + local checkSecond :: Boolean = + lstType.isTerminal || !null(getOccursDcl("silver:core:location", lstType.typeName, top.env)) || lstType.tracked; + local errFirst :: [Message] = + if checkFirst then [] else [err(top.location, "Production has location annotation or is tracked, but first element of signature does not have location and is not tracked.")]; + local errSecond :: [Message] = + if checkSecond then [] else [err(top.location, "Production has location annotation or is tracked, but last element of signature does not have location and is not tracked.")]; + + local lhsHasLocation :: Boolean = + case top.namedSignature.namedInputElements of + | [namedSignatureElement("silver:core:location", _)] -> true + | _ -> false + end; + local lhsHasOrigin :: Boolean = top.namedSignature.outputElement.typerep.tracked; + + top.concreteSyntaxTypeErrors <- + case top.namedSignature.namedInputElements of + | [] -> [] + | [namedSignatureElement("silver:core:location", _)] -> [] + | _ -> [err(top.location, "Annotation(s) on this production are not handleable by the parser generator (only a single annotation, and only silver:core:location is supported.)")] + end; + + top.concreteSyntaxTypeErrors <- + if lhsHasLocation || lhsHasOrigin + then case length(top.namedSignature.inputElements) of + | 0 -> [] -- OK + | 1 -> errFirst + | _ -> errFirst ++ errSecond + end + else []; +} + +aspect production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + top.concreteSyntaxTypeErrors <- + if t.typerep.permittedInConcreteSyntax then [] + else [err(t.location, t.unparse ++ " is not permitted on concrete productions. Only terminals and nonterminals (without type variables) can appear here")]; +} + +synthesized attribute permittedInConcreteSyntax :: Boolean occurs on Type; + +aspect default production +top::Type ::= +{ + top.permittedInConcreteSyntax = false; +} + +aspect production nonterminalType +top::Type ::= fn::String ks::[Kind] tracked::Boolean +{ + top.permittedInConcreteSyntax = null(ks); +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.permittedInConcreteSyntax = true; +} + diff --git a/grammars/silver/compiler/definition/concrete_syntax/Project.sv b/grammars/silver/compiler/definition/concrete_syntax/Project.sv new file mode 100644 index 000000000..af6d048cd --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/Project.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:definition:concrete_syntax; + +imports silver:langutil:lsp as lsp; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +imports silver:compiler:definition:concrete_syntax:ast; + +option silver:compiler:modification:copper; diff --git a/grammars/silver/compiler/definition/concrete_syntax/Root.sv b/grammars/silver/compiler/definition/concrete_syntax/Root.sv new file mode 100644 index 000000000..35b71d130 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/Root.sv @@ -0,0 +1,21 @@ +grammar silver:compiler:definition:concrete_syntax; + +monoid attribute syntaxAst :: [SyntaxDcl]; +monoid attribute parserSpecs :: [ParserSpec]; + +attribute syntaxAst, parserSpecs occurs on Root, AGDcls, AGDcl; +flowtype syntaxAst {decorate} on Root, AGDcls, AGDcl; +flowtype parserSpecs {decorate} on Root, AGDcls, AGDcl; +propagate syntaxAst, parserSpecs on Root, AGDcls; + +aspect default production +top::AGDcl ::= +{ + propagate syntaxAst, parserSpecs; +} + +aspect production appendAGDcl +top::AGDcl ::= ag1::AGDcl ag2::AGDcl +{ + propagate syntaxAst, parserSpecs; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/RootSpec.sv b/grammars/silver/compiler/definition/concrete_syntax/RootSpec.sv new file mode 100644 index 000000000..f5ba7f766 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/RootSpec.sv @@ -0,0 +1,55 @@ +grammar silver:compiler:definition:concrete_syntax; + +import silver:compiler:driver:util; + +attribute syntaxAst, parserSpecs occurs on RootSpec, ModuleExportedDefs, Grammar; +propagate syntaxAst, parserSpecs on RootSpec, Grammar; + +monoid attribute hasSyntaxAst::Boolean with false, ||; +monoid attribute hasParserSpecs::Boolean with false, ||; +attribute syntaxAst, parserSpecs, hasSyntaxAst, hasParserSpecs occurs on InterfaceItems, InterfaceItem; +propagate syntaxAst, parserSpecs, hasSyntaxAst, hasParserSpecs on InterfaceItems, InterfaceItem; + +aspect production consInterfaceItem +top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems +{ + top.interfaceErrors <- if !top.hasSyntaxAst then ["Missing item syntaxAst"] else []; + top.interfaceErrors <- if !top.hasParserSpecs then ["Missing item parserSpecs"] else []; +} + +aspect default production +top::InterfaceItem ::= +{ + propagate syntaxAst, parserSpecs, hasSyntaxAst, hasParserSpecs; +} + +abstract production syntaxAstInterfaceItem +top::InterfaceItem ::= val::[SyntaxDcl] +{ + propagate isEqual; + top.syntaxAst <- val; + top.hasSyntaxAst <- true; +} + +abstract production parserSpecsInterfaceItem +top::InterfaceItem ::= val::[ParserSpec] +{ + propagate isEqual; + top.parserSpecs <- val; + top.hasParserSpecs <- true; +} + +aspect function packInterfaceItems +InterfaceItems ::= r::Decorated RootSpec +{ + interfaceItems <- [syntaxAstInterfaceItem(r.syntaxAst)]; + interfaceItems <- [parserSpecsInterfaceItem(r.parserSpecs)]; +} + +aspect production moduleExportedDefs +top::ModuleExportedDefs ::= l::Location compiled::EnvTree grammarDependencies::[String] need::[String] seen::[String] +{ + top.syntaxAst := if null(need) || null(rs) then [] else (head(rs).syntaxAst ++ recurse.syntaxAst); + top.parserSpecs := if null(need) || null(rs) then [] else (head(rs).parserSpecs ++ recurse.parserSpecs); +} + diff --git a/grammars/silver/compiler/definition/concrete_syntax/TerminalDcl.sv b/grammars/silver/compiler/definition/concrete_syntax/TerminalDcl.sv new file mode 100644 index 000000000..879e2bb3d --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/TerminalDcl.sv @@ -0,0 +1,201 @@ +grammar silver:compiler:definition:concrete_syntax; + +import silver:langutil:pp; +import silver:regex as abs; +import silver:regex:concrete_syntax; + +terminal Ignore_kwd 'ignore' lexer classes {MODIFIER}; +terminal Marking_kwd 'marking' lexer classes {MODIFIER}; +terminal Named_kwd 'named' lexer classes {MODIFIER}; +terminal Left_kwd 'left' lexer classes {MODIFIER}; +terminal Association_kwd 'association' lexer classes {MODIFIER}; +terminal Right_kwd 'right' lexer classes {MODIFIER}; +terminal RepeatProb_kwd 'repeatProb' lexer classes {MODIFIER}; -- For use by the treegen extension + +-- We actually need to reserved this due to its appearance in PRODUCTION modifiers. +terminal Precedence_kwd 'precedence' lexer classes {MODIFIER,RESERVED}; + +abstract production terminalDclDefault +top::AGDcl ::= t::TerminalKeywordModifier id::Name r::RegExpr tm::TerminalModifiers +{ + top.unparse = t.unparse ++ "terminal " ++ id.unparse ++ " " ++ r.unparse ++ " " ++ tm.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ id.name; + + top.defs := [termDef(top.grammarName, id.location, fName, r.terminalRegExprSpec, r.easyName, tm.genRepeatProb)]; + + top.errors <- + if length(getTypeDclAll(fName, top.env)) > 1 + then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- + if isLower(substring(0,1,id.name)) + then [err(id.location, "Types must be capitalized. Invalid terminal name " ++ id.name)] + else []; + + -- This is a crude check, but effective. + top.errors <- + if indexOf("\n", unescapeString(r.unparse)) != -1 && indexOf("\r", unescapeString(r.unparse)) == -1 + then [wrn(r.location, "Regex contains '\\n' but not '\\r'. This is your reminder about '\\r\\n' newlines.")] + else []; + + propagate errors; + + top.syntaxAst := [ + syntaxTerminal(fName, r.terminalRegExprSpec, + foldr(consTerminalMod, nilTerminalMod(), t.terminalModifiers ++ tm.terminalModifiers), + location=top.location, sourceGrammar=top.grammarName)]; +} + +concrete production terminalDclKwdModifiers +top::AGDcl ::= t::TerminalKeywordModifier 'terminal' id::Name r::RegExpr ';' +{ + forwards to terminalDclDefault(t, id, r, terminalModifiersNone(location=$5.location), location=top.location); +} action { + insert semantic token IdTypeDcl_t at id.location; +} + +concrete production terminalDclAllModifiers +top::AGDcl ::= t::TerminalKeywordModifier 'terminal' id::Name r::RegExpr tm::TerminalModifiers ';' +{ + forwards to terminalDclDefault(t, id, r, tm, location=top.location); +} action { + insert semantic token IdTypeDcl_t at id.location; +} + +{-- + - This exists as a catch-all for representing regular expressions for terminals. + - There's only one option here, but it's an extension point. + -} +nonterminal RegExpr with config, location, grammarName, unparse, terminalRegExprSpec, easyName; + +synthesized attribute terminalRegExprSpec :: abs:Regex; +synthesized attribute easyName :: Maybe; + +terminal RegexSlash_t '/' lexer classes {lsp:Regexp}; + +concrete production regExpr_c +top::RegExpr ::= '/' r::Regex '/' +layout {} +{ + top.unparse = "/" ++ r.unparse ++ "/"; + forwards to regExpr(r.ast, location=top.location); +} + +abstract production regExpr +top::RegExpr ::= r::abs:Regex +{ + top.unparse = "/" ++ show(80, r.pp) ++ "/"; + top.terminalRegExprSpec = r; + top.easyName = nothing(); +} + + +closed nonterminal TerminalKeywordModifier with unparse, location, terminalModifiers; + +concrete production terminalKeywordModifierIgnore +top::TerminalKeywordModifier ::= 'ignore' +{ + top.unparse = "ignore "; + + top.terminalModifiers := [termIgnore()]; +} + +concrete production terminalKeywordModifierMarking +top::TerminalKeywordModifier ::= 'marking' +{ + top.unparse = "marking "; + + top.terminalModifiers := [termMarking()]; +} + +concrete production terminalKeywordModifierNone +top::TerminalKeywordModifier ::= +{ + top.unparse = ""; + + top.terminalModifiers := []; +} + + +nonterminal TerminalModifiers with config, location, unparse, terminalModifiers, genRepeatProb, errors, env, grammarName, compiledGrammars, flowEnv; +closed nonterminal TerminalModifier with config, location, unparse, terminalModifiers, genRepeatProb, errors, env, grammarName, compiledGrammars, flowEnv; + +monoid attribute terminalModifiers :: [SyntaxTerminalModifier]; +monoid attribute genRepeatProb :: Maybe with nothing(), orElse; + +propagate terminalModifiers, genRepeatProb, errors on TerminalModifiers; + +aspect default production +top::TerminalModifier ::= +{ + top.genRepeatProb := nothing(); +} + +abstract production terminalModifiersNone +top::TerminalModifiers ::= +{ + top.unparse = ""; +} +concrete production terminalModifierSingle +top::TerminalModifiers ::= tm::TerminalModifier +{ + top.unparse = tm.unparse; +} +concrete production terminalModifiersCons +top::TerminalModifiers ::= h::TerminalModifier ',' t::TerminalModifiers +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + +concrete production terminalModifierLeft +top::TerminalModifier ::= 'association' '=' 'left' +{ + top.unparse = "association = left"; + + top.terminalModifiers := [termAssociation("left")]; + top.errors := []; +} +concrete production terminalModifierRight +top::TerminalModifier ::= 'association' '=' 'right' +{ + top.unparse = "association = right"; + + top.terminalModifiers := [termAssociation("right")]; + top.errors := []; +} + +concrete production terminalModifierPrecedence +top::TerminalModifier ::= 'precedence' '=' i::Int_t +{ + top.unparse = "precedence = " ++ i.lexeme; + + top.terminalModifiers := [termPrecedence(toInteger(i.lexeme))]; + top.errors := []; +} + +-- For use by the treegen extension. +-- Has to be in the "host language" since it goes in the regular env and not the CST AST. +concrete production terminalModifierRepeatProb +top::TerminalModifier ::= 'repeatProb' '=' f::Float_t +{ + top.unparse = "repeatProb = " ++ f.lexeme; + + top.terminalModifiers := []; + top.genRepeatProb := just(toFloat(f.lexeme)); + top.errors := + if toFloat(f.lexeme) >= 1.0 + then [err(f.location, "Repeat probability must be < 1.0")] + else []; +} + +concrete production terminalModifierNamed +top::TerminalModifier ::= 'named' name::String_t +{ + top.unparse = "named " ++ name.lexeme; + + top.terminalModifiers := [termPrettyName(substring(1, length(name.lexeme) - 1, name.lexeme))]; + top.errors := []; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/CstAst.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/CstAst.sv new file mode 100644 index 000000000..13862b9a0 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/CstAst.sv @@ -0,0 +1,174 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +imports silver:regex; +imports silver:compiler:definition:type; +imports silver:compiler:definition:env; + +imports silver:compiler:translation:java:core only makeIdName, makeProdName, makeNTName; +imports silver:compiler:translation:java:type only transType; + +import silver:compiler:definition:concrete_syntax:copper as copper; +import silver:util:graph as g; +import silver:util:treemap as tm; +import silver:util:treeset as s; + +{-- + - Encapsulates transformations and analysis of Syntax + -} +closed nonterminal SyntaxRoot with location, sourceGrammar, cstErrors, copperParser, allTerminals, allNonterminals, dominatingTerminals, compareTo, isEqual; +propagate compareTo, isEqual, allTerminals, allNonterminals on SyntaxRoot; + +@{-- The Copper API object corresponding to the parser. -} +synthesized attribute copperParser::copper:ParserBean; + +@{-- An environment containing all terminals that dominate any particular one. -} +synthesized attribute dominatingTerminals::EnvTree; + +abstract production cstRoot +top::SyntaxRoot ::= + parsername::String startnt::String s::Syntax + customStartLayout::Maybe<[String]> terminalPrefixes::[Pair] componentGrammarMarkingTerminals::[Pair] +{ + s.cstEnv = directBuildTree(s.cstDcls); + s.cstNTProds = directBuildTree(s.cstProds); + s.classTerminals = directBuildTree(s.classTerminalContribs); + s.containingGrammar = "host"; + s.superClasses = + directBuildTree( + g:toList( + g:transitiveClosure( + g:add( + s.superClassContribs, + g:empty())))); + s.subClasses = + directBuildTree( + g:toList( + g:transitiveClosure( + g:add( + map(\ p::Pair -> pair(p.snd, p.fst), s.superClassContribs), + g:empty())))); + s.parserAttributeAspects = directBuildTree(s.parserAttributeAspectContribs); + s.layoutTerms = + buildLayoutEnv( + map((.fullName), s.allTerminals), + map((.fullName), s.allProductions ++ s.allNonterminals), + s.layoutContribs); + s.prefixesForTerminals = directBuildTree(terminalPrefixes); + s.componentGrammarMarkingTerminals = directBuildTree(componentGrammarMarkingTerminals); + s.prettyNames = tm:add(s.prettyNamesAccum, tm:empty()); + + top.dominatingTerminals = directBuildTree(s.dominatingTerminalContribs); + + -- Move productions under their nonterminal, and sort the declarations + production s2 :: Syntax = foldr(consSyntax, nilSyntax(), sortBy(sortKeyLte, s.cstNormalize)); + s2.cstEnv = s.cstEnv; + s2.containingGrammar = "host"; + s2.cstNTProds = error("TODO: make this environment not be decorated?"); -- TODO + s2.classTerminals = s.classTerminals; + s2.superClasses = s.superClasses; + s2.subClasses = s.subClasses; + s2.parserAttributeAspects = s.parserAttributeAspects; + s2.layoutTerms = s.layoutTerms; + s2.prefixesForTerminals = s.prefixesForTerminals; + s2.componentGrammarMarkingTerminals = s.componentGrammarMarkingTerminals; + s2.prettyNames = tm:add(s2.prettyNamesAccum, tm:empty()); + + -- This should be on s1, because the s2 transform assumes everything is well formed. + -- In particular, it drops productions it can't find an NT for. + top.cstErrors := s.cstErrors; + + production startFound :: [Decorated SyntaxDcl] = searchEnvTree(startnt, s.cstEnv); + + top.cstErrors <- if !null(startFound) then [] + else ["Nonterminal " ++ startnt ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced as parser's starting nonterminal)"]; + + -- The layout before and after the root nonterminal. By default, the layout of the root nonterminal. + local startLayout::[copper:ElementReference] = + map((.copperElementReference), + map(head, + lookupStrings( + fromMaybe(searchEnvTree(startnt, s.layoutTerms), customStartLayout), + s.cstEnv))); + + local parserClassAuxCode::String = + s""" + protected List tokenList = null; + protected int tabStop = 8; + + public void reset() { + tokenList = new ArrayList(); + virtualLocation.setTabStop(tabStop); + } + + public List getTokens() { + return tokenList; // The way we reset this iterator when parsing again is to create a new list, so this is defacto immutable + } + + public void setTabStop(int tabStop) { + this.tabStop = tabStop; + } + + private void insertToken(common.Terminal token) { + // Insert after the last token that starts before the new one + int i; + for (i = tokenList.size(); i > 0; i--) { + if (tokenList.get(i - 1).getStartOffset() < token.getStartOffset()) { + break; + } + } + tokenList.add(i, token); + } +${s2.lexerClassRefDcls} + """; + local parserInitCode::String = "reset();"; + local preambleCode::String = "import java.util.ArrayList;\nimport java.util.List;\n"; + + local grammarElements::[copper:GrammarElement] = s2.copperGrammarElements + ++ [ copper:parserAttribute(top.sourceGrammar, builtinLoc("silver:compiler:definition:concrete_syntax:ast"), + "context", "common.DecoratedNode", "context = common.TopNode.singleton;") + ] + ++ flatMap((.copperGrammarElements), s2.disambiguationClasses); + top.copperParser = copper:parserBean(top.sourceGrammar, top.location, + makeCopperName(parsername), parsername, + head(startFound).copperElementReference, startLayout, + [s"common.SilverCopperParser<${makeNTName(head(startFound).fullName)}>"], + parserClassAuxCode, parserInitCode, preambleCode, + copper:grammar_(top.sourceGrammar, top.location, s2.containingGrammar, + grammarElements)); +} + + +{- +Assumptions we make about initial Syntax: + +1. All type parameter lists are the appropriate length. (Silver type checking) +-} + +function makeCopperName +String ::= str::String +{ + return makeIdName(str); +} + +-- Compute an environment containg the layout for a given list of items +function buildLayoutEnv +EnvTree ::= allTerms::[String] layoutItems::[String] layoutContribs::[Pair] +{ + -- Build a set of all terminals, for faster lookup + local terms::s:Set = s:add(allTerms, s:empty()); + -- Build a graph of nonterminals, productions and layout terminals where there is an edge a -> b iff a inherits layout from b + local transitiveLayout::g:Graph = g:transitiveClosure(g:add(layoutContribs, g:empty())); + -- For every item that we wish to compute layout (productions and nonterminals), find all inherited layout terminals + local layoutTerms::[Pair] = + map( + \ item::String -> + pair(item, s:toList(s:intersect(terms, g:edgesFrom(item, transitiveLayout)))), + layoutItems); + -- Build the layout EnvTree + return + directBuildTree( + flatMap( + \ item::Pair -> map(pair(item.fst, _), item.snd), + layoutTerms)); +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/LexerClassModifiers.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/LexerClassModifiers.sv new file mode 100644 index 000000000..8b13273d2 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/LexerClassModifiers.sv @@ -0,0 +1,144 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +import silver:util:treemap as tm; + +-- From TerminalModifiers +-- monoid attribute dominates_ :: [Decorated SyntaxDcl]; +-- monoid attribute submits_ :: [Decorated SyntaxDcl]; +-- synthesized attribute prefixSeperator :: Maybe; + +autocopy attribute className :: String; + +{-- + - Modifiers for lexer classes. + -} +nonterminal SyntaxLexerClassModifiers with compareTo, isEqual, cstEnv, cstErrors, className, classTerminals, superClasses, subClasses, superClassContribs, disambiguationClasses, prefixSeperator, containingGrammar, dominates_, submits_; + +propagate compareTo, isEqual, cstErrors, superClassContribs, disambiguationClasses, prefixSeperator, dominates_, submits_ + on SyntaxLexerClassModifiers; + +abstract production consLexerClassMod +top::SyntaxLexerClassModifiers ::= h::SyntaxLexerClassModifier t::SyntaxLexerClassModifiers +{ + top.cstErrors <- + if h.prefixSeperator.isJust && t.prefixSeperator.isJust + then ["Multiple prefix separators for class " ++ top.className] + else []; +} + +abstract production nilLexerClassMod +top::SyntaxLexerClassModifiers ::= +{} + + + +{-- + - Modifiers for lexer classes. + -} +closed nonterminal SyntaxLexerClassModifier with location, sourceGrammar, compareTo, isEqual, cstEnv, cstErrors, className, classTerminals, superClasses, subClasses, superClassContribs, disambiguationClasses, prefixSeperator, containingGrammar, dominates_, submits_; + +propagate compareTo, isEqual on SyntaxLexerClassModifier; + +{- We default ALL attributes, so we can focus only on those that are interesting in each case... -} +aspect default production +top::SyntaxLexerClassModifier ::= +{ + -- Empty values as defaults + propagate cstErrors, superClassContribs, disambiguationClasses, dominates_, submits_, prefixSeperator; +} + +{-- + - Other lexer classes of which this is considered a sub-class. + -} +abstract production lexerClassExtends +top::SyntaxLexerClassModifier ::= super::[String] +{ + -- Lexer classes not included in this parser are ignored, so library-defined + -- lexer classes can be optionally used without requring the library to be + -- included in the parser. See https://github.com/melt-umn/silver/issues/694 + production superRefs :: [Decorated SyntaxDcl] = concat(lookupStrings(super, top.cstEnv)); + + top.superClassContribs := map(pair(top.className, _), map((.fullName), superRefs)); +} + +{-- + - The submits list for the lexer class. Either lexer classes or terminals. + -} +abstract production lexerClassSubmits +top::SyntaxLexerClassModifier ::= sub::[String] +{ + production allSubs :: [String] = unions(sub :: lookupStrings(sub, top.subClasses)); + production subRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allSubs, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from submit clause for lexer class)"], --TODO: come up with a way to reference a given lexer class (line numbers would be great) + zipWith(pair, sub, subRefs)); + + top.submits_ := map(head, subRefs); +} +{-- + - The dominates list for the lexer class. Either lexer classes or terminals. + -} +abstract production lexerClassDominates +top::SyntaxLexerClassModifier ::= dom::[String] +{ + production allDoms :: [String] = unions(dom :: lookupStrings(dom, top.subClasses)); + production domRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allDoms, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from dominates clause for lexer class)"], + zipWith(pair, dom, domRefs)); + + top.dominates_ := map(head, domRefs); +} + +{-- + - A disambiguation function that should be created for the members of a lexer class. + -} +abstract production lexerClassDisambiguate +top::SyntaxLexerClassModifier ::= acode::String +{ + production terms :: [String] = searchEnvTree(top.className, top.classTerminals); + production funName::String = s"disambiguate_${makeCopperName(top.className)}"; + + production syntaxDcl::SyntaxDcl = + syntaxDisambiguationGroup(funName, terms, true, s""" +common.ConsCell tempShiftableList = common.ConsCell.nil; +for (int i = nextMember(0, shiftable); i >= 0; i = nextMember(i+1, shiftable)) { + tempShiftableList = new common.ConsCell(i, tempShiftableList); +} +final common.ConsCell shiftableList = tempShiftableList; +${acode} +""", location=top.location, sourceGrammar=top.sourceGrammar); + syntaxDcl.cstEnv = top.cstEnv; + syntaxDcl.containingGrammar = top.containingGrammar; + syntaxDcl.classTerminals = top.classTerminals; + syntaxDcl.superClasses = top.superClasses; + syntaxDcl.subClasses = top.subClasses; + + -- These are required by the flow type of xmlCopper, + -- but aren't really required by the syntaxDisambiguationGroup production. + syntaxDcl.parserAttributeAspects = tm:empty(); + syntaxDcl.layoutTerms = tm:empty(); + syntaxDcl.prefixesForTerminals = tm:empty(); + syntaxDcl.componentGrammarMarkingTerminals = tm:empty(); + syntaxDcl.prettyNames = tm:empty(); + + -- TODO: Check for duplicate disambiguation for a lexer class + + top.disambiguationClasses := [syntaxDcl]; +} + +{-- + - The default prefix separator for the members of a lexer class. + -} +abstract production lexerClassPrefixSeperator +top::SyntaxLexerClassModifier ::= sep::String +{ + top.cstErrors := []; + top.prefixSeperator := just(sep); +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/NonterminalModifiers.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/NonterminalModifiers.sv new file mode 100644 index 000000000..a68e6a60a --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/NonterminalModifiers.sv @@ -0,0 +1,52 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +imports silver:compiler:definition:core only nonterminalName; + +{-- + - Modifiers for nonterminals. + -} + +nonterminal SyntaxNonterminalModifiers with compareTo, isEqual, cstEnv, cstErrors, customLayout, nonterminalName; + +propagate compareTo, isEqual, cstErrors, customLayout on SyntaxNonterminalModifiers; + +abstract production consNonterminalMod +top::SyntaxNonterminalModifiers ::= h::SyntaxNonterminalModifier t::SyntaxNonterminalModifiers +{} + +abstract production nilNonterminalMod +top::SyntaxNonterminalModifiers ::= +{} + + +{-- + - Modifiers for nonterminals. + -} +nonterminal SyntaxNonterminalModifier with compareTo, isEqual, cstEnv, cstErrors, customLayout, nonterminalName; + +propagate compareTo, isEqual on SyntaxNonterminalModifier; + +aspect default production +top::SyntaxNonterminalModifier ::= +{ + -- Empty values as defaults + propagate cstErrors, customLayout; +} + +{-- + - The layout for this nonterminal. + -} +abstract production ntLayout +top::SyntaxNonterminalModifier ::= terms::[String] +{ + local termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from layout clause on nonterminal " ++ top.nonterminalName ++ ")"], + zipWith(pair, terms, termRefs)); + + top.customLayout := just(terms); +} + diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/PrettyNames.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/PrettyNames.sv new file mode 100644 index 000000000..9c38110a1 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/PrettyNames.sv @@ -0,0 +1,28 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +function asPrettyName +Maybe ::= r::Regex +{ + return map(\x::String -> "'" ++ x ++ "'", r.asLiteral); +} + +implicit synthesized attribute asLiteral::Maybe; +attribute asLiteral occurs on Regex; + +aspect default production +top::Regex ::= +{ + implicit top.asLiteral =; +} + +aspect production char +top::Regex ::= _ +{ + top.asLiteral = char; +} + +aspect production seq +top::Regex ::= r1::Regex r2::Regex +{ + top.asLiteral = r1.asLiteral ++ r2.asLiteral; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/ProductionModifiers.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/ProductionModifiers.sv new file mode 100644 index 000000000..a8f17f5a9 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/ProductionModifiers.sv @@ -0,0 +1,86 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +imports silver:compiler:definition:concrete_syntax only productionSig; + +monoid attribute productionPrecedence :: Maybe with nothing(), orElse; +-- acode from terminal modifiers +monoid attribute customLayout :: Maybe<[String]> with nothing(), orElse; +monoid attribute productionOperator :: Maybe with nothing(), orElse; + +{-- + - Modifiers for productions. + -} +nonterminal SyntaxProductionModifiers with compareTo, isEqual, cstEnv, cstErrors, acode, productionPrecedence, customLayout, productionOperator, productionSig; + +propagate compareTo, isEqual, cstErrors, acode, productionPrecedence, customLayout, productionOperator + on SyntaxProductionModifiers; + +abstract production consProductionMod +top::SyntaxProductionModifiers ::= h::SyntaxProductionModifier t::SyntaxProductionModifiers +{} + +abstract production nilProductionMod +top::SyntaxProductionModifiers ::= +{} + + +{-- + - Modifiers for productions. + -} +nonterminal SyntaxProductionModifier with compareTo, isEqual, cstEnv, cstErrors, acode, productionPrecedence, customLayout, productionOperator, productionSig; + +propagate compareTo, isEqual on SyntaxProductionModifier; + +aspect default production +top::SyntaxProductionModifier ::= +{ + -- Empty values as defaults + propagate cstErrors, acode, productionPrecedence, customLayout, productionOperator; +} + +{-- + - The precedence for the production. (Resolves reduce/reduce conflicts.) + -} +abstract production prodPrecedence +top::SyntaxProductionModifier ::= lvl::Integer +{ + top.productionPrecedence := just(lvl); +} +{-- + - The terminal this production uses for shift/reduce conflict resolution. + - By default, the last terminal in the production? TODO + -} +abstract production prodOperator +top::SyntaxProductionModifier ::= term::String +{ + local termRef :: [Decorated SyntaxDcl] = searchEnvTree(term, top.cstEnv); + + top.cstErrors := if !null(termRef) then [] + else ["Terminal " ++ term ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from operator clause on production " ++ top.productionSig.fullName ++ ")"]; + top.productionOperator := just(head(termRef)); +} +{-- + - The action to perform when this production is REDUCEd. + -} +abstract production prodAction +top::SyntaxProductionModifier ::= acode::String +{ + top.acode := acode; +} +{-- + - The layout for this production. + -} +abstract production prodLayout +top::SyntaxProductionModifier ::= terms::[String] +{ + local termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from layout clause on production " ++ top.productionSig.fullName ++ ")"], + zipWith(pair, terms, termRefs)); + + top.customLayout := just(terms); +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/Regex.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/Regex.sv new file mode 100644 index 000000000..d9dff46e3 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/Regex.sv @@ -0,0 +1,92 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +imports silver:core hiding empty, alt; +import silver:compiler:definition:concrete_syntax:copper as copper; + +-- Translation of regex to Copper grammar beans. +synthesized attribute copperRegex::copper:Regex occurs on Regex; +synthesized attribute copperRegexAlts::[copper:Regex] occurs on Regex; +synthesized attribute copperRegexSeqs::[copper:Regex] occurs on Regex; +implicit synthesized attribute copperRegexCharSet::Maybe; +attribute copperRegexCharSet occurs on Regex; + +aspect default production +top::Regex ::= +{ + top.copperRegexAlts = [top.copperRegex]; + top.copperRegexSeqs = [top.copperRegex]; + implicit top.copperRegexCharSet =; +} + +--------------------------------------------------------------------------------- + +aspect production char +top::Regex ::= _ +{ + local charSet::copper:CharSet = copper:singleChar(char); + top.copperRegex = copper:characterSetRegex(charSet); + top.copperRegexCharSet = charSet; +} +aspect production wildChar +top::Regex ::= +{ + -- Copper has no direct representation of dot. + -- Dot represents everything EXCEPT \n + + local charSet::copper:CharSet = copper:invertCharSet(copper:singleChar("\n")); + top.copperRegex = copper:characterSetRegex(charSet); + top.copperRegexCharSet = charSet; +} +aspect production charRange +top::Regex ::= _ _ +{ + local charSet::copper:CharSet = copper:charRange(lChar, uChar); + top.copperRegex = copper:characterSetRegex(charSet); + top.copperRegexCharSet = charSet; +} +aspect production negChars +top::Regex ::= r::Regex +{ + local charSet::copper:CharSet = copper:invertCharSet(r.copperRegexCharSet.fromJust); + top.copperRegex = copper:characterSetRegex(charSet); + top.copperRegexCharSet = charSet; +} + +aspect production empty +top::Regex ::= +{ + top.copperRegex = copper:choiceRegex([]); + top.copperRegexAlts = []; +} +aspect production epsilon +top::Regex ::= +{ + top.copperRegex = copper:emptyStringRegex(); + top.copperRegexSeqs = []; +} +aspect production alt +top::Regex ::= r1::Regex r2::Regex +{ + top.copperRegex = + case top.copperRegexCharSet of + | just(cs) -> copper:characterSetRegex(cs) + | nothing() -> copper:choiceRegex(top.copperRegexAlts) + end; + top.copperRegexAlts = + case top.copperRegexCharSet of + | just(_) -> [top.copperRegex] + | nothing() -> r1.copperRegexAlts ++ r2.copperRegexAlts + end; + top.copperRegexCharSet = copper:unionCharSets(r1.copperRegexCharSet, r2.copperRegexCharSet); +} +aspect production seq +top::Regex ::= r1::Regex r2::Regex +{ + top.copperRegex = copper:concatenationRegex(top.copperRegexSeqs); + top.copperRegexSeqs = r1.copperRegexSeqs ++ r2.copperRegexSeqs; +} +aspect production star +top::Regex ::= r::Regex +{ + top.copperRegex = copper:kleeneStarRegex(r.copperRegex); +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/Syntax.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/Syntax.sv new file mode 100644 index 000000000..bd064c163 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/Syntax.sv @@ -0,0 +1,479 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +imports silver:compiler:translation:java:core only makeTerminalName; +import silver:compiler:definition:concrete_syntax:copper as copper; +import silver:util:treemap as tm; +import silver:util:treeset as s; + +-- For looking syntax elements up by name. +monoid attribute cstDcls :: [Pair]; +autocopy attribute cstEnv :: EnvTree; +monoid attribute cstErrors :: [String]; + +-- Transformation that moves productions underneath their respective nonterminals. +monoid attribute cstProds :: [Pair]; +autocopy attribute cstNTProds :: EnvTree; +monoid attribute cstNormalize :: [SyntaxDcl]; + +-- Compute and allow lookup of all terminals in a lexer class +monoid attribute classTerminalContribs::[Pair]; +autocopy attribute classTerminals::EnvTree; +monoid attribute superClassContribs::[Pair]; +autocopy attribute superClasses::EnvTree; +autocopy attribute subClasses::EnvTree; + +-- Parser attribute action code aspects +monoid attribute parserAttributeAspectContribs::[Pair]; +autocopy attribute parserAttributeAspects::EnvTree; + +-- TODO: Attributes that lift out various sorts of SyntaxDcls all extract references +-- of type Decorated SyntaxDcl. The actual set of attributes needed for translation +-- varies between different SyntaxDcl productions, however the flow analysis forces +-- all these references to have the entire set of possible inh attributes. +-- We should perhaps consider factoring out different sorts of SyntaxDcls into seperate +-- nonterminals, e.g. SyntaxNonterminal, SyntaxProduction, etc. and collect references +-- to these various nonterminals with only the relevant attributes instead. +monoid attribute allTerminals :: [Decorated SyntaxDcl]; +monoid attribute allIgnoreTerminals :: [String]; +monoid attribute allMarkingTerminals :: [String]; +monoid attribute allProductions :: [Decorated SyntaxDcl]; +monoid attribute allProductionNames :: [String]; -- Doesn't depend on anything +monoid attribute allNonterminals :: [Decorated SyntaxDcl]; +monoid attribute disambiguationClasses :: [Decorated SyntaxDcl]; +synthesized attribute domContribs :: [Decorated SyntaxDcl]; +synthesized attribute subContribs :: [Decorated SyntaxDcl]; +monoid attribute memberTerminals :: [Decorated SyntaxDcl]; +monoid attribute dominatingTerminalContribs :: [(String, Decorated SyntaxDcl)]; +synthesized attribute terminalRegex::Regex; +autocopy attribute containingGrammar :: String; +monoid attribute lexerClassRefDcls :: String; +synthesized attribute exportedProds :: [String]; +synthesized attribute hasCustomLayout :: Boolean; +monoid attribute layoutContribs :: [Pair]; -- prod/nt name, prod/nt/term name +autocopy attribute layoutTerms::EnvTree; + +autocopy attribute prefixesForTerminals :: EnvTree; +autocopy attribute componentGrammarMarkingTerminals :: EnvTree<[String]>; + +-- Creating unambiguous s; this is a multiset used to accumulate all the +-- names for terminals, and the actual name for will be modified to +-- disambiguate if it would be ambiguous. +monoid attribute prettyNamesAccum::[Pair]; +autocopy attribute prettyNames::tm:Map; + +synthesized attribute copperElementReference::copper:ElementReference; +synthesized attribute copperGrammarElements::[copper:GrammarElement]; + +{-- + - An abstract syntax tree for representing concrete syntax. + -} +nonterminal Syntax with compareTo, isEqual, cstDcls, cstEnv, cstErrors, cstProds, cstNTProds, cstNormalize, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allProductionNames, allNonterminals, disambiguationClasses, memberTerminals, dominatingTerminalContribs, classTerminalContribs, classTerminals, superClassContribs, superClasses, subClasses, parserAttributeAspectContribs, parserAttributeAspects, lexerClassRefDcls, layoutContribs, layoutTerms, containingGrammar, prefixesForTerminals, componentGrammarMarkingTerminals, prettyNamesAccum, prettyNames, copperGrammarElements; +propagate compareTo, isEqual on Syntax; + +flowtype decorate {cstEnv, classTerminals, superClasses, subClasses, containingGrammar, layoutTerms, prefixesForTerminals, componentGrammarMarkingTerminals, parserAttributeAspects, prettyNames} on Syntax, SyntaxDcl; + +propagate cstDcls, cstErrors, cstProds, cstNormalize, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allProductionNames, allNonterminals, disambiguationClasses, memberTerminals, dominatingTerminalContribs, classTerminalContribs, superClassContribs, parserAttributeAspectContribs, lexerClassRefDcls, layoutContribs, prettyNamesAccum + on Syntax; + +abstract production nilSyntax +top::Syntax ::= +{ + top.copperGrammarElements = []; +} + +abstract production consSyntax +top::Syntax ::= s1::SyntaxDcl s2::Syntax +{ + top.copperGrammarElements = s1.copperGrammarElements ++ s2.copperGrammarElements; +} + +{-- + - An individual declaration of a concrete syntax element. + -} +closed nonterminal SyntaxDcl with location, sourceGrammar, compareTo, isEqual, cstDcls, cstEnv, cstErrors, cstProds, cstNTProds, cstNormalize, fullName, sortKey, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allProductionNames, allNonterminals, disambiguationClasses, memberTerminals, dominatingTerminalContribs, classTerminalContribs, classTerminals, superClassContribs, superClasses, subClasses, parserAttributeAspectContribs, parserAttributeAspects, lexerClassRefDcls, exportedProds, hasCustomLayout, layoutContribs, layoutTerms, domContribs, subContribs, terminalRegex, prefixSeperator, containingGrammar, prefixesForTerminals, componentGrammarMarkingTerminals, prettyNamesAccum, prettyNames, copperElementReference, copperGrammarElements; + +synthesized attribute sortKey :: String; + +propagate compareTo, isEqual, cstErrors, prefixSeperator on SyntaxDcl; + +aspect default production +top::SyntaxDcl ::= +{ + -- Empty values as defaults + propagate cstProds, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allProductionNames, allNonterminals, disambiguationClasses, memberTerminals, dominatingTerminalContribs, classTerminalContribs, superClassContribs, parserAttributeAspectContribs, lexerClassRefDcls, layoutContribs, prettyNamesAccum; + top.domContribs = error("Internal compiler error: should only ever be demanded of lexer classes"); + top.subContribs = error("Internal compiler error: should only ever be demanded of lexer classes"); + top.exportedProds = error("Internal compiler error: should only ever be demanded of nonterminals"); + top.terminalRegex = error("Internal compiler error: should only ever be demanded of terminals"); + top.hasCustomLayout = false; +} + + +{-- + - A nonterminal. Using Type instead of String, because we'll be doing parameterization later. + - subdcls is empty to start. A transformed version of the tree will move all + - productions for this nonterminal under subdcls. + -} +abstract production syntaxNonterminal +top::SyntaxDcl ::= t::Type subdcls::Syntax exportedProds::[String] exportedLayoutTerms::[String] modifiers::SyntaxNonterminalModifiers +{ + top.fullName = t.typeName; + top.sortKey = "EEE" ++ t.typeName; + top.cstDcls := [pair(t.typeName, top)] ++ subdcls.cstDcls; + top.allNonterminals := [top]; + + top.cstErrors <- if length(searchEnvTree(t.typeName, top.cstEnv)) == 1 then [] + else ["Name conflict with nonterminal " ++ t.typeName]; + top.cstProds := subdcls.cstProds; + top.cstNormalize := + let myProds :: [SyntaxDcl] = searchEnvTree(t.typeName, top.cstNTProds) + in if null(myProds) then [] -- Eliminate "Useless nonterminals" as these are expected in Silver code (non-syntax) + else [ syntaxNonterminal(t, foldr(consSyntax, nilSyntax(), myProds), + exportedProds, exportedLayoutTerms, modifiers, + location=top.location, sourceGrammar=top.sourceGrammar) + ] + end; + + top.exportedProds = exportedProds; + top.hasCustomLayout = modifiers.customLayout.isJust; + top.layoutContribs := map(pair(t.typeName, _), fromMaybe(exportedLayoutTerms, modifiers.customLayout)); + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(t.typeName)); + top.copperGrammarElements = + [ copper:nonterminal_(top.sourceGrammar, top.location, + makeCopperName(t.typeName), t.typeName, makeNTName(t.typeName)) + ] ++ subdcls.copperGrammarElements; + + modifiers.nonterminalName = t.typeName; + + t.boundVariables = t.freeVariables; +} + +{-- + - A terminal, and regular expression. + -} +abstract production syntaxTerminal +top::SyntaxDcl ::= n::String regex::Regex modifiers::SyntaxTerminalModifiers +{ + top.fullName = n; + top.sortKey = "CCC" ++ n; + top.cstDcls := [pair(n, top)]; + top.cstErrors <- + if length(searchEnvTree(n, top.cstEnv)) == 1 then [] + else ["Name conflict with terminal " ++ n]; + + modifiers.terminalName = n; + + top.allTerminals := [top]; + top.allIgnoreTerminals := if modifiers.ignored then [top.fullName] else []; + top.allMarkingTerminals := if modifiers.marking then [top.fullName] else []; + top.classTerminalContribs := modifiers.classTerminalContribs; + top.memberTerminals := [top]; + top.dominatingTerminalContribs := + map(pair(n, _), flatMap((.memberTerminals), modifiers.submits_)) ++ + map(pair(_, top), map((.fullName), flatMap((.memberTerminals), modifiers.dominates_))); + top.terminalRegex = regex; + + -- left(terminal name) or right(string prefix) + production pfx::[String] = searchEnvTree(n, top.prefixesForTerminals); + top.cstErrors <- + if length(pfx) <= 1 then [] + else ["Multiple prefixes for terminal " ++ n]; + + top.cstNormalize := + case modifiers.prefixSeperatorToApply of + | just(sep) -> + [ syntaxTerminal(n, seq(regex, regexLiteral(sep)), modifiers, + location=top.location, sourceGrammar=top.sourceGrammar) + ] + | nothing() -> [top] + end; + + local prettyName :: String = fromMaybe(fromMaybe(n, asPrettyName(regex)), modifiers.prettyName); + top.prettyNamesAccum := [pair(prettyName, n)]; + local disambiguatedPrettyName :: String = + case length(tm:lookup(prettyName, top.prettyNames)) of + | 1 -> prettyName + | _ -> prettyName ++ " (" ++ n ++ ")" + end; + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(n)); + top.copperGrammarElements = + [ copper:terminal_(top.sourceGrammar, top.location, makeCopperName(n), + disambiguatedPrettyName, regex.copperRegex, + modifiers.opPrecedence.isJust, modifiers.opPrecedence.fromJust, + fromMaybe("", modifiers.opAssociation), makeTerminalName(n), + "RESULT = new " ++ makeTerminalName(n) ++ "(lexeme,virtualLocation,(int)getStartRealLocation().getPos(),(int)getEndRealLocation().getPos());tokenList.add(RESULT);\n" ++ modifiers.acode, + map((.copperElementReference), modifiers.lexerClasses), !null(pfx), + copper:elementReference(top.sourceGrammar, top.location, + top.containingGrammar, head(pfx)), + map((.copperElementReference), modifiers.submits_), map((.copperElementReference), modifiers.dominates_)) + ]; +} + +{-- + - A (named) production. Using types for later parameterization. + -} +abstract production syntaxProduction +top::SyntaxDcl ::= ns::NamedSignature modifiers::SyntaxProductionModifiers +{ + top.fullName = ns.fullName; + top.sortKey = "FFF" ++ ns.fullName; + top.cstDcls := [pair(ns.fullName, top)]; + top.allProductions := [top]; + top.allProductionNames := [ns.fullName]; + + modifiers.productionSig = ns; + + production lhsRef :: [Decorated SyntaxDcl] = + searchEnvTree(ns.outputElement.typerep.typeName, top.cstEnv); + production rhsRefs :: [[Decorated SyntaxDcl]] = + lookupStrings(map((.typeName), map((.typerep), ns.inputElements)), top.cstEnv); + + top.cstErrors <- if length(searchEnvTree(ns.fullName, top.cstEnv)) == 1 then [] + else ["Name conflict with production " ++ ns.fullName]; + + top.cstErrors <- if length(lhsRef) == 1 then + case head(lhsRef) of + | syntaxNonterminal(_,_,_,_,_) -> [] + | _ -> ["LHS of production " ++ ns.fullName ++ " is not a nonterminal"] end + else ["Nonterminal " ++ ns.outputElement.typerep.typeName ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from LHS of production " ++ ns.fullName ++ ")"]; + + top.cstErrors <- checkRHS(ns.fullName, map((.typerep), ns.inputElements), rhsRefs); + + top.cstProds := [pair(ns.outputElement.typerep.typeName, top)]; + top.cstNormalize := []; + + top.hasCustomLayout = modifiers.customLayout.isJust; + top.layoutContribs := + map(pair(ns.fullName, _), fromMaybe([], modifiers.customLayout)) ++ + -- The production inherits its LHS nonterminal's layout, unless overridden. + (if top.hasCustomLayout then [] else [pair(ns.fullName, head(lhsRef).fullName)]) ++ + -- All nonterminals on the RHS that export this production inherit this + -- production's layout, unless overriden on the nonterminal. + flatMap( + \ rhsRef::[Decorated SyntaxDcl] -> + case head(rhsRef) of + | syntaxNonterminal(_,_,_,_,_) + when !head(rhsRef).hasCustomLayout && + contains(top.fullName, head(rhsRef).exportedProds) -> + [pair(head(rhsRef).fullName, ns.fullName)] + | _ -> [] + end, + rhsRefs); + + -- Copper doesn't support default layout on nonterminals, so we specify layout on every production. + production prodLayout::[copper:ElementReference] = + map(\dcl::[Decorated SyntaxDcl] -> head(dcl).copperElementReference, + lookupStrings(searchEnvTree(ns.fullName, top.layoutTerms), top.cstEnv)); + + local isTracked :: Boolean = + case head(lhsRef) of + | syntaxNonterminal(nonterminalType(_, _, tracked), _, _, _, _) -> tracked + | _ -> error("LHS is not a nonterminal") + end; + local commaIfArgsOrAnnos :: String = if length(ns.inputElements) + length(ns.namedInputElements)!= 0 then "," else ""; + local originImpl :: String = if isTracked then + "new silver.core.PparsedOriginInfo(common.OriginsUtil.SET_FROM_PARSER_OIT, common.Terminal.createSpan(_children, virtualLocation, (int)_pos.getPos()), common.ConsCell.nil)" ++ commaIfArgsOrAnnos + else ""; + + local code::String = + -- Annoying workaround for if a lambda in an action block needs to capture RESULT when accessing a child. + -- Java complains when we capture something that is non-final. + "final " ++ makeProdName(ns.fullName) ++ " RESULTfinal = new " ++ makeProdName(ns.fullName) ++ "(" ++ originImpl ++ fetchChildren(0, ns.inputElements) ++ insertLocationAnnotation(ns) ++ ");\n" ++ + "RESULT = RESULTfinal;\n" ++ + modifiers.acode; + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(ns.fullName)); + top.copperGrammarElements = + [ copper:production_(top.sourceGrammar, top.location, + makeCopperName(ns.fullName), modifiers.productionPrecedence.isJust, + modifiers.productionPrecedence.fromJust, + modifiers.productionOperator.isJust, + modifiers.productionOperator.fromJust.copperElementReference, code, + head(lhsRef).copperElementReference, + map((.copperElementReference), map(head, rhsRefs)), prodLayout) + ]; +} + +function fetchChildren +String ::= i::Integer ns::[NamedSignatureElement] +{ + return if null(ns) then "" + else if null(tail(ns)) then "_children[" ++ toString(i) ++ "]" + else "_children[" ++ toString(i) ++ "], " ++ fetchChildren(i + 1, tail(ns)); +} + +function insertLocationAnnotation +String ::= ns::Decorated NamedSignature +{ + local pfx :: String = if null(ns.inputElements) then "" else ", "; + + return if null(ns.namedInputElements) then "" + else if length(ns.namedInputElements) > 1 then pfx ++ "multiple_annotation_problem" -- TODO + else if head(ns.namedInputElements).elementName != "silver:core:location" then pfx ++ "unknown_annotation_type_problem" + else pfx ++ "common.Terminal.createSpan(_children, virtualLocation, (int)_pos.getPos())"; +} + + +function lookupStrings +[[a]] ::= t::[String] e::EnvTree +{ + return map(searchEnvTree(_, e), t); +} +function checkRHS +[String] ::= pn::String rhs::[Type] refs::[[Decorated SyntaxDcl]] +{ + return if null(rhs) then [] + else (if length(head(refs)) == 1 then + case head(head(refs)) of + | syntaxNonterminal(_,_,_,_,_) -> [] + | syntaxTerminal(_,_,_) -> [] + | _ -> ["parameter " ++ head(rhs).typeName ++ " of production " ++ pn ++ " is not syntax."] + end + else ["Terminal " ++ head(rhs).typeName ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from RHS of " ++ pn ++ ")"]) + ++ checkRHS(pn, tail(rhs), tail(refs)); +} + +{-- + - A lexer class. Copper doesn't take these, so we'll have to translate away + - the domlist/sublist that appear here. + -} +abstract production syntaxLexerClass +top::SyntaxDcl ::= n::String modifiers::SyntaxLexerClassModifiers +{ + top.fullName = n; + top.sortKey = "AAA" ++ n; + top.cstDcls := [pair(n, top)]; + top.cstErrors <- + if length(searchEnvTree(n, top.cstEnv)) == 1 then [] + else ["Name conflict with lexer class " ++ n]; + modifiers.className = n; + + -- TODO: these attributes are on all SyntaxDcls, but only have meaning for this production + -- that's UUUUGLY. + top.domContribs = modifiers.dominates_; + top.subContribs = modifiers.submits_; + + top.cstNormalize := [top]; + top.superClassContribs := modifiers.superClassContribs; + top.disambiguationClasses := modifiers.disambiguationClasses; + + production terms :: [String] = searchEnvTree(n, top.classTerminals); + top.memberTerminals := flatMap(searchEnvTree(_, top.cstEnv), terms); + + local termsInit::String = + foldr( + \ term::String rest::String -> s"new common.ConsCell(Terminals.${makeCopperName(term)}.num(), ${rest})", + "common.ConsCell.nil", + terms); + top.lexerClassRefDcls := + s" protected common.ConsCell ${makeCopperName(n)} = ${termsInit};\n"; + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(n)); + top.copperGrammarElements = + [ copper:terminalClass(top.sourceGrammar, top.location, makeCopperName(n)) + ]; +} + +{-- + - A parser attribute. The acode initializes it. + -} +abstract production syntaxParserAttribute +top::SyntaxDcl ::= n::String ty::Type acode::String +{ + top.fullName = n; + top.sortKey = "BBB" ++ n; + top.cstDcls := [pair(n, top)]; + top.cstErrors <- if length(searchEnvTree(n, top.cstEnv)) == 1 then [] + else ["Name conflict with parser attribute " ++ n]; + + top.cstNormalize := [top]; + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(n)); + top.copperGrammarElements = + [ copper:parserAttribute(top.sourceGrammar, top.location, + makeCopperName(n), ty.transType, + acode ++ implode("\n", searchEnvTree(n, top.parserAttributeAspects))) + ]; + + -- TODO: technically, there should be no free variables in ty. + ty.boundVariables = []; +} + +{-- + - Additonal action code that should be added to the initialization of + - a parser attribute. + -} +abstract production syntaxParserAttributeAspect +top::SyntaxDcl ::= n::String acode::String +{ + top.fullName = n; + top.sortKey = "BBB" ++ n; + top.cstDcls := []; + top.cstErrors <- + if !null(searchEnvTree(n, top.cstEnv)) then [] + else ["Parser attribute " ++ n ++ " was referenced but this grammar was not included in this parser."]; + + top.cstNormalize := [top]; + + top.parserAttributeAspectContribs := [pair(n, acode)]; + -- The Copper information for these gets picked up by the main syntaxParserAttribute declaration. + top.copperElementReference = error("can't demand copperElementReference of an aspect"); + top.copperGrammarElements = []; +} + +{-- + - A disambiguation group. + - The acode distinguished between the listed terminals. + -} +abstract production syntaxDisambiguationGroup +top::SyntaxDcl ::= n::String terms::[String] applicableToSubsets::Boolean acode::String +{ + top.fullName = n; + top.sortKey = "DDD" ++ n; + top.cstDcls := []; + + local trefs::[[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); + + -- this 'n' here appears to actually hold the line number of the + -- disambiguation, and the grammar. But we arent supposed to know this? + top.cstErrors <- flatMap(\p ::Pair -> + if !null(p.snd) then [] + else ["Terminal " ++ p.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from disambiguation group " ++ n ++ ")"], + zipWith(pair, terms, trefs)); + + top.cstNormalize := [top]; + + top.copperElementReference = copper:elementReference(top.sourceGrammar, + top.location, top.containingGrammar, makeCopperName(n)); + local members::[copper:ElementReference] = + map(\dcl::[Decorated SyntaxDcl] -> head(dcl).copperElementReference, + trefs); + top.copperGrammarElements = + [ copper:disambiguationFunction(top.sourceGrammar, top.location, + makeCopperName(n), acode, members, applicableToSubsets) + ]; +} + +{-- Sort key PREFIXES are as follows: + | syntaxLexerClass(_,_,_) -> AAA + | syntaxParserAttribute(_,_,_) -> BBB + | syntaxTerminal(_,_,_) -> CCC + | syntaxDisambiguationGroup(_,_,_) -> DDD + | syntaxNonterminal(_,_) -> EEE + | syntaxProduction(_,_,_,_) -> FFF +-} + +function sortKeyLte +Boolean ::= l::SyntaxDcl r::SyntaxDcl +{ return l.sortKey <= r.sortKey; } + diff --git a/grammars/silver/compiler/definition/concrete_syntax/ast/TerminalModifiers.sv b/grammars/silver/compiler/definition/concrete_syntax/ast/TerminalModifiers.sv new file mode 100644 index 000000000..9835a61e1 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/ast/TerminalModifiers.sv @@ -0,0 +1,237 @@ +grammar silver:compiler:definition:concrete_syntax:ast; + +import silver:compiler:definition:concrete_syntax:copper as copper; + +monoid attribute ignored :: Boolean with false, ||; +monoid attribute marking :: Boolean with false, ||; +monoid attribute acode :: String; +monoid attribute opPrecedence :: Maybe with nothing(), orElse; +monoid attribute opAssociation :: Maybe with nothing(), orElse; -- TODO type? +monoid attribute prefixSeperator :: Maybe with nothing(), orElse; +monoid attribute prefixSeperatorToApply :: Maybe with nothing(), orElse; +monoid attribute prettyName :: Maybe with nothing(), orElse; +autocopy attribute terminalName :: String; + +monoid attribute dominates_ :: [Decorated SyntaxDcl]; +monoid attribute submits_ :: [Decorated SyntaxDcl]; +monoid attribute lexerClasses :: [Decorated SyntaxDcl]; + +{-- + - Modifiers for terminals. + -} +nonterminal SyntaxTerminalModifiers with compareTo, isEqual, cstEnv, cstErrors, + classTerminalContribs, superClasses, subClasses, ignored, acode, + opPrecedence, opAssociation, prefixSeperator, prefixSeperatorToApply, + componentGrammarMarkingTerminals, marking, terminalName, prettyName, + dominates_, submits_, lexerClasses; + +propagate compareTo, isEqual, cstErrors, classTerminalContribs, ignored, acode, opPrecedence, + opAssociation, prefixSeperator, prefixSeperatorToApply, marking, prettyName, + dominates_, submits_, lexerClasses + on SyntaxTerminalModifiers; + +abstract production consTerminalMod +top::SyntaxTerminalModifiers ::= h::SyntaxTerminalModifier t::SyntaxTerminalModifiers +{ + top.cstErrors <- + if h.prefixSeperator.isJust && t.prefixSeperator.isJust + then ["Multiple prefix separators for terminal " ++ top.terminalName] + else []; +} + +abstract production nilTerminalMod +top::SyntaxTerminalModifiers ::= +{} + + + +{-- + - Modifiers for terminals. + -} +closed nonterminal SyntaxTerminalModifier with compareTo, isEqual, cstEnv, cstErrors, + classTerminalContribs, superClasses, subClasses, dominates_, submits_, + lexerClasses, ignored, acode, opPrecedence, opAssociation, prefixSeperator, + prefixSeperatorToApply, componentGrammarMarkingTerminals, marking, + terminalName, prettyName; + +propagate compareTo, isEqual on SyntaxTerminalModifier; + +{- We default ALL attributes, so we can focus only on those that are interesting in each case... -} +aspect default production +top::SyntaxTerminalModifier ::= +{ + -- Empty values as defaults + propagate cstErrors, classTerminalContribs, dominates_, submits_, + lexerClasses, ignored, acode, opPrecedence, opAssociation, prefixSeperator, + prefixSeperatorToApply, marking, prettyName; +} + +{-- + - If present, it's an ignore terminal, otherwise ordinary terminal. + - Copper has no notion of an ignore terminal, this is translated away. + -} +abstract production termIgnore +top::SyntaxTerminalModifier ::= +{ + top.ignored := true; +} +{-- + - If present, this is a Marking terminal. In the default translation, + - this does nothing. + -} +abstract production termMarking +top::SyntaxTerminalModifier ::= +{ + top.marking := true; +} +{-- + - The terminal's precedence. (Resolves shift/reduce conflicts) + -} +abstract production termPrecedence +top::SyntaxTerminalModifier ::= lvl::Integer +{ + top.opPrecedence := just(lvl); +} +{-- + - The terminal's association. Either left, right, or nonassoc. TODO: a type? + -} +abstract production termAssociation +top::SyntaxTerminalModifier ::= direction::String +{ + top.opAssociation := just(direction); +} +{-- + - The terminal's "pretty name". Used for error messages. + -} +abstract production termPrettyName +top::SyntaxTerminalModifier ::= prettyName::String +{ + top.prettyName := just(prettyName); +} +{-- + - The terminal's lexer classes. + -} +abstract production termClasses +top::SyntaxTerminalModifier ::= cls::[String] +{ + production allCls :: [String] = unions(cls :: lookupStrings(cls, top.superClasses)); + -- Lexer classes not included in this parser are ignored, so library-defined + -- lexer classes can be optionally used without requring the library to be + -- included in the parser. See https://github.com/melt-umn/silver/issues/694 + production allClsRefs :: [Decorated SyntaxDcl] = concat(lookupStrings(allCls, top.cstEnv)); + + top.cstErrors := []; + top.classTerminalContribs := map(pair(_, top.terminalName), allCls); + -- We "translate away" lexer classes dom/sub, by moving that info to the terminals (here) + top.dominates_ := flatMap((.domContribs), allClsRefs); + top.submits_ := flatMap((.subContribs), allClsRefs); + top.lexerClasses := allClsRefs; + + local termSeps :: [Maybe] = map((.prefixSeperator), allClsRefs); + top.prefixSeperator := foldr(orElse, nothing(), termSeps); + top.cstErrors <- + if length(catMaybes(termSeps)) > 1 + then ["Multiple prefix separators for terminal " ++ top.terminalName] + else []; +} +{-- + - The submits list for the terminal. Either lexer classes or terminals. + -} +abstract production termSubmits +top::SyntaxTerminalModifier ::= sub::[String] +{ + production allSubs :: [String] = unions(sub :: lookupStrings(sub, top.subClasses)); + production subRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allSubs, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from submit clause on terminal " ++ top.terminalName ++ ")"], + zipWith(pair, sub, subRefs)); + top.submits_ := map(head, subRefs); +} +{-- + - The dominates list for the terminal. Either lexer classes or terminals. + -} +abstract production termDominates +top::SyntaxTerminalModifier ::= dom::[String] +{ + production allDoms :: [String] = unions(dom :: lookupStrings(dom, top.subClasses)); + production domRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allDoms, top.cstEnv); + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from dominates clause on terminal " ++ top.terminalName ++ ")"], + zipWith(pair, dom, domRefs)); + top.dominates_ := map(head, domRefs); +} +{-- + - The action to take whenever this terminal is SHIFTed. + -} +abstract production termAction +top::SyntaxTerminalModifier ::= acode::String +{ + top.acode := acode; +} +{-- + - The prefix separator to use for the terminal. + - Doesn't seem super useful, but support this on terminals too for consistency + -} +abstract production termPrefixSeperator +top::SyntaxTerminalModifier ::= sep::String +{ + top.prefixSeperator := just(sep); +} +{-- + - The terminals/grammars prefixed by this terminal, for which to use their separator. + -} +abstract production termUsePrefixSeperatorFor +top::SyntaxTerminalModifier ::= terms::[String] grams::[String] +{ + production allTerms :: [String] = terms ++ concat(concat(lookupStrings(grams, top.componentGrammarMarkingTerminals))); + + production termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allTerms, top.cstEnv); + top.prefixSeperatorToApply := + case termRefs of + | [] -> nothing() + | [ref] :: _ -> ref.prefixSeperator + | _ -> error("Lookup failure not caught during error checking") + end; + + top.cstErrors := flatMap(\ a::Pair -> + if !null(a.snd) then [] + else ["Terminal " ++ a.fst ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced from use prefix seperator for clause for terminal)"], + zipWith(pair, terms, termRefs)); + + top.cstErrors <- + flatMap( + \ s::Decorated SyntaxDcl -> + if !s.prefixSeperator.isJust + then ["Terminal " ++ s.fullName ++ " does not define a prefix separator, and must use an explicit terminal to define a prefix."] + else [], + map(head, termRefs)); + + {- TODO: This really should be some sort of warning, not an error, I think. + top.cstErrors <- + if null(allTerms) + then [top.terminalName ++ " does not prefix any terminals"] + else []; + -} + + local distinctSepTermRefs :: [Decorated SyntaxDcl] = + nubBy( + \ s1::Decorated SyntaxDcl s2::Decorated SyntaxDcl -> + case s1.prefixSeperator, s2.prefixSeperator of + | just(ps1), just(ps2) -> ps1 == ps2 + | _, _ -> false + end, + map(head, termRefs)); + top.cstErrors <- + if length(distinctSepTermRefs) > 1 + then ["Terminals " ++ implode(", ", map((.fullName), distinctSepTermRefs)) ++ + " have different prefix separators, so their prefixes must be specified seperately"] + else []; +} + diff --git a/grammars/silver/compiler/definition/concrete_syntax/copper/ElementReference.sv b/grammars/silver/compiler/definition/concrete_syntax/copper/ElementReference.sv new file mode 100644 index 000000000..9d7b0c662 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/copper/ElementReference.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:definition:concrete_syntax:copper; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference +type ElementReference foreign; + +function elementReference +ElementReference ::= sourceGrammar::String location::Location + grammarName::String name::String +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeElementReference(%sourceGrammar%.toString(), %location%, %grammarName%.toString(), %name%.toString())"; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/copper/GrammarElement.sv b/grammars/silver/compiler/definition/concrete_syntax/copper/GrammarElement.sv new file mode 100644 index 000000000..991ca4727 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/copper/GrammarElement.sv @@ -0,0 +1,68 @@ +grammar silver:compiler:definition:concrete_syntax:copper; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.GrammarElement +type GrammarElement foreign; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.DisambiguationFunction +function disambiguationFunction +GrammarElement ::= sourceGrammar::String location::Location id::String + code::String members::[ElementReference] applicableToSubsets::Boolean +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeDisambiguationFunction(%sourceGrammar%.toString(), %location%, %id%.toString(), %code%.toString(), new common.javainterop.ConsCellCollection<>(%members%), %applicableToSubsets%)"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Nonterminal +function nonterminal_ +GrammarElement ::= sourceGrammar::String location::Location id::String + pp::String type_::String +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeNonTerminal(%sourceGrammar%.toString(), %location%, %id%.toString(), %pp%.toString(), %type_%.toString())"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ParserAttribute +function parserAttribute +GrammarElement ::= sourceGrammar::String location::Location id::String + type_::String code::String +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.makeParserAttribute(%sourceGrammar%.toString(), %location%, %id%.toString(), %type_%.toString(), %code%.toString())"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Production +function production_ +GrammarElement ::= sourceGrammar::String location::Location id::String + hasPrecedence::Boolean precedence_::Integer hasOperator::Boolean + operator_::ElementReference code::String lhs::ElementReference + rhs::[ElementReference] prodLayout::[ElementReference] +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeProduction(%sourceGrammar%.toString(), %location%, %id%.toString(), %hasPrecedence% ? %precedence_% : null, %hasOperator% ? (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference)%operator_% : null, %code%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference)%lhs%, new common.javainterop.ConsCellCollection<>(%rhs%), new common.javainterop.ConsCellCollection<>(%prodLayout%))"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Terminal +function terminal_ +GrammarElement ::= sourceGrammar::String location::Location id::String + pp::String regex::Regex hasPrecedence::Boolean precedence_::Integer + associativity::String type_::String code::String + classes_::[ElementReference] hasPrefix::Boolean prefix_::ElementReference + submits_::[ElementReference] dominates_::[ElementReference] +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeTerminal(%sourceGrammar%.toString(), %location%, %id%.toString(), %pp%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Regex)%regex%, %hasPrecedence% ? %precedence_% : null, %associativity%.toString(), %type_%.toString(), %code%.toString(), new common.javainterop.ConsCellCollection<>(%classes_%), %hasPrefix% ? (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference)%prefix_% : null, new common.javainterop.ConsCellCollection<>(%submits_%), new common.javainterop.ConsCellCollection<>(%dominates_%))"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.TerminalClass +function terminalClass +GrammarElement ::= sourceGrammar::String location::Location id::String +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeTerminalClass(%sourceGrammar%.toString(), %location%, %id%.toString())"; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/copper/Misc.sv b/grammars/silver/compiler/definition/concrete_syntax/copper/Misc.sv new file mode 100644 index 000000000..01f805e98 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/copper/Misc.sv @@ -0,0 +1,72 @@ +grammar silver:compiler:definition:concrete_syntax:copper; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ParserBean +type ParserBean foreign; + +function parserBean +ParserBean ::= sourceGrammar::String location::Location id::String + name::String startSymbol::ElementReference + startLayout::[ElementReference] interfaceNames::[String] parserClassAuxCode::String + parserInitCode::String preambleCode::String grammar_::Grammar +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeParserBean(%sourceGrammar%.toString(), %location%, %id%.toString(), %name%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference)%startSymbol%, new common.javainterop.ConsCellCollection<>(%startLayout%), new common.javainterop.ConsCellCollection<>(%interfaceNames%), %parserClassAuxCode%.toString(), %parserInitCode%.toString(), %preambleCode%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Grammar)%grammar_%)"; +} + +function extendedParserBean +ParserBean ::= sourceGrammar::String location::Location id::String + name::String startSymbol::ElementReference + startLayout::[ElementReference] interfaceNames::[String] parserClassAuxCode::String + parserInitCode::String preambleCode::String hostGrammar::Grammar + extGrammar::Grammar +{ + return error("copper FFI function"); +} foreign { + "java" : return "common.CopperUtil.makeExtendedParserBean(%sourceGrammar%.toString(), %location%, %id%.toString(), %name%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CopperElementReference)%startSymbol%, new common.javainterop.ConsCellCollection<>(%startLayout%), new common.javainterop.ConsCellCollection<>(%interfaceNames%), %parserClassAuxCode%.toString(), %parserInitCode%.toString(), %preambleCode%.toString(), (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Grammar)%hostGrammar%, (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Grammar)%extGrammar%)"; +} + +function compileParserBeanT +IOVal ::= parser_::ParserBean packageName::String parserName::String + runMDA::Boolean outFile::String dumpHtml::Boolean dumpHtmlTo::String + xmlDump::Boolean io::IOToken +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.compile((edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ParserBean)%parser_%, %packageName%.toString(), %parserName%.toString(), %runMDA%, %outFile%.toString(), %dumpHtml%, %dumpHtmlTo%.toString(), %xmlDump%, %io%)"; +} + +abstract production compileParserBean +top::IO ::= parser_::ParserBean packageName::String + parserName::String runMDA::Boolean outFile::String dumpHtml::Boolean + dumpHtmlTo::String xmlDump::Boolean +{ + local val::IOVal = compileParserBeanT(parser_, packageName, + parserName, runMDA, outFile, dumpHtml, dumpHtmlTo, xmlDump, top.stateIn); + top.stateOut = val.io; + top.stateVal = val.iovalue; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Grammar +type Grammar foreign; + +function grammar_ +Grammar ::= sourceGrammar::String location::Location id::String + grammarElements::[GrammarElement] +-- TODO: setGrammarLayout? +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.makeGrammar(%sourceGrammar%.toString(), %location%, %id%.toString(), new common.javainterop.ConsCellCollection<>(%grammarElements%))"; +} + +function extensionGrammar +Grammar ::= sourceGrammar::String location::Location id::String + grammarElements::[GrammarElement] markingTerminals::[ElementReference] + bridgeProductions::[ElementReference] + glueDisambiguationFunctions::[ElementReference] +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.makeExtensionGrammar(%sourceGrammar%.toString(), %location%, %id%.toString(), new common.javainterop.ConsCellCollection<>(%grammarElements%), new common.javainterop.ConsCellCollection<>(%markingTerminals%), new common.javainterop.ConsCellCollection<>(%bridgeProductions%), new common.javainterop.ConsCellCollection<>(%glueDisambiguationFunctions%))"; +} diff --git a/grammars/silver/compiler/definition/concrete_syntax/copper/Regex.sv b/grammars/silver/compiler/definition/concrete_syntax/copper/Regex.sv new file mode 100644 index 000000000..c0b99d360 --- /dev/null +++ b/grammars/silver/compiler/definition/concrete_syntax/copper/Regex.sv @@ -0,0 +1,85 @@ +grammar silver:compiler:definition:concrete_syntax:copper; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Regex +type Regex foreign; + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.EmptyStringRegex +function emptyStringRegex +Regex ::= +{ + return error("copper FFI function"); +} foreign { + "java": return "new edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.EmptyStringRegex()"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ChoiceRegex +function choiceRegex +Regex ::= subexps::[Regex] +{ + return error("copper FFI function"); +} foreign { + "java": return "new edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ChoiceRegex().addSubexps(new java.util.ArrayList(new common.javainterop.ConsCellCollection<>(%subexps%)))"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ConcatenationRegex +function concatenationRegex +Regex ::= subexps::[Regex] +{ + return error("copper FFI function"); +} foreign { + "java": return "new edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.ConcatenationRegex().addSubexps(new java.util.ArrayList(new common.javainterop.ConsCellCollection<>(%subexps%)))"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.KleeneStarRegex +function kleeneStarRegex +Regex ::= r::Regex +{ + return error("copper FFI function"); +} foreign { + "java": return "new edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.KleeneStarRegex((edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Regex) %r%)"; +} + +-- edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CharacterSetRegex +function characterSetRegex +Regex ::= cs::CharSet +{ + return error("copper FFI function"); +} foreign { + "java": return "%cs%"; +} + +-- common.copperutil.CharSet +type CharSet foreign; + +-- This should always be called with a single-char string. +function singleChar +CharSet ::= c::String +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.makeSingleChar(%c%.toString())"; +} + +function invertCharSet +CharSet ::= inner::CharSet +{ + return error("copper FFI function"); +} foreign { + "java": return "((edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CharacterSetRegex)%inner%).invert()"; +} + +function charRange +CharSet ::= lower::String upper::String +{ + return error("copper FFI function"); +} foreign { + "java": return "common.CopperUtil.makeCharRange(%lower%.toString(), %upper%.toString())"; +} + +function unionCharSets +CharSet ::= l::CharSet r::CharSet +{ + return error("copper FFI function"); +} foreign { + "java": return "edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CharacterSetRegex.union((edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CharacterSetRegex)%l%, (edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.CharacterSetRegex)%r%)"; +} diff --git a/grammars/silver/compiler/definition/core/AGDcl.sv b/grammars/silver/compiler/definition/core/AGDcl.sv new file mode 100644 index 000000000..573cb7633 --- /dev/null +++ b/grammars/silver/compiler/definition/core/AGDcl.sv @@ -0,0 +1,104 @@ +grammar silver:compiler:definition:core; + +{-- + - Top-level declarations of a Silver grammar. The "meat" of a file. + -} +nonterminal AGDcls with config, grammarName, env, location, unparse, errors, defs, occursDefs, moduleNames, compiledGrammars, grammarDependencies, jarName; +nonterminal AGDcl with config, grammarName, env, location, unparse, errors, defs, occursDefs, moduleNames, compiledGrammars, grammarDependencies, jarName; + +flowtype decorate {config, grammarName, env, flowEnv, compiledGrammars, grammarDependencies} on AGDcls, AGDcl; +flowtype forward {decorate} on AGDcls, AGDcl; +flowtype errors {decorate} on AGDcls, AGDcl; +flowtype defs {decorate} on AGDcls, AGDcl; +flowtype occursDefs {decorate} on AGDcls, AGDcl; +flowtype jarName {decorate} on AGDcls, AGDcl; + +propagate errors, moduleNames, jarName on AGDcls, AGDcl; +propagate defs, occursDefs on AGDcls; + +concrete production nilAGDcls +top::AGDcls ::= +{ + top.unparse = ""; +} + +concrete production consAGDcls +top::AGDcls ::= h::AGDcl t::AGDcls +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + + top.errors <- warnIfMultJarName(h.jarName, t.jarName, top.location); +} + +-------- +-- AGDcl + +{-- + - A semantically meaningless declaration. Does nothing. + - Used for: (1) 'nil' counterpart to appendAgDcl + -} +abstract production emptyAGDcl +top::AGDcl ::= +{ + top.unparse = ""; +} + +abstract production errorAGDcl +top::AGDcl ::= e::[Message] +{ + top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; + top.errors <- e; +} + +abstract production defsAGDcl +top::AGDcl ::= d::[Def] +{ + top.unparse = s"{- Defs:\n${hackUnparse(d)} -}"; + top.defs := d; +} + +{-- + - Permits extensions to expand an AGDcl into a series of AGDcl's. + -} +abstract production appendAGDcl +top::AGDcl ::= h::AGDcl t::AGDcl +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + propagate defs, occursDefs; + + top.errors <- warnIfMultJarName(h.jarName, t.jarName, top.location); +} + +function makeAppendAGDclOfAGDcls +AGDcl ::= dcls::AGDcls +{ + return case dcls of + | nilAGDcls(location=l) -> emptyAGDcl(location=l) + | consAGDcls(dcl, rest, location=l) -> appendAGDcl(dcl, makeAppendAGDclOfAGDcls(rest), location=l) + end; +} + +abstract production jarNameDcl +top::AGDcl ::= n::Name +{ + top.unparse = "jarName " ++ n.unparse; + top.jarName <- just(n.name); +} + +aspect default production +top::AGDcl ::= +{ + propagate moduleNames, defs, occursDefs, jarName; +} + +function warnIfMultJarName +[Message] ::= n1::Maybe n2::Maybe loc::Location +{ + return if n1.isJust && n2.isJust + then [wrn(loc, "Duplicate specification of jar name: " ++ + n1.fromJust ++ " and " ++ n2.fromJust)] + else []; +} + +-- All AGDcls have their own file, or modification. None here. + diff --git a/grammars/silver/compiler/definition/core/Annotation.sv b/grammars/silver/compiler/definition/core/Annotation.sv new file mode 100644 index 000000000..7109688d9 --- /dev/null +++ b/grammars/silver/compiler/definition/core/Annotation.sv @@ -0,0 +1,31 @@ +grammar silver:compiler:definition:core; + +terminal Annotation_kwd 'annotation' lexer classes {KEYWORD}; + +disambiguate Annotation_kwd, IdLower_t { pluck Annotation_kwd; } + +concrete production annotationDcl +top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "annotation " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + production fName :: String = top.grammarName ++ ":" ++ a.name; + + top.defs := [annoDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "The name '" ++ fName ++ "' is already bound.")] + else []; + top.errors <- + if indexOf(":", a.name) == -1 then [] + else [err(a.location, "The name '" ++ a.name ++ "' must not be qualified.")]; + + top.errors <- tl.errorsTyVars; +} + + diff --git a/grammars/silver/compiler/definition/core/AspectDcl.sv b/grammars/silver/compiler/definition/core/AspectDcl.sv new file mode 100644 index 000000000..9a5739f32 --- /dev/null +++ b/grammars/silver/compiler/definition/core/AspectDcl.sv @@ -0,0 +1,321 @@ +grammar silver:compiler:definition:core; + +nonterminal AspectProductionSignature with config, grammarName, env, location, unparse, errors, defs, realSignature, namedSignature, signatureName; +nonterminal AspectProductionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement, realSignature; + +nonterminal AspectFunctionSignature with config, grammarName, env, location, unparse, errors, defs, realSignature, namedSignature, signatureName; +nonterminal AspectFunctionLHS with config, grammarName, env, location, unparse, errors, defs, realSignature, outputElement; + +nonterminal AspectRHS with config, grammarName, env, location, unparse, errors, defs, inputElements, realSignature; +nonterminal AspectRHSElem with config, grammarName, env, location, unparse, errors, defs, realSignature, inputElements, deterministicCount; + +flowtype forward {realSignature, grammarName, env, flowEnv} on AspectProductionSignature, AspectProductionLHS, AspectFunctionSignature, AspectFunctionLHS, AspectRHS; +flowtype forward {deterministicCount, realSignature, grammarName, env, flowEnv} on AspectRHSElem; + +{-- + - The signature elements from the fun/produciton being aspected. + -} +autocopy attribute realSignature :: [NamedSignatureElement]; + +propagate errors on AspectProductionSignature, AspectProductionLHS, AspectFunctionSignature, AspectFunctionLHS, AspectRHS, AspectRHSElem; + +concrete production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + top.unparse = "aspect production " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; + + top.defs := + if null(body.productionAttributes) then [] + else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; + + production namedSig :: NamedSignature = ns.namedSignature; + + production attribute realSig :: NamedSignature; + realSig = if id.lookupValue.found + then freshenNamedSignature(id.lookupValue.dcl.namedSignature) + else bogusNamedSignature(); + + -- Making sure we're aspecting a production is taken care of by type checking. + + top.errors <- id.lookupValue.errors; + + production attribute sigDefs :: [Def] with ++; + sigDefs := ns.defs; + + ns.signatureName = id.lookupValue.fullName; + ns.env = newScopeEnv(sigDefs, top.env); + ns.realSignature = if null(id.lookupValue.dcls) then [] else [realSig.outputElement] ++ realSig.inputElements; + + local attribute prodAtts :: [Def]; + prodAtts = if id.lookupValue.found + then defsFromPADcls(getProdAttrs(id.lookupValue.fullName, top.env), namedSig) + else []; + + local contextSigDefs::[Def] = + flatMap( + \ c::Context -> c.contextSigDefs(realSig, top.grammarName, top.location), + realSig.contexts); + local contextSigOccursDefs::[OccursDclInfo] = + flatMap( + \ c::Context -> c.contextSigOccursDefs(realSig, top.grammarName, top.location), + realSig.contexts); + local sourceGrammar::String = + if id.lookupValue.found + then id.lookupValue.dcl.sourceGrammar + -- Default since we need to supply something, in case the production doesn't exist. + else top.grammarName; + + body.env = + occursEnv(contextSigOccursDefs, + newScopeEnv(body.defs ++ sigDefs ++ contextSigDefs, + newScopeEnv(prodAtts, top.env))); + body.frame = aspectProductionContext(namedSig, myFlowGraph, sourceGrammar=sourceGrammar); -- graph from flow:env +} action { + insert semantic token IdFnProd_t at id.location; + sigNames = []; +} + +concrete production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + top.unparse = "aspect function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; + + top.defs := + if null(body.productionAttributes) then [] + else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; + + production namedSig :: NamedSignature = ns.namedSignature; + + production attribute realSig :: NamedSignature; + realSig = if id.lookupValue.found + then freshenNamedSignature(id.lookupValue.dcl.namedSignature) + else bogusNamedSignature(); + + -- Making sure we're aspecting a function is taken care of by type checking. + + top.errors <- id.lookupValue.errors; + + production attribute sigDefs :: [Def] with ++; + sigDefs := ns.defs; + + ns.signatureName = id.lookupValue.fullName; + ns.env = newScopeEnv(sigDefs, top.env); + ns.realSignature = if null(id.lookupValue.dcls) then [] else [realSig.outputElement] ++ realSig.inputElements; + + local attribute prodAtts :: [Def]; + prodAtts = if id.lookupValue.found + then defsFromPADcls(getProdAttrs(id.lookupValue.fullName, top.env), namedSig) + else []; + + local contextSigDefs::[Def] = + flatMap( + \ c::Context -> c.contextSigDefs(realSig, top.grammarName, top.location), + realSig.contexts); + local contextSigOccursDefs::[OccursDclInfo] = + flatMap( + \ c::Context -> c.contextSigOccursDefs(realSig, top.grammarName, top.location), + realSig.contexts); + local sourceGrammar::String = + if id.lookupValue.found + then id.lookupValue.dcl.sourceGrammar + -- Default since we need to supply something, in case the production doesn't exist. + else top.grammarName; + + body.env = + occursEnv(contextSigOccursDefs, + newScopeEnv(body.defs ++ sigDefs ++ contextSigDefs, + newScopeEnv(prodAtts, top.env))); + body.frame = aspectFunctionContext(namedSig, myFlowGraph, sourceGrammar=sourceGrammar); -- graph from flow:env +} action { + insert semantic token IdFnProd_t at id.location; + sigNames = []; +} + +concrete production aspectProductionSignature +top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS +{ + top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; + + propagate defs; + + top.namedSignature = + namedSignature( + top.signatureName, nilContext(), + foldNamedSignatureElements(rhs.inputElements), + lhs.outputElement, + foldNamedSignatureElements(annotationsForNonterminal(lhs.outputElement.typerep, top.env))); + + lhs.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; + rhs.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); +} action { + sigNames = foldNamedSignatureElements(lhs.outputElement :: rhs.inputElements).elementNames; +} + +concrete production aspectProductionLHSNone +top::AspectProductionLHS ::= '_' +{ + top.unparse = "_"; + forwards to aspectProductionLHSId(name("p_top", top.location), location=top.location); +} + +concrete production aspectProductionLHSId +top::AspectProductionLHS ::= id::Name +{ + top.unparse = id.unparse; + + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + forwards to aspectProductionLHSFull(id, rType, location=top.location); +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +concrete production aspectProductionLHSTyped +top::AspectProductionLHS ::= id::Name '::' t::TypeExpr +{ + top.unparse = id.unparse; + + top.errors <- t.errors; + + forwards to aspectProductionLHSFull(id, t.typerep, location=top.location); +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +abstract production aspectProductionLHSFull +top::AspectProductionLHS ::= id::Name t::Type +{ + top.unparse = id.unparse ++ "::" ++ prettyType(t); + + production attribute fName :: String; + fName = if null(top.realSignature) then id.name else head(top.realSignature).elementName; + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + top.outputElement = namedSignatureElement(id.name, t); + + top.defs := [aliasedLhsDef(top.grammarName, id.location, fName, performSubstitution(t, top.upSubst), id.name)]; + + top.errors <- if length(getValueDclInScope(id.name, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + else []; +} + +concrete production aspectRHSElemNil +top::AspectRHS ::= +{ + top.unparse = ""; + + propagate defs; + top.inputElements = []; +} + +concrete production aspectRHSElemCons +top::AspectRHS ::= h::AspectRHSElem t::AspectRHS +{ + top.unparse = h.unparse ++ " " ++ t.unparse; + + propagate defs; + + top.inputElements = h.inputElements ++ t.inputElements; + + h.deterministicCount = length(t.inputElements); + h.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; + t.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); +} + +concrete production aspectRHSElemNone +top::AspectRHSElem ::= '_' +{ + top.unparse = "_"; + + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + forwards to aspectRHSElemFull( + name("p_" ++ toString(top.deterministicCount), $1.location), + rType, + location=top.location); +} + +concrete production aspectRHSElemId +top::AspectRHSElem ::= id::Name +{ + top.unparse = id.unparse; + + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + top.errors <- [wrn(top.location, "Giving just a name '" ++ id.name ++ "' is deprecated in aspect signature. Please explicitly use a name and type.")]; + + forwards to aspectRHSElemFull(id, rType, location=top.location); +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +concrete production aspectRHSElemTyped +top::AspectRHSElem ::= id::Name '::' t::TypeExpr +{ + top.unparse = id.unparse ++ "::" ++ t.unparse; + + top.errors <- t.errors; + + forwards to aspectRHSElemFull(id, t.typerep, location=top.location); +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +abstract production aspectRHSElemFull +top::AspectRHSElem ::= id::Name t::Type +{ + top.unparse = id.unparse ++ "::" ++ prettyType(t); + + production attribute fName :: String; + fName = if null(top.realSignature) then id.name else head(top.realSignature).elementName; + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + top.inputElements = [namedSignatureElement(id.name, t)]; + + top.defs := [aliasedChildDef(top.grammarName, id.location, fName, performSubstitution(t, top.upSubst), id.name)]; + + top.errors <- if length(getValueDclInScope(id.name, top.env)) > 1 + then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] + else []; +} + +concrete production aspectFunctionSignature +top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS +{ + top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; + + propagate defs; + + top.namedSignature = + namedSignature( + top.signatureName, nilContext(), + foldNamedSignatureElements(rhs.inputElements), + lhs.outputElement, + -- For the moment, functions do not have named parameters (hence, nilNamedSignatureElement) + nilNamedSignatureElement()); + + lhs.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; + rhs.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); +} + +concrete production functionLHSType +top::AspectFunctionLHS ::= t::TypeExpr +{ + top.unparse = t.unparse; + + production attribute fName :: String; + fName = if null(top.realSignature) then "_NULL_" else head(top.realSignature).elementName; + production attribute rType :: Type; + rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; + + top.outputElement = namedSignatureElement(fName, t.typerep); + + -- TODO: this needs thinking. is it broken? maybe __return? or wait, it's doing that automatically isnt it... + top.defs := [aliasedLhsDef(top.grammarName, t.location, fName, performSubstitution(t.typerep, top.upSubst), fName)]; +} diff --git a/grammars/silver/compiler/definition/core/AttributeDcl.sv b/grammars/silver/compiler/definition/core/AttributeDcl.sv new file mode 100644 index 000000000..a96deddd8 --- /dev/null +++ b/grammars/silver/compiler/definition/core/AttributeDcl.sv @@ -0,0 +1,45 @@ +grammar silver:compiler:definition:core; + +concrete production attributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "inherited attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + top.defs := [inhDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; +} + +concrete production attributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "synthesized attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + top.defs := [synDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; +} diff --git a/grammars/silver/compiler/definition/core/Attributes.sv b/grammars/silver/compiler/definition/core/Attributes.sv new file mode 100644 index 000000000..148593213 --- /dev/null +++ b/grammars/silver/compiler/definition/core/Attributes.sv @@ -0,0 +1,12 @@ +grammar silver:compiler:definition:core; + +{-- + - The grammar containing this tree. + -} +autocopy attribute grammarName :: String; + +{-- + - The name to use for the generated .jar if not overridden by -o command-line option. + -} +monoid attribute jarName :: Maybe with nothing(), orElse; + diff --git a/grammars/silver/compiler/definition/core/BlockContext.sv b/grammars/silver/compiler/definition/core/BlockContext.sv new file mode 100644 index 000000000..c235532e4 --- /dev/null +++ b/grammars/silver/compiler/definition/core/BlockContext.sv @@ -0,0 +1,153 @@ +grammar silver:compiler:definition:core; + +import silver:compiler:definition:flow:driver only ProductionGraph; + +{-- + - Permissions management information for certain features that can appear in production + - statements, etc. i.e. "can forward/return/pluck?" + -} +nonterminal BlockContext with permitReturn, permitForward, permitProductionAttributes, + permitLocalAttributes, lazyApplication, hasFullSignature, hasPartialSignature, + fullName, lhsNtName, signature, sourceGrammar, flowGraph; + + +{-- Are 'return' equations allowed in this context? -} +synthesized attribute permitReturn :: Boolean; +{-- Are 'forwards to' equations allowed in this context? -} +synthesized attribute permitForward :: Boolean; +{-- Are 'local' equations allowed in this context? -} +synthesized attribute permitLocalAttributes :: Boolean; +{-- Are 'production attribute' equations allowed in this context? + DISTINCT from locals, due to action blocks. -} +synthesized attribute permitProductionAttributes :: Boolean; + +{-- + - Whether the signature includes the name of a LHS. + - Not strictly necessary to set partial to true, but to be expected... + - REFACTORING NOTES: Used to: + - 1. Figure out how to get a production graph (REPLACE THIS) (i.e. function vs production) + - 2. Ignore checking LHS Inhs in functions, which don't have LHS inhs... + -} +synthesized attribute hasFullSignature :: Boolean; +{-- + - Whether the signature includes the type of a LHS & name/type pairs for RHS. + - And the name. e.g. top.frame.fullName + - REFACTORING NOTES: Used to: + - 1. Decide if syn eq should be exported by NT alone (default eq) or OCC/NT (normal syn eq) + - 2. Decide if we need to look at deps of syn eqs (i.e. default eqs don't get checked locally) + - 3. Decide to emit a default equation or synthesized equation + -} +synthesized attribute hasPartialSignature :: Boolean; +{-- + - Whether expressions should be evaluated lazily in this context. + - (False for action blocks, for example.) + -} +synthesized attribute lazyApplication :: Boolean; +{-- + - The full name of the LHS nonterminal. + - ONLY ACCESSIBLE IF `top.hasFullSignature` is true! + -} +synthesized attribute lhsNtName :: String; +{-- + - The signature of the current context. + - Not always sensible, depending on context. Needs care in use. + - TODO: figure out a way to guard accesses maybe? + -} +synthesized attribute signature :: NamedSignature; +{-- + - The flow graph for the current context. + -} +synthesized attribute flowGraph :: ProductionGraph; + +{- fullName on BlockContext: + - Used to: + - 1. Name locals. + - 2. Equations to emit flowDefs + - 3. Exprs to emit anon flowDefs. + - + - sourceGrammar on BlockContext: + - Used to: + - 1. Do isExportedBy checks, finding prod origin grammar. + -} + + +aspect default production +top::BlockContext ::= +{ + top.lhsNtName = error("LHS NT accessed for non-production"); + -- most restrictive possible + top.permitReturn = false; + top.permitForward = false; + top.permitProductionAttributes = false; + top.permitLocalAttributes = false; + top.lazyApplication = true; + top.hasPartialSignature = false; + top.hasFullSignature = false; + + -- always required: fullName, signature, flowGraph, usePassedOriginsContext +} + +abstract production functionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.fullName = sig.fullName; + top.lhsNtName = "::nolhs"; -- unfortunately this is sometimes accessed, and a dummy value works okay + top.signature = sig; + top.flowGraph = g; + + top.permitReturn = true; + top.hasPartialSignature = true; + top.permitProductionAttributes = true; + top.permitLocalAttributes = true; +} + +abstract production productionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.fullName = sig.fullName; + top.lhsNtName = sig.outputElement.typerep.typeName; + top.signature = sig; + top.flowGraph = g; + + top.permitForward = true; + top.hasPartialSignature = true; + top.hasFullSignature = true; + top.permitProductionAttributes = true; + top.permitLocalAttributes = true; +} + + -- This was necessitated by origins work, but is probably also generally useful. +abstract production inLambdaContext +top::BlockContext ::= containingContext::BlockContext +{ + forwards to containingContext; +} + +abstract production aspectFunctionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.permitReturn = false; + forwards to functionContext(sig, g, sourceGrammar=top.sourceGrammar); +} + +abstract production aspectProductionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.permitForward = false; + forwards to productionContext(sig, g, sourceGrammar=top.sourceGrammar); +} + +abstract production globalExprContext +top::BlockContext ::= fn::String ctxs::Contexts ty::Type g::ProductionGraph +{ + top.fullName = fn; + top.signature = globalSignature(fn, ctxs, ty); + top.flowGraph = g; +} + +abstract production bogusContext +top::BlockContext ::= g::ProductionGraph +{ + forwards to globalExprContext("_NULL_", nilContext(), errorType(), g, sourceGrammar=top.sourceGrammar); +} + diff --git a/grammars/silver/compiler/definition/core/ClassDcl.sv b/grammars/silver/compiler/definition/core/ClassDcl.sv new file mode 100644 index 000000000..078690eec --- /dev/null +++ b/grammars/silver/compiler/definition/core/ClassDcl.sv @@ -0,0 +1,182 @@ +grammar silver:compiler:definition:core; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; + +concrete production typeClassDcl +top::AGDcl ::= 'class' cl::ConstraintList '=>' id::QNameType var::TypeExpr '{' body::ClassBody '}' +{ + top.unparse = s"class ${cl.unparse} => ${id.unparse} ${var.unparse}\n{\n${body.unparse}\n}"; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production tv :: TyVar = + case var.typerep.freeVariables of + | v :: _ -> v + | _ -> freshTyVar(starKind()) + end; + production supers::[Context] = cl.contexts; -- *Direct* super classes only, not transitive + production boundVars::[TyVar] = [tv]; + + top.defs := classDef(top.grammarName, id.location, fName, supers, tv, var.typerep.kindrep, body.classMembers) :: body.defs; + + -- id *should* be just a Name, but it has to be a QNameType to avoid a reduce/reduce conflict + top.errors <- + if indexOf(":", id.name) == -1 then [] + else [err(id.location, "Class name must be unqualified.")]; + + -- Here we ensure that the type is just a type *variable* + top.errors <- var.errorsTyVars; + + -- Redefinition check of the name + top.errors <- + if length(getTypeDclAll(fName, top.env)) > 1 + then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- + if isLower(substring(0,1,id.name)) + then [err(id.location, "Types must be capitalized. Invalid class name " ++ id.name)] + else []; + + top.errors <- + if contains(fName, catMaybes(map((.contextClassName), transitiveSuperContexts(top.env, var.typerep, [], fName)))) + then [err(top.location, "Cycle exists in superclass relationships.")] + else []; + + production attribute headPreDefs :: [Def] with ++; + headPreDefs := []; + + production attribute headDefs :: [Def] with ++; + headDefs := cl.defs; + headDefs <- [currentInstDef(top.grammarName, id.location, fName, var.typerep)]; + + cl.constraintPos = classPos(fName, var.freeVariables); + cl.env = newScopeEnv(headPreDefs, top.env); + + var.env = cl.env; + + body.env = occursEnv(cl.occursDefs, newScopeEnv(headDefs, cl.env)); + body.constraintEnv = cl.env; + body.classHead = instContext(fName, var.typerep); + body.frameContexts = supers; +} action { + insert semantic token IdTypeClassDcl_t at id.baseNameLoc; +} + +concrete production typeClassDclNoCL +top::AGDcl ::= 'class' id::QNameType var::TypeExpr '{' body::ClassBody '}' +{ + top.unparse = s"class ${id.unparse} ${var.unparse}\n{\n${body.unparse}\n}"; + + forwards to typeClassDcl($1, nilConstraint(location=top.location), '=>', id, var, $4, body, $6, location=top.location); +} action { + insert semantic token IdTypeClassDcl_t at id.baseNameLoc; +} + +autocopy attribute classHead::Context; +autocopy attribute constraintEnv::Decorated Env; +autocopy attribute frameContexts::[Context]; -- Only used for computing frame in members + +nonterminal ClassBody with + config, grammarName, env, defs, flowEnv, flowDefs, location, unparse, errors, lexicalTypeVariables, lexicalTyVarKinds, classHead, constraintEnv, frameContexts, compiledGrammars, classMembers; +nonterminal ClassBodyItem with + config, grammarName, env, defs, flowEnv, flowDefs, location, unparse, errors, lexicalTypeVariables, lexicalTyVarKinds, classHead, constraintEnv, frameContexts, compiledGrammars, classMembers; + +propagate flowDefs, errors, lexicalTypeVariables, lexicalTyVarKinds on ClassBody, ClassBodyItem; +propagate defs on ClassBody; + +concrete production consClassBody +top::ClassBody ::= h::ClassBodyItem t::ClassBody +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + top.classMembers = h.classMembers ++ t.classMembers; +} +concrete production nilClassBody +top::ClassBody ::= +{ + top.unparse = ""; + top.classMembers = []; +} + +concrete production classBodyItem +top::ClassBodyItem ::= id::Name '::' ty::TypeExpr ';' +{ + forwards to constraintClassBodyItem(id, $2, nilConstraint(location=top.location), '=>', ty, $4, location=top.location); +} action { + insert semantic token IdTypeClassMemberDcl_t at id.location; +} + +concrete production constraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr ';' +{ + top.unparse = s"${id.name} :: ${cl.unparse} => ${ty.unparse};"; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production boundVars :: [TyVar] = + setUnionTyVarsAll(top.classHead.freeVariables :: map((.freeVariables), cl.contexts) ++ [ty.typerep.freeVariables]); + top.classMembers = [pair(fName, false)]; + + cl.constraintPos = + case top.classHead of + | instContext(cls, _) -> classMemberPos(cls, boundVars) + | _ -> error("Class head is not an instContext") + end; + cl.env = top.constraintEnv; + + top.defs := [classMemberDef(top.grammarName, top.location, fName, boundVars, top.classHead, cl.contexts, ty.typerep)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + else []; +} action { + insert semantic token IdTypeClassMemberDcl_t at id.location; +} + +concrete production defaultClassBodyItem +top::ClassBodyItem ::= id::Name '::' ty::TypeExpr '=' e::Expr ';' +{ + forwards to defaultConstraintClassBodyItem(id, $2, nilConstraint(location=top.location), '=>', ty, $4, e, $6, location=top.location); +} action { + insert semantic token IdTypeClassMemberDcl_t at id.location; +} + +concrete production defaultConstraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr '=' e::Expr ';' +{ + top.unparse = s"${id.name} :: ${cl.unparse} => ${ty.unparse} = ${e.unparse};"; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production boundVars :: [TyVar] = + setUnionTyVarsAll(top.classHead.freeVariables :: map((.freeVariables), cl.contexts) ++ [ty.typerep.freeVariables]); + top.classMembers = [pair(fName, true)]; + + cl.constraintPos = + case top.classHead of + | instContext(cls, _) -> classMemberPos(cls, boundVars) + | _ -> error("Class head is not an instContext") + end; + cl.env = top.constraintEnv; + + e.isRoot = true; + e.originRules = []; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); + + e.frame = globalExprContext(fName, foldContexts(top.frameContexts ++ cl.contexts), ty.typerep, myFlowGraph, sourceGrammar=top.grammarName); + e.env = occursEnv(cl.occursDefs, newScopeEnv(cl.defs, top.env)); + + top.defs := [classMemberDef(top.grammarName, top.location, fName, boundVars, top.classHead, cl.contexts, ty.typerep)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + else []; +} action { + insert semantic token IdTypeClassMemberDcl_t at id.location; +} + +-- TODO: Defaults diff --git a/grammars/silver/compiler/definition/core/DclInfo.sv b/grammars/silver/compiler/definition/core/DclInfo.sv new file mode 100644 index 000000000..5bbee786a --- /dev/null +++ b/grammars/silver/compiler/definition/core/DclInfo.sv @@ -0,0 +1,137 @@ +grammar silver:compiler:definition:core; + +import silver:compiler:modification:copper only terminalIdReference; + +{-- + - The production a variable reference should forward to for this type of value + -} +synthesized attribute refDispatcher :: (Expr ::= PartiallyDecorated QName Location) occurs on ValueDclInfo; +{-- + - The production an "assignment" should forward to for this type of value + -} +synthesized attribute defDispatcher :: (ProductionStmt ::= PartiallyDecorated QName Expr Location) occurs on ValueDclInfo; +{-- + - The production an "equation" left hand side should forward to for this type of value (i.e. the 'x' in 'x.a = e') + -} +synthesized attribute defLHSDispatcher :: (DefLHS ::= PartiallyDecorated QName Location) occurs on ValueDclInfo; + +{-- + - The handler for 'x.a' for 'a', given that 'x' is DECORATED. + - @see accessDispather in TypeExp.sv, for the first step in that process... + - @see decoratedAccessHandler production for where this is used + -} +synthesized attribute decoratedAccessHandler :: (Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) occurs on AttributeDclInfo; +{-- + - The handler for 'x.a' for 'a', given that 'x' is UNdecorated. + - @see accessDispather in TypeExp.sv, for the first step in that process... + - @see undecoratedAccessHandler production for where this is used + -} +synthesized attribute undecoratedAccessHandler :: (Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) occurs on AttributeDclInfo; +{-- + - The production an "equation" should forward to for this type of attribute (i.e. the 'a' in 'x.a = e') + -} +synthesized attribute attrDefDispatcher :: (ProductionStmt ::= PartiallyDecorated DefLHS PartiallyDecorated QNameAttrOccur Expr Location) occurs on AttributeDclInfo; +{-- + - The production an "occurs on" decl should forward to for this type of attribute (for extension use, defaultAttributionDcl for all syn/inh/autocopy attrs.) + -} +synthesized attribute attributionDispatcher :: (AGDcl ::= PartiallyDecorated QName BracketedOptTypeExprs QName BracketedOptTypeExprs Location) occurs on AttributeDclInfo; + +-- -- non-interface values +aspect production childDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.refDispatcher = childReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: we should be smarted about error messages, and mention its a child + top.defLHSDispatcher = childDefLHS(_, location=_); +} +aspect production lhsDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.refDispatcher = lhsReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: be smarter about the error message + top.defLHSDispatcher = lhsDefLHS(_, location=_); +} +aspect production localDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.refDispatcher = localReference(_, location=_); + top.defDispatcher = localValueDef(_, _, location=_); + top.defLHSDispatcher = localDefLHS(_, location=_); +} + + +-- -- interface values +aspect production prodDcl +top::ValueDclInfo ::= ns::NamedSignature hasForward::Boolean +{ + top.refDispatcher = productionReference(_, location=_); + -- Note that we still need production references, even though bug #16 removes the production type. + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} +aspect production funDcl +top::ValueDclInfo ::= ns::NamedSignature +{ + top.refDispatcher = functionReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} +aspect production globalValueDcl +top::ValueDclInfo ::= fn::String bound::[TyVar] contexts::[Context] ty::Type +{ + top.refDispatcher = globalValueReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} +aspect production classMemberDcl +top::ValueDclInfo ::= fn::String bound::[TyVar] head::Context contexts::[Context] ty::Type +{ + top.refDispatcher = classMemberReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} + +-- -- interface Production attr (values) +aspect production forwardDcl +top::ValueDclInfo ::= ty::Type +{ + top.refDispatcher = forwardReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: better error message + top.defLHSDispatcher = forwardDefLHS(_, location=_); +} + +aspect production termIdDcl +top::ValueDclInfo ::= fn::String +{ + top.refDispatcher = terminalIdReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} + +-- -- interface Attributes +aspect production synDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); +} +aspect production inhDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: above should probably be an error handler! access inh from undecorated? + top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); +} +aspect production annoDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.decoratedAccessHandler = accessBounceUndecorate(annoAccessHandler(_, _, location=_), _, _, _); + top.undecoratedAccessHandler = annoAccessHandler(_, _, location=_); + top.attrDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, "Annotations are not defined as equations within productions")], dl, attr, e, location=l); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); +} \ No newline at end of file diff --git a/grammars/silver/compiler/definition/core/Expr.sv b/grammars/silver/compiler/definition/core/Expr.sv new file mode 100644 index 000000000..dcf6b0234 --- /dev/null +++ b/grammars/silver/compiler/definition/core/Expr.sv @@ -0,0 +1,1229 @@ +grammar silver:compiler:definition:core; + +--import silver:compiler:analysis:typechecking:core; +import silver:util:treeset as ts; + +nonterminal Expr with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, typerep, isRoot, originRules; +nonterminal Exprs with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, exprs, rawExprs, isRoot, originRules; + +nonterminal ExprInhs with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, decoratingnt, suppliedInhs, allSuppliedInhs, isRoot, originRules; +nonterminal ExprInh with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, decoratingnt, suppliedInhs, allSuppliedInhs, isRoot, originRules; +nonterminal ExprLHSExpr with + config, grammarName, env, location, unparse, errors, freeVars, frame, name, typerep, decoratingnt, suppliedInhs, allSuppliedInhs, isRoot, originRules; + +flowtype unparse {} on Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr; +flowtype freeVars {frame} on Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr; +flowtype Expr = decorate {grammarName, env, flowEnv, downSubst, finalSubst, frame, isRoot, originRules, compiledGrammars, config}, forward {decorate}; + +flowtype decorate {grammarName, env, flowEnv, downSubst, finalSubst, frame, isRoot, originRules, compiledGrammars, config} on Exprs; +flowtype decorate {grammarName, env, flowEnv, downSubst, finalSubst, frame, isRoot, originRules, compiledGrammars, config, decoratingnt, allSuppliedInhs} on ExprInhs, ExprInh; +flowtype decorate {grammarName, env, frame, isRoot, originRules, config, decoratingnt, allSuppliedInhs} on ExprLHSExpr; +flowtype forward {} on Exprs, ExprInhs, ExprInh, ExprLHSExpr; + +flowtype errors {decorate} on Exprs, ExprInhs, ExprInh, ExprLHSExpr; + +propagate errors on Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr + excluding terminalAccessHandler; +propagate freeVars on Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr; + +{-- + - The nonterminal being decorated. (Used for 'decorate with {}') + -} +autocopy attribute decoratingnt :: Type; +{-- + - The inherited attributes being supplied in a decorate expression + -} +synthesized attribute suppliedInhs :: [String]; +autocopy attribute allSuppliedInhs :: [String]; +{-- + - A list of decorated expressions from an Exprs. + -} +monoid attribute exprs :: [Decorated Expr]; +{-- + - Get each individual Expr, without decorating them. + -} +monoid attribute rawExprs :: [Expr]; +{-- + - Compute the expression's free (unbound) variables + -} +monoid attribute freeVars :: ts:Set; + +-- Is this Expr the logical "root" of the expression? That is, will it's value be the value computed +-- for the attribute/return value/etc that it is part of? +autocopy attribute isRoot :: Boolean; + +autocopy attribute originRules :: [Decorated Expr]; + +attribute grammarName, frame occurs on Contexts, Context; + +abstract production errorExpr +top::Expr ::= e::[Message] +{ + top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; + top.errors <- e; + top.typerep = errorType(); +} + +concrete production nestedExpr +top::Expr ::= '(' e::Expr ')' +{ + top.unparse = "(" ++ e.unparse ++ ")"; + + forwards to e; +} + +concrete production baseExpr +top::Expr ::= q::QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + forwards to (if null(q.lookupValue.dcls) + then errorReference(q.lookupValue.errors, _, location=_) + else q.lookupValue.dcl.refDispatcher)(q, top.location); +} action { + if (contains(q.name, sigNames)) { + insert semantic token IdSigName_t at q.baseNameLoc; + } +} + +abstract production errorReference +top::Expr ::= msg::[Message] q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + top.errors <- msg; + top.typerep = errorType(); +} + +-- TODO: We should separate this out, even, to be "nonterminal/decorable" and "as-is" +abstract production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + top.typerep = if isDecorable(q.lookupValue.typeScheme.monoType, top.env) + then q.lookupValue.typeScheme.asNtOrDecType + else q.lookupValue.typeScheme.monoType; +} + +abstract production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + -- An LHS is *always* a decorable (nonterminal) type. + top.typerep = q.lookupValue.typeScheme.asNtOrDecType; +} + +abstract production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + top.typerep = if isDecorable(q.lookupValue.typeScheme.monoType, top.env) + then q.lookupValue.typeScheme.asNtOrDecType + else q.lookupValue.typeScheme.monoType; +} + +abstract production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + -- An LHS (and thus, forward) is *always* a decorable (nonterminal) type. + top.typerep = q.lookupValue.typeScheme.asNtOrDecType; +} + +-- Note here that production and function *references* are distinguished. +-- Later on, we do *not* distinguish for application. + +abstract production productionReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + production typeScheme::PolyType = q.lookupValue.typeScheme; + top.typerep = typeScheme.typerep; + + production contexts::Contexts = + foldContexts(map(performContextSubstitution(_, top.finalSubst), typeScheme.contexts)); + contexts.env = top.env; + contexts.frame = top.frame; + contexts.config = top.config; + contexts.grammarName = top.grammarName; + contexts.compiledGrammars = top.compiledGrammars; +} + +abstract production functionReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + production typeScheme::PolyType = q.lookupValue.typeScheme; + top.typerep = typeScheme.typerep; + + production contexts::Contexts = + foldContexts(map(performContextSubstitution(_, top.finalSubst), typeScheme.contexts)); + contexts.env = top.env; + contexts.frame = top.frame; + contexts.config = top.config; + contexts.grammarName = top.grammarName; + contexts.compiledGrammars = top.compiledGrammars; +} + +abstract production classMemberReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + production typeScheme::PolyType = q.lookupValue.typeScheme; + top.typerep = typeScheme.typerep; + + production instHead::Context = + case typeScheme.contexts of + | c :: _ -> performContextSubstitution(c, top.finalSubst) + | _ -> error("Class member should have at least one context!") + end; + instHead.env = top.env; + instHead.frame = top.frame; + instHead.config = top.config; + instHead.grammarName = top.grammarName; + instHead.compiledGrammars = top.compiledGrammars; + production contexts::Contexts = + case typeScheme.contexts of + | _ :: cs -> foldContexts(map(performContextSubstitution(_, top.finalSubst), cs)) + | _ -> error("Class member should have at least one context!") + end; + contexts.env = top.env; + contexts.frame = top.frame; + contexts.config = top.config; + contexts.grammarName = top.grammarName; + contexts.compiledGrammars = top.compiledGrammars; +} + +abstract production globalValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars <- ts:fromList([q.name]); + + -- Type inference + production typeScheme::PolyType = q.lookupValue.typeScheme; + top.typerep = typeScheme.typerep; + + -- Context resolution + -- Performs final substitution on all the contexts + production contexts::Contexts = + foldContexts(map(performContextSubstitution(_, top.finalSubst), typeScheme.contexts)); + contexts.env = top.env; + contexts.frame = top.frame; + contexts.config = top.config; + contexts.grammarName = top.grammarName; + contexts.compiledGrammars = top.compiledGrammars; +} + +concrete production concreteForwardExpr +top::Expr ::= q::'forward' +{ + top.unparse = "forward"; + + -- TODO: we're forwarding to baseExpr just to decorate the tree we create. + -- That's a bit weird. + forwards to baseExpr(qName(q.location, "forward"), location=top.location); +} + +concrete production application +top::Expr ::= e::Expr '(' es::AppExprs ',' anns::AnnoAppExprs ')' +{ + -- TODO: fix comma when one or the other is empty + top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; + propagate freeVars; + + local correctNumTypes :: [Type] = + if length(t.inputTypes) > es.appExprSize + then take(es.appExprSize, t.inputTypes) + else if length(t.inputTypes) < es.appExprSize + then t.inputTypes ++ repeat(errorType(), es.appExprSize - length(t.inputTypes)) + else t.inputTypes; + + -- NOTE: REVERSED ORDER + -- We may need to resolve e's type to get at the actual 'function type' + local t :: Type = performSubstitution(e.typerep, e.upSubst); + es.appExprTypereps = reverse(correctNumTypes); + es.appExprApplied = e.unparse; + anns.appExprApplied = e.unparse; + anns.remainingFuncAnnotations = t.namedTypes; + anns.funcAnnotations = anns.remainingFuncAnnotations; + + top.errors <- + if !t.isApplicable + then [] + else if length(t.inputTypes) > es.appExprSize + then [err(top.location, "Too few arguments provided to function '" ++ e.unparse ++ "'")] + else if length(t.inputTypes) < es.appExprSize + then [err(top.location, "Too many arguments provided to function '" ++ e.unparse ++ "'")] + else []; + + -- TODO: You know, since the rule is we can't access .typerep without "first" supplying + -- .downSubst, perhaps we should just... report .typerep after substitution in the first place! + forwards to t.applicationDispatcher(e, es, anns, top.location); +} + +concrete production applicationAnno +top::Expr ::= e::Expr '(' anns::AnnoAppExprs ')' +{ + forwards to application(e, $2, emptyAppExprs(location=$2.location), ',', anns, $4, location=top.location); +} +concrete production applicationExpr +top::Expr ::= e::Expr '(' es::AppExprs ')' +{ + forwards to application(e, $2, es, ',', emptyAnnoAppExprs(location=$4.location), $4, location=top.location); +} +concrete production applicationEmpty +top::Expr ::= e::Expr '(' ')' +{ + forwards to application(e, $2, emptyAppExprs(location=$2.location), ',', emptyAnnoAppExprs(location=$3.location), $3, location=top.location); +} + +abstract production errorApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; + + top.errors <- + if e.typerep.isError then [] else + [err(top.location, e.unparse ++ " has type " ++ prettyType(performSubstitution(e.typerep, e.upSubst)) ++ + " and cannot be invoked as a function.")]; + -- TODO This error message is cumbersomely generated... + + top.typerep = errorType(); +} + +-- Note that this applies to both function and productions. +-- We don't distinguish anymore at this point. A production reference +-- becomes a function, effectively. +abstract production functionApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; + top.freeVars := e.freeVars ++ es.freeVars ++ anns.freeVars; + + -- TODO: we have an ambiguity here in the longer term. + -- How to distinguish between + -- foo(x) where there is an annotation 'a'? + -- Is this partial application, give (Foo ::= ;a::Something) or (Foo) + error. + -- Possibly this can be solved by having somehting like "foo(x,a=?)" + forwards to + (if es.isPartial || anns.isPartial + then partialApplication + else functionInvocation)(e, es, anns, location=top.location); +} + +abstract production functionInvocation +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; + + local ety :: Type = performSubstitution(e.typerep, e.upSubst); + + top.typerep = ety.outputType; +} + +abstract production partialApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; + + local ety :: Type = performSubstitution(e.typerep, e.upSubst); + + top.typerep = + appTypes( + functionType(length(es.missingTypereps) + length(anns.partialAnnoTypereps), map(fst, anns.missingAnnotations)), + es.missingTypereps ++ anns.partialAnnoTypereps ++ map(snd, anns.missingAnnotations) ++ [ety.outputType]); +} + +concrete production noteAttachment +top::Expr ::= 'attachNote' note::Expr 'on' e::Expr 'end' +{ + top.unparse = "attachNote" ++ note.unparse ++ " on " ++ e.unparse ++ " end"; + + top.typerep = e.typerep; + + note.isRoot = false; + e.isRoot = false; + e.originRules = top.originRules ++ [note]; +} + +-- NOTE: this is not intended to be used normally. +-- Its purpose is for test cases. Essentially all other situations should never care what the forward tree is. +concrete production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + top.unparse = e.unparse ++ ".forward"; + top.typerep = e.typerep; +} + +concrete production access +top::Expr ::= e::Expr '.' q::QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + propagate freeVars; + + local eTy::Type = performSubstitution(e.typerep, e.upSubst); + q.attrFor = if eTy.isDecorated then eTy.decoratedType else eTy; + + -- Note: we're first consulting the TYPE of the LHS. + forwards to eTy.accessHandler(e, q, top.location); + -- This jumps to: + -- errorAccessHandler (e.g. 1.unparse) + -- undecoratedAccessHandler + -- decoratedAccessHandler (see that production, for how normal attribute access proceeds!) + -- terminalAccessHandler +} + +abstract production errorAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + top.typerep = errorType(); + + top.errors <- + if e.typerep.isError then [] else + let ref :: String = + if length(e.unparse) < 12 then "'" ++ e.unparse ++ "' has" else "LHS of '.' is" + in [err(top.location, ref ++ " type " ++ prettyType(q.attrFor) ++ " and cannot have attributes.")] + end; +} + +abstract production annoAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + production index :: Integer = + findNamedSigElem(q.name, annotationsForNonterminal(q.attrFor, top.env), 0); + + top.typerep = q.typerep; +} + +abstract production terminalAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + -- NO use of q.errors, as that become nonsensical here. + top.errors := e.errors; + + top.errors <- + if q.name == "lexeme" || q.name == "location" || + q.name == "filename" || q.name == "line" || q.name == "column" + then [] + else [err(q.location, q.name ++ " is not a terminal attribute")]; + + top.typerep = + if q.name == "lexeme" || q.name == "filename" + then stringType() + else if q.name == "line" || q.name == "column" + then intType() + else if q.name == "location" + then nonterminalType("silver:core:Location", [], false) + else errorType(); +} + +abstract production undecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + -- Note: LHS is UNdecorated, here we dispatch based on the kind of attribute. + forwards to (if !q.found then errorDecoratedAccessHandler(_, _, location=_) + else q.attrDcl.undecoratedAccessHandler)(e, q, top.location); + -- annoAccessHandler + -- accessBouncer +} + +{-- + - Accessing an attribute occasionally demands manipulating the left-hand side. + - This production is intended to permit that. + -} +abstract production accessBouncer +top::Expr ::= target::(Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) e::Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + propagate freeVars; + + -- Basically the only purpose here is to decorate 'e'. + forwards to target(e, q, top.location); +} +function accessBounceDecorate +Expr ::= target::(Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur l::Location +{ + return accessBouncer(target, decorateExprWithEmpty('decorate', exprRef(e, location=l), 'with', '{', '}', location=l), q, location=l); +} +function accessBounceUndecorate +Expr ::= target::(Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location) e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur l::Location +{ + return accessBouncer(target, mkStrFunctionInvocationDecorated(l, "silver:core:new", [e]), q, location=l); +} + +abstract production decoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + -- Note: LHS is decorated, here we dispatch based on the kind of attribute. + forwards to (if !q.found then errorDecoratedAccessHandler(_, _, location=_) + else q.attrDcl.decoratedAccessHandler)(e, q, top.location); + -- From here we go to: + -- synDecoratedAccessHandler + -- inhDecoratedAccessHandler + -- errorDecoratedAccessHandler -- unknown attribute error raised already. +} + +abstract production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + top.typerep = q.typerep; +} + +abstract production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + top.typerep = q.typerep; +} + +-- TODO: change name. really "unknownDclAccessHandler" +abstract production errorDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.unparse = e.unparse ++ "." ++ q.unparse; + + top.typerep = errorType(); +} + + +concrete production decorateExprWithEmpty +top::Expr ::= 'decorate' e::Expr 'with' '{' '}' +{ + top.unparse = "decorate " ++ e.unparse ++ " with {}"; + + forwards to decorateExprWith($1, e, $3, $4, exprInhsEmpty(location=top.location), $5, location=top.location); +} + +concrete production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + top.unparse = "decorate " ++ e.unparse ++ " with {" ++ inh.unparse ++ "}"; + + production eType::Type = performSubstitution(e.typerep, inh.downSubst); -- Specialize e.typerep + production ntType::Type = if eType.isDecorated then eType.decoratedType else eType; + + -- TODO: This _could_ be partiallyDecoratedType, but we use decorate in a ton of places where we expect a decoratedType + top.typerep = decoratedType(ntType, inhSetType(sort(nub(inh.suppliedInhs ++ eType.inhSetMembers)))); + e.isRoot = false; + + inh.decoratingnt = ntType; + inh.allSuppliedInhs = inh.suppliedInhs; +} + +abstract production exprInhsEmpty +top::ExprInhs ::= +{ + top.unparse = ""; + + top.suppliedInhs = []; +} + +concrete production exprInhsOne +top::ExprInhs ::= lhs::ExprInh +{ + top.unparse = lhs.unparse; + + top.suppliedInhs = lhs.suppliedInhs; +} + +concrete production exprInhsCons +top::ExprInhs ::= lhs::ExprInh inh::ExprInhs +{ + top.unparse = lhs.unparse ++ " " ++ inh.unparse; + + top.suppliedInhs = lhs.suppliedInhs ++ inh.suppliedInhs; +} + +concrete production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' +{ + top.unparse = lhs.unparse ++ " = " ++ e.unparse ++ ";"; + + top.suppliedInhs = lhs.suppliedInhs; +} + +concrete production exprLhsExpr +top::ExprLHSExpr ::= q::QNameAttrOccur +{ + top.name = q.name; + top.unparse = q.unparse; + + top.typerep = q.typerep; + top.suppliedInhs = if q.attrFound then [q.attrDcl.fullName] else []; + + q.attrFor = top.decoratingnt; +} + +concrete production trueConst +top::Expr ::= 'true' +{ + top.unparse = "true"; + + top.typerep = boolType(); +} + +concrete production falseConst +top::Expr ::= 'false' +{ + top.unparse = "false"; + + top.typerep = boolType(); +} + +concrete production and +top::Expr ::= e1::Expr '&&' e2::Expr +{ + top.unparse = e1.unparse ++ " && " ++ e2.unparse; + + top.typerep = boolType(); + + e1.isRoot = false; + e2.isRoot = false; +} + +concrete production or +top::Expr ::= e1::Expr '||' e2::Expr +{ + top.unparse = e1.unparse ++ " || " ++ e2.unparse; + + top.typerep = boolType(); + + e1.isRoot = false; + e2.isRoot = false; +} + +concrete production notOp +top::Expr ::= '!' e::Expr +{ + top.unparse = "! " ++ e.unparse; + + top.typerep = boolType(); + + e.isRoot = false; +} + +concrete production gtOp +top::Expr ::= e1::Expr op::'>' e2::Expr +{ + top.unparse = e1.unparse ++ " > " ++ e2.unparse; + + forwards to + -- silver:core:gt(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:gt"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production ltOp +top::Expr ::= e1::Expr op::'<' e2::Expr +{ + top.unparse = e1.unparse ++ " < " ++ e2.unparse; + + forwards to + -- silver:core:lt(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:lt"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production gteOp +top::Expr ::= e1::Expr op::'>=' e2::Expr +{ + top.unparse = e1.unparse ++ " >= " ++ e2.unparse; + + forwards to + -- silver:core:gte(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:gte"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production lteOp +top::Expr ::= e1::Expr op::'<=' e2::Expr +{ + top.unparse = e1.unparse ++ " <= " ++ e2.unparse; + + forwards to + -- silver:core:lte(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:lte"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production eqOp +top::Expr ::= e1::Expr op::'==' e2::Expr +{ + top.unparse = e1.unparse ++ " == " ++ e2.unparse; + + forwards to + -- silver:core:eq(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:eq"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production neqOp +top::Expr ::= e1::Expr op::'!=' e2::Expr +{ + top.unparse = e1.unparse ++ " != " ++ e2.unparse; + + forwards to + -- silver:core:neq(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:neq"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production ifThenElse +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr +precedence = 0 +{ + top.unparse = "if " ++ e1.unparse ++ " then " ++ e2.unparse ++ " else " ++ e3.unparse; + + top.typerep = e2.typerep; +} + +concrete production intConst +top::Expr ::= i::Int_t +{ + top.unparse = i.lexeme; + + top.typerep = intType(); +} + +concrete production floatConst +top::Expr ::= f::Float_t +{ + top.unparse = f.lexeme; + + top.typerep = floatType(); +} + +concrete production plus +top::Expr ::= e1::Expr '+' e2::Expr +{ + top.unparse = e1.unparse ++ " + " ++ e2.unparse; + + top.typerep = e1.typerep; + + e1.isRoot=false; + e2.isRoot=false; +} + +concrete production minus +top::Expr ::= e1::Expr '-' e2::Expr +{ + top.unparse = e1.unparse ++ " - " ++ e2.unparse; + + top.typerep = e1.typerep; + + e1.isRoot=false; + e2.isRoot=false; +} + +concrete production multiply +top::Expr ::= e1::Expr '*' e2::Expr +{ + top.unparse = e1.unparse ++ " * " ++ e2.unparse; + + top.typerep = e1.typerep; + + e1.isRoot=false; + e2.isRoot=false; +} + +concrete production divide +top::Expr ::= e1::Expr '/' e2::Expr +{ + top.unparse = e1.unparse ++ " / " ++ e2.unparse; + + top.typerep = e1.typerep; + + e1.isRoot=false; + e2.isRoot=false; +} + +concrete production modulus +top::Expr ::= e1::Expr '%' e2::Expr +{ + top.unparse = e1.unparse ++ " % " ++ e2.unparse; + + top.typerep = e1.typerep; + + e1.isRoot=false; + e2.isRoot=false; +} + +concrete production neg +top::Expr ::= '-' e::Expr +precedence = 13 +{ + top.unparse = "- " ++ e.unparse; + + top.typerep = e.typerep; + + e.isRoot=false; +} + +concrete production stringConst +top::Expr ::= s::String_t +{ + top.unparse = s.lexeme; + + top.typerep = stringType(); +} + +concrete production plusPlus +top::Expr ::= e1::Expr op::'++' e2::Expr +{ + top.unparse = e1.unparse ++ " ++ " ++ e2.unparse; + + forwards to + -- silver:core:append(e1, e2) + applicationExpr( + baseExpr(qName(op.location, "silver:core:append"), location=op.location), '(', + snocAppExprs( + oneAppExprs(presentAppExpr(e1, location=top.location), location=top.location), ',', + presentAppExpr(e2, location=top.location), + location=top.location),')', + location=top.location); +} + +concrete production terminalConstructor +top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' +{ + top.unparse = "terminal(" ++ t.unparse ++ ", " ++ es.unparse ++ ", " ++ el.unparse ++ ")"; + + top.typerep = t.typerep; + + es.isRoot = false; + el.isRoot = false; +} + +concrete production terminalFunction +top::Expr ::= 'terminal' '(' t::TypeExpr ',' e::Expr ')' +{ + -- So, *maybe* this is deprecated? But let's not complain for now, because + -- it's too widely used. + + --top.errors <- [wrn(t.location, "terminal(type,lexeme) is deprecated. Use terminal(type,lexeme,location) instead.")]; + + local bogus :: Expr = + mkStrFunctionInvocation($6.location, "silver:core:bogusLoc", []); + + forwards to terminalConstructor($1, $2, t, $4, e, ',', bogus, $6, location=top.location); +} + +-- These sorta seem obsolete, but there are some important differences from AppExprs. +-- For one, AppExprs expects a fixed, imposed list of types. Here we're flexible! +-- This is used by both pattern matching and list literals. +abstract production exprsEmpty +top::Exprs ::= +{ + top.unparse = ""; + + top.exprs := []; + top.rawExprs := []; +} +concrete production exprsSingle +top::Exprs ::= e::Expr +{ + top.unparse = e.unparse; + + top.exprs := [e]; + top.rawExprs := [e]; +} +concrete production exprsCons +top::Exprs ::= e1::Expr ',' e2::Exprs +{ + top.unparse = e1.unparse ++ ", " ++ e2.unparse; + + top.exprs := [e1] ++ e2.exprs; + top.rawExprs := [e1] ++ e2.rawExprs; +} + + +{-- + - Exprs with optional underscores omitting parameters. Used exclusively for + - (partial) function application. + -} +nonterminal AppExprs with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, exprs, rawExprs, + isPartial, missingTypereps, appExprIndicies, appExprSize, appExprTypereps, appExprApplied, isRoot, originRules; + +nonterminal AppExpr with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, exprs, rawExprs, + isPartial, missingTypereps, appExprIndicies, appExprIndex, appExprTyperep, appExprApplied, isRoot, originRules; + +propagate errors, freeVars on AppExprs, AppExpr; +propagate exprs, rawExprs on AppExprs; + +synthesized attribute isPartial :: Boolean; +synthesized attribute missingTypereps :: [Type]; +synthesized attribute appExprIndicies :: [Integer]; +synthesized attribute appExprSize :: Integer; +inherited attribute appExprIndex :: Integer; +inherited attribute appExprTypereps :: [Type]; +inherited attribute appExprTyperep :: Type; +autocopy attribute appExprApplied :: String; + +-- These are the "new" Exprs syntax. This allows missing (_) arguments, to indicate partial application. +concrete production missingAppExpr +top::AppExpr ::= '_' +{ + top.unparse = "_"; + + top.isPartial = true; + top.missingTypereps = [top.appExprTyperep]; + + top.rawExprs := []; + top.exprs := []; + top.appExprIndicies = []; +} +concrete production presentAppExpr +top::AppExpr ::= e::Expr +{ + top.unparse = e.unparse; + + top.isPartial = false; + top.missingTypereps = []; + + top.rawExprs := [e]; + top.exprs := [e]; + top.appExprIndicies = [top.appExprIndex]; +} + +concrete production snocAppExprs +top::AppExprs ::= es::AppExprs ',' e::AppExpr +{ + top.unparse = es.unparse ++ ", " ++ e.unparse; + + top.isPartial = es.isPartial || e.isPartial; + top.missingTypereps = es.missingTypereps ++ e.missingTypereps; + + top.appExprIndicies = es.appExprIndicies ++ e.appExprIndicies; + + top.appExprSize = es.appExprSize + 1; + + e.appExprIndex = es.appExprSize; + e.appExprTyperep = head(top.appExprTypereps); + + es.appExprTypereps = tail(top.appExprTypereps); +} +concrete production oneAppExprs +top::AppExprs ::= e::AppExpr +{ + top.unparse = e.unparse; + + top.isPartial = e.isPartial; + top.missingTypereps = e.missingTypereps; + + top.appExprIndicies = e.appExprIndicies; + + top.appExprSize = 1; + + e.appExprIndex = 0; + e.appExprTyperep = if null(top.appExprTypereps) + then errorType() + else head(top.appExprTypereps); +} +abstract production emptyAppExprs +top::AppExprs ::= +{ + top.unparse = ""; + + top.isPartial = false; + top.missingTypereps = []; + + top.appExprIndicies = []; + top.appExprSize = 0; + + -- Assumption: We only get here when we're looking at () + -- i.e. we can't ever have 'too many' provided error + top.errors <- if null(top.appExprTypereps) then [] + else [err(top.location, "Too few arguments provided to function '" ++ top.appExprApplied ++ "'")]; +} + + +nonterminal AnnoAppExprs with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, + isPartial, appExprApplied, exprs, + remainingFuncAnnotations, funcAnnotations, + missingAnnotations, partialAnnoTypereps, annoIndexConverted, annoIndexSupplied, isRoot, originRules; +nonterminal AnnoExpr with + config, grammarName, env, location, unparse, errors, freeVars, frame, compiledGrammars, + isPartial, appExprApplied, exprs, + remainingFuncAnnotations, funcAnnotations, + missingAnnotations, partialAnnoTypereps, annoIndexConverted, annoIndexSupplied, isRoot, originRules; + +propagate errors, freeVars, exprs on AnnoAppExprs, AnnoExpr; + +{-- + - Annotations that have not yet been supplied + -} +inherited attribute remainingFuncAnnotations :: [Pair]; +{-- + - All annotations of this function + -} +autocopy attribute funcAnnotations :: [Pair]; +{-- + - Annotations that have not been supplied (by subtracting from remainingFuncAnnotations) + -} +synthesized attribute missingAnnotations :: [Pair]; +{-- + - Typereps of those annotations that are partial (_) + -} +synthesized attribute partialAnnoTypereps :: [Type]; + +synthesized attribute annoIndexConverted :: [Integer]; +synthesized attribute annoIndexSupplied :: [Integer]; + +concrete production annoExpr +top::AnnoExpr ::= qn::QName '=' e::AppExpr +{ + top.unparse = qn.unparse ++ "=" ++ e.unparse; + + local fq :: Pair> [Pair]> = + extractNamedArg(qn.name, top.remainingFuncAnnotations); + + e.appExprIndex = + findNamedArgType(qn.name, top.funcAnnotations, 0); + e.appExprTyperep = + if fq.fst.isJust then fq.fst.fromJust.snd else errorType(); + + top.missingAnnotations = fq.snd; -- minus qn, if it was there + top.partialAnnoTypereps = e.missingTypereps; + + top.errors <- + if fq.fst.isJust then [] + else [err(qn.location, "Named parameter '" ++ qn.name ++ "' is not appropriate for '" ++ top.appExprApplied ++ "'")]; + top.isPartial = e.isPartial; + top.annoIndexConverted = + if e.isPartial then [e.appExprIndex] else []; + top.annoIndexSupplied = + if e.isPartial then [] else [e.appExprIndex]; +} + +concrete production snocAnnoAppExprs +top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr +{ + top.unparse = es.unparse ++ ", " ++ e.unparse; + + top.isPartial = es.isPartial || e.isPartial; + + e.remainingFuncAnnotations = top.remainingFuncAnnotations; + es.remainingFuncAnnotations = e.missingAnnotations; + top.missingAnnotations = es.missingAnnotations; + + top.partialAnnoTypereps = es.partialAnnoTypereps ++ e.partialAnnoTypereps; + top.annoIndexConverted = es.annoIndexConverted ++ e.annoIndexConverted; + top.annoIndexSupplied = es.annoIndexSupplied ++ e.annoIndexSupplied; +} + +concrete production oneAnnoAppExprs +top::AnnoAppExprs ::= e::AnnoExpr +{ + top.unparse = e.unparse; + + top.isPartial = e.isPartial; + top.errors <- + if null(top.missingAnnotations) then [] + else [err(top.location, "Missing named parameters for function '" ++ top.appExprApplied ++ "': " + ++ implode(", ", map(fst, top.missingAnnotations)))]; + + e.remainingFuncAnnotations = top.remainingFuncAnnotations; + top.missingAnnotations = e.missingAnnotations; + + top.partialAnnoTypereps = e.partialAnnoTypereps; + top.annoIndexConverted = e.annoIndexConverted; + top.annoIndexSupplied = e.annoIndexSupplied; +} + +abstract production emptyAnnoAppExprs +top::AnnoAppExprs ::= +{ + top.unparse = ""; + + top.isPartial = false; + top.errors <- + if null(top.missingAnnotations) then [] + else [err(top.location, "Missing named parameters for function '" ++ top.appExprApplied ++ "': " + ++ implode(", ", map(fst, top.missingAnnotations)))]; + + top.missingAnnotations = top.remainingFuncAnnotations; + + top.partialAnnoTypereps = []; + top.annoIndexConverted = []; + top.annoIndexSupplied = []; +} + +function reorderedAnnoAppExprs +[Decorated Expr] ::= d::Decorated AnnoAppExprs +{ + -- This is an annoyingly poor quality implementation + return map(snd, sortBy(reorderedLte, zipWith(pair, d.annoIndexSupplied, d.exprs))); +} +function reorderedLte +Boolean ::= l::Pair r::Pair { return l.fst <= r.fst; } + +function extractNamedArg +Pair> [Pair]> ::= n::String l::[Pair] +{ + local recurse :: Pair> [Pair]> = + extractNamedArg(n, tail(l)); + + return if null(l) then pair(nothing(), []) + else if head(l).fst == n then pair(just(head(l)), tail(l)) + else pair(recurse.fst, head(l) :: recurse.snd); +} + +function findNamedArgType +Integer ::= s::String l::[Pair] z::Integer +{ + return if null(l) then -1 + else if s == head(l).fst then z + else findNamedArgType(s, tail(l), z+1); +} + + +{-- + - Utility for other modules to create function invocations. + - This makes no assumptions, use it any way you wish! + -} +function mkStrFunctionInvocation +Expr ::= l::Location e::String es::[Expr] +{ + return mkFullFunctionInvocation(l, baseExpr(qName(l, e), location=l), es, []); +} +function mkFunctionInvocation +Expr ::= l::Location e::Expr es::[Expr] +{ + return mkFullFunctionInvocation(l, e, es, []); +} +function mkFullFunctionInvocation +Expr ::= l::Location e::Expr es::[Expr] ans::[Pair] +{ + return application(e, '(', + foldl(snocAppExprs(_, ',', _, location=l), emptyAppExprs(location=l), + map(presentAppExpr(_, location=l), es)), + ',', + foldl(snocAnnoAppExprs(_, ',', _, location=l), emptyAnnoAppExprs(location=l), + map(mkAnnoExpr, ans)), + ')', location=l); +} +-- Internal helper function +function mkAnnoExpr +AnnoExpr ::= p::Pair +{ + return annoExpr(qName(p.snd.location, p.fst), '=', presentAppExpr(p.snd, location=p.snd.location), location=p.snd.location); +} + +{-- + - Utility for other modules to create function invocations. + - + - Major assumption: The expressions are already decorated, and the + - typing substitution threaded through them will then be fed through + - the expr created by this function. + - + - The purpose of this vs just mkFunctionInvocationDecorated + - is to avoid exponential growth from forwarding. Type checking + - an expr, then forwarding to a function call that again type + - checks that expr well... just nest those and boom. + -} +function mkFunctionInvocationDecorated +Expr ::= l::Location e::Expr es::[PartiallyDecorated Expr] +{ + return mkFunctionInvocation(l, e, map(exprRef(_, location=l), es)); +} +function mkStrFunctionInvocationDecorated +Expr ::= l::Location e::String es::[PartiallyDecorated Expr] +{ + return mkFunctionInvocation(l, baseExpr(qName(l, e), location=l), map(exprRef(_, location=l), es)); +} + +{-- + - We allow references to existing subexpressions to appear arbitrarily in trees. + - + - There is one MAJOR restriction on the use of this production: + - The referenced expression (e) MUST APPEAR in the same expression tree + - as it is referenced in. + - + - This is for type information reasons: the subtree referenced must have been + - typechecked in the same 'typing context' as wherever this tree appears. + - + - This is trivially satisfied for the typical use case for this production, + - where you're typechecking your children, then forwarding to some tree with + - references to those children. + -} +abstract production exprRef +top::Expr ::= e::PartiallyDecorated Expr +{ + top.unparse = e.unparse; + + -- See the major restriction. This should have been checked for error already! + top.typerep = e.typerep; + + -- TODO: one of the little things we might want is to make this transparent to + -- forwarding. e.g. e might be a 'childReference' and pattern matching would + -- need to separately account for this! + -- To accomplish this, we might want some notion of a decorated forward. +} + diff --git a/grammars/silver/compiler/definition/core/FunctionDcl.sv b/grammars/silver/compiler/definition/core/FunctionDcl.sv new file mode 100644 index 000000000..60fef7243 --- /dev/null +++ b/grammars/silver/compiler/definition/core/FunctionDcl.sv @@ -0,0 +1,95 @@ +grammar silver:compiler:definition:core; + +nonterminal FunctionSignature with config, grammarName, env, location, unparse, errors, defs, constraintDefs, occursDefs, namedSignature, signatureName; +nonterminal FunctionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement; + +propagate errors on FunctionSignature, FunctionLHS; + +concrete production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + top.unparse = "function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production namedSig :: NamedSignature = ns.namedSignature; + + top.defs := funDef(top.grammarName, id.location, namedSig) :: + if null(body.productionAttributes) then [] + else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- + if null(body.uniqueSignificantExpression) + then [err(top.location, "Function '" ++ id.name ++ "' does not have a return value.")] + else if length(body.uniqueSignificantExpression) > 1 + then [err(top.location, "Function '" ++ id.name ++ "' has more than one declared return value.")] + else []; + + production attribute sigDefs :: [Def] with ++; + sigDefs := ns.defs; + + ns.signatureName = fName; + ns.env = newScopeEnv(sigDefs, top.env); + + local attribute prodAtts :: [Def]; + prodAtts = defsFromPADcls(getProdAttrs(fName, top.env), namedSig); + + body.env = occursEnv(ns.occursDefs, newScopeEnv(body.defs ++ sigDefs ++ ns.constraintDefs, newScopeEnv(prodAtts, top.env))); + body.frame = functionContext(namedSig, myFlowGraph, sourceGrammar=top.grammarName); -- graph from flow:env +} action { + insert semantic token IdFnProdDcl_t at id.location; + sigNames = []; +} + +concrete production functionSignature +top::FunctionSignature ::= cl::ConstraintList '=>' lhs::FunctionLHS '::=' rhs::ProductionRHS +{ + top.unparse = s"${cl.unparse} => ${lhs.unparse} ::= ${rhs.unparse}"; + + cl.constraintPos = signaturePos(top.namedSignature); + rhs.env = occursEnv(cl.occursDefs, top.env); + + top.defs := lhs.defs ++ rhs.defs; + top.constraintDefs = cl.defs; + top.occursDefs := cl.occursDefs; + + top.namedSignature = + namedSignature( + top.signatureName, + foldContexts(cl.contexts), + foldNamedSignatureElements(rhs.inputElements), + lhs.outputElement, + -- For the moment, functions do not have named parameters (hence, nilNamedSignatureElement) + nilNamedSignatureElement()); +} action { + sigNames = foldNamedSignatureElements(rhs.inputElements).elementNames; +} + +concrete production functionSignatureNoCL +top::FunctionSignature ::= lhs::FunctionLHS '::=' rhs::ProductionRHS +{ + top.unparse = s"${lhs.unparse} ::= ${rhs.unparse}"; + + forwards to functionSignature(nilConstraint(location=top.location), '=>', lhs, $2, rhs, location=top.location); +} action { + sigNames = foldNamedSignatureElements(rhs.inputElements).elementNames; +} + +concrete production functionLHS +top::FunctionLHS ::= t::TypeExpr +{ + top.unparse = t.unparse; + + production attribute fName :: String; + fName = "__func__lhs"; + + top.outputElement = namedSignatureElement(fName, t.typerep); + + -- TODO: think about this. lhs doesn't really have an fName. + top.defs := [lhsDef(top.grammarName, t.location, fName, t.typerep)]; +} + diff --git a/grammars/silver/compiler/definition/core/GlobalDcl.sv b/grammars/silver/compiler/definition/core/GlobalDcl.sv new file mode 100644 index 000000000..67e827709 --- /dev/null +++ b/grammars/silver/compiler/definition/core/GlobalDcl.sv @@ -0,0 +1,59 @@ +grammar silver:compiler:definition:core; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; + +concrete production globalValueDclConcrete +top::AGDcl ::= 'global' id::Name '::' cl::ConstraintList '=>' t::TypeExpr '=' e::Expr ';' +{ + top.unparse = "global " ++ id.unparse ++ " :: " ++ cl.unparse ++ " => " ++ t.unparse ++ " = " ++ e.unparse ++ ";\n"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ id.name; + + production boundVars::[TyVar] = t.typerep.freeVariables; + + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(t.lexicalTypeVariables); + + production attribute typeExprDefs :: [Def] with ++; + typeExprDefs := addNewLexicalTyVars(top.grammarName, top.location, t.lexicalTyVarKinds, allLexicalTyVars); + + -- The following environments require the definitions from the type + -- expression, as constructed above in typeExprDefs using its lexical + -- type variables + cl.env = newScopeEnv(typeExprDefs, top.env); + t.env = cl.env; + -- The expression also requires the constraint list definitions in its env + e.env = occursEnv(cl.occursDefs, newScopeEnv(cl.defs, cl.env)); + + top.defs := [globalDef(top.grammarName, id.location, fName, boundVars, cl.contexts, t.typerep)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + else []; + + e.originRules = []; + e.isRoot = true; + + cl.constraintPos = globalPos(boundVars); + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); + + e.frame = globalExprContext(fName, foldContexts(cl.contexts), t.typerep, myFlowGraph, sourceGrammar=top.grammarName); +} + +-- If the global declaration does not have constraints, we unparse appropriately +-- in the following production and forward to the generic globalValueDclConcrete +-- production with an empty constraint list +concrete production globalValueDclConcreteNoCL +top::AGDcl ::= 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' +{ + top.unparse = "global " ++ id.unparse ++ " :: " ++ t.unparse ++ " = " ++ e.unparse ++ ";\n"; + + forwards to globalValueDclConcrete($1, id, $3, nilConstraint(location=top.location), '=>', t, $5, e, $7, location=top.location); +} \ No newline at end of file diff --git a/grammars/silver/definition/core/GrammarParts.sv b/grammars/silver/compiler/definition/core/GrammarParts.sv similarity index 76% rename from grammars/silver/definition/core/GrammarParts.sv rename to grammars/silver/compiler/definition/core/GrammarParts.sv index 970010231..2f335b4a0 100644 --- a/grammars/silver/definition/core/GrammarParts.sv +++ b/grammars/silver/compiler/definition/core/GrammarParts.sv @@ -1,4 +1,4 @@ -grammar silver:definition:core; +grammar silver:compiler:definition:core; nonterminal Grammar with -- Global inherited attributes @@ -7,7 +7,9 @@ nonterminal Grammar with grammarName, env, globalImports, grammarDependencies, -- Synthesized attributes declaredName, moduleNames, exportedGrammars, optionalGrammars, condBuild, - defs, occursDefs, importedDefs, importedOccursDefs, grammarErrors, jarName; + defs, occursDefs, importedDefs, importedOccursDefs, grammarErrors, allFileErrors, jarName; + +flowtype Grammar = decorate {config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, grammarName, env, flowEnv, globalImports, grammarDependencies}; {-- - A list of grammars that this grammar depends upon, @@ -25,12 +27,16 @@ autocopy attribute globalImports :: Decorated Env; - The definitions resulting from grammar-wide imports definitions. - At the top of a grammar, these are echoed down as globalImports -} -monoid attribute importedDefs :: [Def] with [], ++; -monoid attribute importedOccursDefs :: [DclInfo] with [], ++; +monoid attribute importedDefs :: [Def]; +monoid attribute importedOccursDefs :: [OccursDclInfo]; {-- - An overall listing of error messages for a grammar -} synthesized attribute grammarErrors :: [Pair]; +{-- + - All files in a grammar, paired with their error messages. + -} +synthesized attribute allFileErrors :: [Pair]; propagate moduleNames, exportedGrammars, optionalGrammars, condBuild, defs, @@ -44,6 +50,7 @@ top::Grammar ::= -- turn into this, and this "aren't found". TODO verify this is true? top.declaredName = ":null"; top.grammarErrors = []; + top.allFileErrors = []; } abstract production consGrammar @@ -53,6 +60,7 @@ top::Grammar ::= h::Root t::Grammar top.grammarErrors = if null(h.errors ++ jarNameErrors) then t.grammarErrors else pair(h.location.filename, h.errors ++ jarNameErrors) :: t.grammarErrors; + top.allFileErrors = (h.location.filename, h.errors ++ jarNameErrors) :: t.allFileErrors; local jarNameErrors :: [Message] = warnIfMultJarName(h.jarName, t.jarName, h.location); } diff --git a/grammars/silver/compiler/definition/core/InstanceDcl.sv b/grammars/silver/compiler/definition/core/InstanceDcl.sv new file mode 100644 index 000000000..0bea79490 --- /dev/null +++ b/grammars/silver/compiler/definition/core/InstanceDcl.sv @@ -0,0 +1,176 @@ +grammar silver:compiler:definition:core; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; + +concrete production instanceDcl +top::AGDcl ::= 'instance' cl::ConstraintList '=>' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + top.unparse = s"instance ${cl.unparse} => ${id.unparse} ${ty.unparse}\n{\n${body.unparse}\n}"; + + production fName :: String = id.lookupType.fullName; + production boundVars::[TyVar] = ty.freeVariables; + production dcl::TypeDclInfo = id.lookupType.dcl; + dcl.givenInstanceType = ty.typerep; + + production superContexts::Contexts = + foldContexts(if id.lookupType.found && !foldContexts(cl.contexts).isTypeError then dcl.superContexts else []); + superContexts.env = body.env; + superContexts.config = top.config; + superContexts.grammarName = top.grammarName; + superContexts.compiledGrammars = top.compiledGrammars; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = constructAnonymousGraph(body.flowDefs, top.env, myProds, myFlow); + superContexts.frame = globalExprContext(fName, foldContexts(body.frameContexts), ty.typerep, myFlowGraph, sourceGrammar=top.grammarName); + + top.defs := [instDef(top.grammarName, id.location, fName, boundVars, cl.contexts, ty.typerep, body.definedMembers)]; + + top.errors <- id.lookupType.errors; + top.errors <- + if !id.lookupType.found || dcl.isClass then [] + else [err(id.location, id.name ++ " is not a type class.")]; + top.errors <- + if !ty.typerep.isError && length(getInstanceDcl(fName, ty.typerep, top.env)) > 1 + then [err(id.location, "Overlapping instances exist for " ++ id.unparse ++ " " ++ ty.unparse)] + else []; + top.errors <- + case ty.typerep of + -- Default instance, must be exported by the class declaration + | skolemType(_) when id.lookupType.found && !isExportedBy(top.grammarName, [dcl.sourceGrammar], top.compiledGrammars) -> + [wrn(top.location, "Orphaned default instance declaration for " ++ fName)] + -- Regular instance, must be exported by the class or type declaration + | t when id.lookupType.found && + !isExportedBy( + top.grammarName, + dcl.sourceGrammar :: map(\ d::TypeDclInfo -> d.sourceGrammar, getTypeDcl(t.typeName, top.env)), + top.compiledGrammars) -> + [wrn(top.location, s"Orphaned instance declaration for ${fName} ${prettyType(t)}")] + | _ -> [] + end; + + cl.constraintPos = instancePos(instContext(fName, ty.typerep), boundVars); + + production attribute headPreDefs :: [Def] with ++; + headPreDefs := []; + + production attribute headDefs :: [Def] with ++; + headDefs := cl.defs; + headDefs <- [currentInstDef(top.grammarName, id.location, fName, ty.typerep)]; + + cl.env = newScopeEnv(headPreDefs, top.env); + ty.env = cl.env; + + body.env = occursEnv(cl.occursDefs, newScopeEnv(headDefs, cl.env)); + body.className = id.lookupType.fullName; + body.instanceType = ty.typerep; + body.expectedClassMembers = if id.lookupType.found then dcl.classMembers else []; + body.frameContexts = superContexts.contexts ++ cl.contexts; +} action { + insert semantic token IdTypeClass_t at id.baseNameLoc; +} + +concrete production instanceDclNoCL +top::AGDcl ::= 'instance' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + top.unparse = s"instance ${id.unparse} ${ty.unparse}\n{\n${body.unparse}\n}"; + + forwards to instanceDcl($1, nilConstraint(location=top.location), '=>', id, ty, $4, body, $6, location=top.location); +} action { + insert semantic token IdTypeClass_t at id.baseNameLoc; +} + +autocopy attribute className::String; +autocopy attribute instanceType::Type; +inherited attribute expectedClassMembers::[Pair]; + +nonterminal InstanceBody with + config, grammarName, env, defs, flowEnv, flowDefs, location, unparse, errors, compiledGrammars, className, instanceType, frameContexts, expectedClassMembers, definedMembers; +nonterminal InstanceBodyItem with + config, grammarName, env, defs, flowEnv, flowDefs, location, unparse, errors, compiledGrammars, className, instanceType, frameContexts, expectedClassMembers, fullName; + +propagate defs, flowDefs, errors on InstanceBody, InstanceBodyItem; + +concrete production consInstanceBody +top::InstanceBody ::= h::InstanceBodyItem t::InstanceBody +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + top.definedMembers = h.fullName :: t.definedMembers; + + h.expectedClassMembers = top.expectedClassMembers; + t.expectedClassMembers = + filter(\ m::Pair -> m.fst != h.fullName, top.expectedClassMembers); +} +concrete production nilInstanceBody +top::InstanceBody ::= +{ + top.unparse = ""; + top.definedMembers = []; + + top.errors <- + flatMap( + \ m::Pair -> + if m.snd then [] else [err(top.location, s"Missing instance member ${m.fst} for class ${top.className}")], + top.expectedClassMembers); +} + +concrete production instanceBodyItem +top::InstanceBodyItem ::= id::QName '=' e::Expr ';' +{ + top.unparse = s"${id.name} = ${e.unparse};"; + + production typeScheme::PolyType = id.lookupValue.typeScheme; + production memberSkolemVars::[TyVar] = freshTyVars(typeScheme.boundVars); + production instSubst::Substitution = + case typeScheme.contexts of + -- Current class context is the first context on the member's type scheme + | instContext(cls, ty) :: _ when cls == top.className -> + composeSubst( + unify(ty, top.instanceType), + -- Skolemize all the other type vars that didn't get instantiated by the instance head + zipVarsIntoSkolemizedSubstitution(typeScheme.boundVars, memberSkolemVars)) + | _ -> emptySubst() -- Fall back in case of errors + end; + production memberContexts::[Context] = + case typeScheme.contexts of + | _ :: cs -> map(performContextSubstitution(_, instSubst), cs) + | _ -> [] + end; + production boundVars::[TyVar] = top.instanceType.freeVariables ++ memberSkolemVars; + + top.errors <- id.lookupValue.errors; + top.errors <- + if !id.lookupValue.found || lookup(top.fullName, top.expectedClassMembers).isJust then [] + else [err(id.location, s"Unexpected instance member ${id.name} for class ${top.className}")]; + + top.fullName = id.lookupValue.fullName; + + local cmDefs::[Def] = + flatMap( + \ c::Context -> c.contextMemberDefs(boundVars, top.grammarName, top.location), + memberContexts); + local cmOccursDefs::[OccursDclInfo] = + flatMap( + \ c::Context -> c.contextMemberOccursDefs(boundVars, top.grammarName, top.location), + memberContexts); + e.env = + newScopeEnv( + cmDefs ++ flatMap(transitiveSuperDefs(top.env, top.instanceType, [], _), flatMap((.instList), cmDefs)), + occursEnv( + cmOccursDefs ++ flatMap(transitiveSuperOccursDefs(top.env, top.instanceType, [], _), flatMap((.instList), cmDefs)), + top.env)); + e.originRules = []; + e.isRoot = true; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); + + e.frame = globalExprContext(top.fullName, foldContexts(top.frameContexts), typeScheme.typerep, myFlowGraph, sourceGrammar=top.grammarName); +} action { + insert semantic token IdTypeClassMember_t at id.baseNameLoc; +} diff --git a/grammars/silver/definition/core/ModuleStmts.sv b/grammars/silver/compiler/definition/core/ModuleStmts.sv similarity index 86% rename from grammars/silver/definition/core/ModuleStmts.sv rename to grammars/silver/compiler/definition/core/ModuleStmts.sv index 1a802d6a5..da6b18e55 100644 --- a/grammars/silver/definition/core/ModuleStmts.sv +++ b/grammars/silver/compiler/definition/core/ModuleStmts.sv @@ -1,6 +1,6 @@ -grammar silver:definition:core; +grammar silver:compiler:definition:core; -imports silver:driver:util; +imports silver:compiler:driver:util; nonterminal ModuleStmts with config, grammarName, location, unparse, errors, moduleNames, defs, occursDefs, exportedGrammars, optionalGrammars, condBuild, compiledGrammars, grammarDependencies; nonterminal ModuleStmt with config, grammarName, location, unparse, errors, moduleNames, defs, occursDefs, exportedGrammars, optionalGrammars, condBuild, compiledGrammars, grammarDependencies; @@ -11,7 +11,7 @@ nonterminal ImportStmts with config, grammarName, location, unparse, errors, mod nonterminal ModuleExpr with config, grammarName, location, unparse, errors, moduleNames, defs, occursDefs, compiledGrammars, grammarDependencies; nonterminal ModuleName with config, grammarName, location, unparse, errors, moduleNames, defs, occursDefs, compiledGrammars, grammarDependencies; -nonterminal NameList with config, grammarName, location, unparse, names; +nonterminal NameList with config, grammarName, location, unparse, names, env; nonterminal WithElems with config, grammarName, location, unparse, envMaps; nonterminal WithElem with config, grammarName, location, unparse, envMaps; @@ -47,23 +47,25 @@ top::Module ::= l::Location production med :: ModuleExportedDefs = moduleExportedDefs(l, compiledGrammars, grammarDependencies, need, seen); - local defs_after_only :: [Def] = - if null(onlyFilter) then med.defs - else filter(filterDefOnEnvItem(envItemInclude(_, onlyFilter), _), med.defs); + local defs :: Defs = foldr(consDefs, nilDefs(), med.defs); + defs.filterItems = onlyFilter; - local defs_after_hiding :: [Def] = - if null(hidingFilter) then defs_after_only - else filter(filterDefOnEnvItem(envItemExclude(_, hidingFilter), _), defs_after_only); + local defs_after_only :: Defs = + if null(onlyFilter) then defs else defs.filterOnly; + defs_after_only.filterItems = hidingFilter; - local defs_after_renames :: [Def] = - if null(withRenames) then defs_after_hiding - else map(mapDefOnEnvItem(envItemApplyRenaming(_, withRenames), _), defs_after_hiding); + local defs_after_hiding :: Defs = + if null(hidingFilter) then defs_after_only else defs_after_only.filterHiding; + defs_after_hiding.withRenames = withRenames; - local defs_after_prepend :: [Def] = - if asPrepend == "" then defs_after_renames - else map(mapDefOnEnvItem(envItemPrepend(_, asPrepend ++ ":"), _), defs_after_renames); + local defs_after_renames :: Defs = + if null(withRenames) then defs_after_hiding else defs_after_hiding.renamed; + defs_after_renames.pfx = asPrepend ++ ":"; - top.defs := defs_after_prepend; + local defs_after_prepend :: Defs = + if asPrepend == "" then defs_after_renames else defs_after_renames.prepended; + + top.defs := defs_after_prepend.defs; top.occursDefs := med.occursDefs; top.errors := med.errors; } @@ -98,7 +100,7 @@ top::ModuleExportedDefs ::= l::Location compiledGrammars::EnvTree 1 + then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- + if isLower(substring(0,1,id.name)) + then [err(id.location, "Types must be capitalized. Invalid nonterminal name " ++ id.name)] + else []; +} action { + insert semantic token IdTypeDcl_t at id.location; +} + +nonterminal NTDeclQualifiers with location, errors; + +synthesized attribute closed :: Boolean occurs on NTDeclQualifiers; +synthesized attribute tracked :: Boolean occurs on NTDeclQualifiers; + +concrete production nilNTQualifier +top::NTDeclQualifiers ::= +{ + -- This controls the default closed-ness and tracked-ness of nonterminals + top.closed = false; + top.tracked = false; + + top.errors := []; +} + +concrete production closedNTQualifier +top::NTDeclQualifiers ::= 'closed' rest::NTDeclQualifiers +{ + top.closed = true; + top.tracked = rest.tracked; + + top.errors := rest.errors; + top.errors <- if rest.closed then [err(top.location, "Duplicate 'closed' qualifier")] else []; +} + +concrete production trackedNTQualifier +top::NTDeclQualifiers ::= 'tracked' rest::NTDeclQualifiers +{ + top.closed = rest.closed; + top.tracked = true; + + top.errors := rest.errors; + top.errors <- if rest.tracked then [err(top.location, "Duplicate 'tracked' qualifier")] else []; +} + +nonterminal NonterminalModifiers with config, location, unparse, errors, env, nonterminalName; -- 0 or some +nonterminal NonterminalModifierList with config, location, unparse, errors, env, nonterminalName; -- 1 or more +closed nonterminal NonterminalModifier with config, location, unparse, errors, env, nonterminalName; -- 1 + +propagate errors on NonterminalModifiers, NonterminalModifierList, NonterminalModifier; + +concrete production nonterminalModifiersNone +top::NonterminalModifiers ::= +{ + top.unparse = ""; +} +concrete production nonterminalModifierSome +top::NonterminalModifiers ::= nm::NonterminalModifierList +{ + top.unparse = nm.unparse; +} + +concrete production nonterminalModifierSingle +top::NonterminalModifierList ::= nm::NonterminalModifier +{ + top.unparse = nm.unparse; +} +concrete production nonterminalModifiersCons +top::NonterminalModifierList ::= h::NonterminalModifier ',' t::NonterminalModifierList +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + diff --git a/grammars/silver/compiler/definition/core/OccursDcl.sv b/grammars/silver/compiler/definition/core/OccursDcl.sv new file mode 100644 index 000000000..18ebabafe --- /dev/null +++ b/grammars/silver/compiler/definition/core/OccursDcl.sv @@ -0,0 +1,186 @@ +grammar silver:compiler:definition:core; + +abstract production defaultAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + + -- TODO: this location is highly unreliable. + + -- We must unconditionally emit the occurs def in order to signal to the + -- environment mechanism that we're in a different namespace than + -- the types/attributes. + top.occursDefs := [ + (if !at.lookupAttribute.dcl.isAnnotation then occursDcl else annoInstanceDcl)( + nt.lookupType.fullName, at.lookupAttribute.fullName, + protontty, + if ntParamKinds == map((.kindrep), nttl.types) && map((.kindrep), atTypeScheme.boundVars) == map((.kindrep), attl.types) + then protoatty + else errorType(), + sourceGrammar=top.grammarName, sourceLocation=at.location)]; + + -- binding errors in looking up these names. + top.errors <- nt.lookupType.errors ++ + -- The nonterminal type list is strictly type VARIABLES only + nttl.errorsTyVars; + + top.errors <- + if attl.missingCount > 0 + then [err(attl.location, "Attribute type arguments cannot contain _")] + else []; + + nttl.initialEnv = top.env; + attl.env = nttl.envBindingTyVars; + nttl.env = nttl.envBindingTyVars; + + local ntTypeScheme::PolyType = nt.lookupType.typeScheme; + local atTypeScheme::PolyType = at.lookupAttribute.typeScheme; + + -- Make sure we get the number and kind of type variables correct for the NT + local ntParamKinds :: [Kind] = + case nt.lookupType.dcls of + | ntDcl(_, ks, _, _) :: _ -> ks + | _ -> [] + end; + top.errors <- + if null(nt.lookupType.dcls) then [] + else if length(ntParamKinds) != length(nttl.types) + then + [err(nt.location, + nt.name ++ " expects " ++ toString(length(ntParamKinds)) ++ + " type variables, but " ++ toString(length(nttl.types)) ++ " were provided.")] + else if ntParamKinds != map((.kindrep), nttl.types) + then + [err(nt.location, + nt.name ++ " had kind " ++ foldr(arrowKind, starKind(), ntParamKinds).typepp ++ + " but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), nttl.types)) ++ ".")] + else []; + + -- Make sure we get the number and kind of type variables correct for the ATTR + top.errors <- + if length(atTypeScheme.boundVars) != length(attl.types) + then [err(at.location, + at.name ++ " expects " ++ toString(length(atTypeScheme.boundVars)) ++ + " type variables, but " ++ toString(length(attl.types)) ++ " were provided.")] + else if map((.kindrep), atTypeScheme.boundVars) != map((.kindrep), attl.types) + then [err(at.location, + at.name ++ " has kind " ++ prettyKind(foldr(arrowKind, starKind(), map((.kindrep), atTypeScheme.boundVars))) ++ + " but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), attl.types)) ++ ".")] + else []; + + -- We have 4 types. + -- 1: A type, from env, for the nonterminal (unapplied) + -- 2: A type, from syntax, for the nonterminal (applied to type vars) + -- 3: A type, from env, for the attribute (with bound type vars) + -- 4: A type, from syntax, for the attribute (sharing the same type vars as #2) + + -- Our goal is to be able to take a (unknown) nonterminal type + -- and yield the appropriate attribute type it corresponds to. + + -- To that end, we want two things: + -- 1: A type that we can unify with some nonterminal type. + -- 2: A type that, under that unification, will be the resulting attribute type. + + -- So we generate a list of fresh type variables corresponding to the types of #2, + -- and generate two substitutions: + -- 1: Rewrite the tyvars of type #3 to the types of type #4. + -- 2: Rewrite our local tyvars to fresh variables. + + -- Thus, we apply #1 to our fresh type vars, perform both substitutions on #3, and get our goal. + + -- Fresh type variables that will go in the environment + local tyVars :: [TyVar] = freshTyVars(nttl.freeVariables); + + -- Apply the nonterminal type to the type variables. + -- NOT .monoType so we do something sensible if someone does "occurs on TypeAlias" or something. + production protontty :: Type = appTypes(ntTypeScheme.typerep, map(varType, tyVars)); + + -- This renames the vars from the environment + -- at's env types -> type params containing local skolem vars (vars -> types) + local rewrite_from :: Substitution = zipVarsAndTypesIntoSubstitution(atTypeScheme.boundVars, attl.types); + + -- local skolem vars -> fresh type vars (vars -> vars) + local rewrite_to :: Substitution = zipVarsIntoSubstitution(nttl.freeVariables, tyVars); + + -- These have to be two separate renamings, because the second renaming replaces names getting substituted in by the first renaming. + production protoatty :: Type = performRenaming(performRenaming(atTypeScheme.typerep, rewrite_from), rewrite_to); + + -- Now, finally, make sure we're not "redefining" the occurs. + production occursCheck :: [OccursDclInfo] = getOccursDcl(at.lookupAttribute.fullName, nt.lookupType.fullName, top.env); + + top.errors <- + if length(occursCheck) > 1 + then [err(at.location, "Attribute '" ++ at.name ++ "' already occurs on '" ++ nt.name ++ "'.")] + else []; + + top.errors <- + if nt.lookupType.found && (!nt.lookupType.dcl.isType || !isDecorable(ntTypeScheme.typerep, top.env)) + then [err(nt.location, nt.name ++ " is not a nonterminal. Attributes can only occur on nonterminals.")] + else []; + + top.errors <- + if !nt.lookupType.found || !at.lookupAttribute.found || !at.lookupAttribute.dcl.isAnnotation || + isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars) then [] + else [err(top.location, "Annotations for a nonterminal must be in a module exported by the nonterminal's declaring grammar.")]; +} + +abstract production errorAttributionDcl +top::AGDcl ::= msg::[Message] at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + top.occursDefs := []; + top.errors <- msg; + + nttl.initialEnv = top.env; + attl.env = nttl.envBindingTyVars; + nttl.env = nttl.envBindingTyVars; + + -- Decorate everything else to still check for errors + top.errors <- + -- binding errors in looking up these names. + nt.lookupType.errors ++ + -- The nonterminal type list is strictly type VARIABLES only + nttl.errorsTyVars; + + -- Make sure we get the number and kinds of tyvars correct for the NT + top.errors <- + case nt.lookupType.dcls of + | ntDcl(_, ks, _, _) :: _ when length(ks) != length(nttl.types) -> + [err(nt.location, + nt.name ++ " expects " ++ toString(length(ks)) ++ + " type variables, but " ++ toString(length(nttl.types)) ++ " were provided.")] + | ntDcl(_, ks, _, _) :: _ when ks != map((.kindrep), nttl.types) -> + [err(nt.location, + nt.name ++ " had kind " ++ foldr(arrowKind, starKind(), ks).typepp ++ + " but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), nttl.types)) ++ ".")] + | _ -> [] + end; +} + +concrete production attributionDcl +top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + + -- Workaround for circular dependency due to dispatching on env: + -- Nothing used to build the env namespaces on which we dispatch can depend on + -- the forward here. + -- Attribution (occurs) defs, which obviously must depend on this forward, are + -- specified by a seperate occursDefs attribute. + -- Attribution dispatch productions should only define occursDefs (i.e. no new + -- nonterminals, productions, attributes, etc.) + top.defs := []; + top.moduleNames := []; + + forwards to + (if !at.lookupAttribute.found + then errorAttributionDcl(at.lookupAttribute.errors, _, _, _, _, location=_) + else at.lookupAttribute.dcl.attributionDispatcher)(at, attl, nt, nttl, top.location); +} + +concrete production annotateDcl +top::AGDcl ::= 'annotation' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' +{ + forwards to attributionDcl('attribute', at, attl, $4, $5, nt, nttl, $8, location=top.location); +} + diff --git a/grammars/silver/compiler/definition/core/ProductionBody.sv b/grammars/silver/compiler/definition/core/ProductionBody.sv new file mode 100644 index 000000000..c1b8e21ba --- /dev/null +++ b/grammars/silver/compiler/definition/core/ProductionBody.sv @@ -0,0 +1,448 @@ +grammar silver:compiler:definition:core; + +nonterminal ProductionBody with + config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, + productionAttributes, uniqueSignificantExpression; +nonterminal ProductionStmts with + config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, + productionAttributes, uniqueSignificantExpression, originRules; +nonterminal ProductionStmt with + config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, + productionAttributes, uniqueSignificantExpression, originRules; + +flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, downSubst} + on ProductionBody; +flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, downSubst, originRules} + on ProductionStmts; +flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, downSubst, finalSubst, originRules} + on ProductionStmt; +flowtype forward {decorate} on ProductionBody, ProductionStmts, ProductionStmt; + +nonterminal DefLHS with + config, grammarName, env, location, unparse, errors, frame, compiledGrammars, name, typerep, defLHSattr, found, originRules; + +flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, defLHSattr, originRules} + on DefLHS; + +nonterminal ForwardInhs with + config, grammarName, env, location, unparse, errors, frame, compiledGrammars, originRules; +nonterminal ForwardInh with + config, grammarName, env, location, unparse, errors, frame, compiledGrammars, originRules; +nonterminal ForwardLHSExpr with + config, grammarName, env, location, unparse, errors, frame, name, typerep, originRules; + +{-- + - Context for ProductionStmt blocks. (Indicates function, production, aspect, etc) + - Includes singature for those contexts with a signature. + -} +autocopy attribute frame :: BlockContext; + +{-- + - Defs of attributes that should be wrapped up as production attributes. + -} +monoid attribute productionAttributes :: [Def]; +{-- + - Either the 'forward' expression, or the 'return' expression. + - I gave it an obtuse name so it could easily be renamed in the future. + -} +monoid attribute uniqueSignificantExpression :: [Decorated Expr]; + +{-- + - The attribute we're defining on a DefLHS. + -} +inherited attribute defLHSattr :: Decorated QNameAttrOccur; + +-- Notes that should be attached to stuff constructed/modified in rules in this productionBody +-- Notes flow 'up' in this from statements and then back 'down' into the via originRules. +synthesized attribute originRuleDefs :: [Decorated Expr] occurs on ProductionStmt, ProductionStmts; + +propagate errors on ProductionBody, ProductionStmts, ProductionStmt, DefLHS, ForwardInhs, ForwardInh, ForwardLHSExpr; +propagate defs, productionAttributes, uniqueSignificantExpression on ProductionBody, ProductionStmts; + + +concrete production productionBody +top::ProductionBody ::= '{' stmts::ProductionStmts '}' +{ + top.unparse = stmts.unparse; + + stmts.originRules = stmts.originRuleDefs; +} + +concrete production productionStmtsNil +top::ProductionStmts ::= +{ + top.unparse = ""; + + top.originRuleDefs = []; +} + +concrete production productionStmtsSnoc +top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + + top.originRuleDefs = h.originRuleDefs ++ t.originRuleDefs; +} + +---------- + +abstract production productionStmtAppend +top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt +{ + top.unparse = h.unparse ++ "\n" ++ t.unparse; + + top.originRuleDefs = h.originRuleDefs ++ t.originRuleDefs; + propagate defs, productionAttributes, uniqueSignificantExpression; +} + +abstract production errorProductionStmt +top::ProductionStmt ::= e::[Message] +{ + top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; + top.errors <- e; +} + +-------------------------------------------------------------------------------- + +aspect default production +top::ProductionStmt ::= +{ + -- as is usual for defaults ("base classes") + -- can't provide unparse or location, errors should NOT be defined! + top.productionAttributes := []; + top.uniqueSignificantExpression := []; + + top.defs := []; + + top.originRuleDefs = []; +} + +concrete production attachNoteStmt +top::ProductionStmt ::= 'attachNote' note::Expr ';' +{ + top.unparse = "attachNote " ++ note.unparse; + note.isRoot = false; -- Irrelevant because OriginNotes aren't tracked + note.originRules = []; --Prevents cyclical dependency when translating + top.originRuleDefs = [note]; +} + +concrete production returnDef +top::ProductionStmt ::= 'return' e::Expr ';' +{ + top.unparse = "\treturn " ++ e.unparse ++ ";"; + + top.uniqueSignificantExpression := [e]; + + top.errors <- if !top.frame.permitReturn + then [err(top.location, "Return is not valid in this context. (They are only permitted in function declarations.)")] + else []; + + e.isRoot = true; +} + +concrete production localAttributeDcl +top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' +{ + top.unparse = "\tlocal attribute " ++ a.unparse ++ "::" ++ te.unparse ++ ";"; + + production attribute fName :: String; + fName = s"${top.frame.fullName}:local:${top.grammarName}:${implode("_", filter(isAlpha, explode(".", top.location.filename)))}:${toString(top.location.line)}:${toString(top.location.column)}:${a.name}"; + + top.defs := [localDef(top.grammarName, a.location, fName, te.typerep)]; + + top.errors <- + if length(getValueDclInScope(a.name, top.env)) > 1 + then [err(a.location, "Value '" ++ a.name ++ "' is already bound.")] + else []; + + top.errors <- if !top.frame.permitLocalAttributes + then [err(top.location, "Local attributes are not valid in this context.")] + else []; +} + +concrete production productionAttributeDcl +top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr ';' +{ + top.unparse = "\tproduction attribute " ++ a.unparse ++ "::" ++ te.unparse ++ ";"; + + production attribute fName :: String; + fName = top.frame.fullName ++ ":local:" ++ top.grammarName ++ ":" ++ a.name; + + top.productionAttributes := [localDef(top.grammarName, a.location, fName, te.typerep)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(a.location, "Value '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- if !top.frame.permitProductionAttributes + then [err(top.location, "Production attributes are not valid in this context.")] + else []; +} + +concrete production forwardsTo +top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' +{ + top.unparse = "\tforwards to " ++ e.unparse; + + e.isRoot = true; + + top.productionAttributes := [forwardDef(top.grammarName, top.location, top.frame.signature.outputElement.typerep)]; + top.uniqueSignificantExpression := [e]; + + top.errors <- if !top.frame.permitForward + then [err(top.location, "Forwarding is not permitted in this context. (Only permitted in non-aspect productions.)")] + else []; +} + +concrete production forwardsToWith +top::ProductionStmt ::= 'forwards' 'to' e::Expr 'with' '{' inh::ForwardInhs '}' ';' +{ + top.unparse = "\tforwards to " ++ e.unparse ++ " with {" ++ inh.unparse ++ "};"; + + forwards to productionStmtAppend( + forwardsTo($1, $2, $3, $8, location=top.location), + forwardingWith('forwarding', $4, $5, inh, $7, $8, location=top.location), + location=top.location); +} + +concrete production forwardingWith +top::ProductionStmt ::= 'forwarding' 'with' '{' inh::ForwardInhs '}' ';' +{ + top.unparse = "\tforwarding with {" ++ inh.unparse ++ "};"; + + production attribute fwdDcls :: [ValueDclInfo]; + fwdDcls = getValueDcl("forward", top.env); + + top.errors <- if null(fwdDcls) + then [err(top.location, "'forwarding with' clause for a production that does not forward!")] + else []; +} + +-- TODO eliminate these (/ combine with the ones for decorate expression) +concrete production forwardInh +top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' +{ + top.unparse = lhs.unparse ++ " = " ++ e.unparse ++ ";"; + + e.isRoot = true; +} + +concrete production forwardInhsOne +top::ForwardInhs ::= lhs::ForwardInh +{ + top.unparse = lhs.unparse; +} + +concrete production forwardInhsCons +top::ForwardInhs ::= lhs::ForwardInh rhs::ForwardInhs +{ + top.unparse = lhs.unparse ++ " " ++ rhs.unparse; +} + +concrete production forwardLhsExpr +top::ForwardLHSExpr ::= q::QNameAttrOccur +{ + top.name = q.name; + top.unparse = q.unparse; + + top.typerep = q.typerep; + + q.attrFor = top.frame.signature.outputElement.typerep; +} + +concrete production attributeDef +top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur '=' e::Expr ';' +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + -- defs must stay here explicitly, because we dispatch on types in the forward here! + top.productionAttributes := []; + top.defs := []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + local problems :: [Message] = + if attr.found && attr.attrDcl.isAnnotation + then [err(attr.location, attr.name ++ " is an annotation, which are supplied to productions as arguments, not defined as equations.")] + else dl.errors ++ attr.errors; + + forwards to + -- oddly enough we may have no errors and need to forward to error production: + -- consider "production foo top::DoesNotExist ::= { top.errors = ...; }" + -- where top is a valid reference to a type that is an error type + -- so there is an error elsewhere + (if !dl.found || !attr.found || !null(problems) + then errorAttributeDef(problems, _, _, _, location=_) + else attr.attrDcl.attrDefDispatcher)(dl, attr, e, top.location); +} + +{- This is a helper that exist primarily to decorate 'e' and add its error messages to the list. + Invariant: msg should not be null! -} +abstract production errorAttributeDef +top::ProductionStmt ::= msg::[Message] dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.isRoot = true; + + forwards to errorProductionStmt(msg ++ e.errors, location=top.location); +} + +abstract production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.isRoot = true; +} + +abstract production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.isRoot = true; +} + +concrete production concreteDefLHS +top::DefLHS ::= q::QName +{ + top.name = q.name; + top.unparse = q.unparse; + + forwards to (if null(q.lookupValue.dcls) + then errorDefLHS(_, location=_) + else q.lookupValue.dcl.defLHSDispatcher)(q, top.location); +} action { + if (contains(q.name, sigNames)) { + insert semantic token IdSigName_t at q.baseNameLoc; + } +} + +abstract production errorDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = false; + + top.errors <- q.lookupValue.errors; + top.errors <- + if top.typerep.isError then [] else [err(q.location, "Cannot define attributes on " ++ q.name)]; + top.typerep = q.lookupValue.typeScheme.monoType; +} + +concrete production concreteDefLHSfwd +top::DefLHS ::= q::'forward' +{ + forwards to concreteDefLHS(qName(q.location, "forward"), location=top.location); +} + +abstract production childDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; + + local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; + + top.errors <- + if existingProblems || top.found then [] + else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on child '" ++ q.name ++ "'")]; + + top.typerep = q.lookupValue.typeScheme.monoType; +} + +abstract production lhsDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = !existingProblems && top.defLHSattr.attrDcl.isSynthesized; + + local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; + + top.errors <- + if existingProblems || top.found then [] + else [err(q.location, "Cannot define inherited attribute '" ++ top.defLHSattr.name ++ "' on the lhs '" ++ q.name ++ "'")]; + + top.typerep = q.lookupValue.typeScheme.monoType; +} + +abstract production localDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; + + local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; + + top.errors <- + if existingProblems || top.found then [] + else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on local '" ++ q.name ++ "'")]; + + top.typerep = q.lookupValue.typeScheme.monoType; +} + +abstract production forwardDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; + + local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; + + top.errors <- + if existingProblems || top.found then [] + else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on forward")]; + + top.typerep = q.lookupValue.typeScheme.monoType; +} + +----- done with DefLHS + +concrete production valueEq +top::ProductionStmt ::= val::QName '=' e::Expr ';' +{ + top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; + + top.errors <- val.lookupValue.errors; + + -- defs must stay here explicitly, because we dispatch on types in the forward here! + top.productionAttributes := []; + top.defs := []; + + forwards to (if null(val.lookupValue.dcls) + then errorValueDef(_, _, location=_) + else val.lookupValue.dcl.defDispatcher)(val, e, top.location); +} + +abstract production errorValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; + + e.isRoot = true; + + top.errors <- + if val.lookupValue.typeScheme.isError then [] + else [err(val.location, val.name ++ " cannot be assigned to.")]; +} + +abstract production localValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; + + -- val is already valid here + + e.isRoot = true; + + -- TODO: missing redefinition check +} + diff --git a/grammars/silver/compiler/definition/core/ProductionDcl.sv b/grammars/silver/compiler/definition/core/ProductionDcl.sv new file mode 100644 index 000000000..6002b6b6b --- /dev/null +++ b/grammars/silver/compiler/definition/core/ProductionDcl.sv @@ -0,0 +1,177 @@ +grammar silver:compiler:definition:core; + +nonterminal ProductionSignature with config, grammarName, env, location, unparse, errors, defs, constraintDefs, occursDefs, namedSignature, signatureName; +nonterminal ProductionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement; +nonterminal ProductionRHS with config, grammarName, env, location, unparse, errors, defs, inputElements, elementCount; +nonterminal ProductionRHSElem with config, grammarName, env, location, unparse, errors, defs, inputElements, deterministicCount; + +flowtype forward {env, signatureName} on ProductionSignature; +flowtype forward {env} on ProductionLHS, ProductionRHS; +flowtype forward {deterministicCount, env} on ProductionRHSElem; + +flowtype decorate {forward, grammarName, flowEnv} on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; + +propagate errors on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; +propagate defs on ProductionRHS; + +{-- + - Used to help give names to children, when names are omitted. + -} +inherited attribute deterministicCount :: Integer; +synthesized attribute elementCount::Integer; + +{-- + - Given to signature syntax, so as to construct a named signature representation. + -} +inherited attribute signatureName :: String; + +{-- + - Defs from the constraint list are passed seperately from the rest of the signature defs, + - to avoid an infinite recursion. + -} +synthesized attribute constraintDefs::[Def]; + +{-- + - The signature names of the production body currently being parsed, for use in reporting semantic tokens. + -} +parser attribute sigNames::[String] action { sigNames = []; }; + +concrete production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + top.unparse = "abstract production " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production namedSig :: NamedSignature = ns.namedSignature; + + top.defs := prodDef(top.grammarName, id.location, namedSig, length(body.uniqueSignificantExpression) > 0) :: + if null(body.productionAttributes) then [] + else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; + + top.errors <- + if length(getValueDclAll(fName, top.env)) > 1 + then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] + + -- TODO: Narrow this down to just a list of productions of the same nonterminal before deciding to error. + else if length(getValueDclAll(id.name, top.env)) > 1 + then [err(top.location, "Production " ++ id.name ++ " shares a name with another production from an imported grammar. Either this production is meant to be an aspect, or you should use 'import ... with " ++ id.name ++ " as ...' to change the other production's apparent name.")] + else []; + + top.errors <- + if length(body.uniqueSignificantExpression) > 1 + then [err(top.location, "Production '" ++ id.name ++ "' has more than one forward declaration.")] + else []; + + top.errors <- + if isLower(substring(0,1,id.name)) then [] + else [wrn(id.location, s"(future) ${id.name}: productions may be required to begin with a lower-case letter.")]; + + production attribute sigDefs :: [Def] with ++; + sigDefs := ns.defs; + + ns.signatureName = fName; + ns.env = newScopeEnv(sigDefs, top.env); + + local attribute prodAtts :: [Def]; + prodAtts = defsFromPADcls(getProdAttrs(fName, top.env), namedSig); + + body.env = occursEnv(ns.occursDefs, newScopeEnv(body.defs ++ sigDefs ++ ns.constraintDefs ++ prodAtts, top.env)); + body.frame = productionContext(namedSig, myFlowGraph, sourceGrammar=top.grammarName); -- graph from flow:env +} action { + insert semantic token IdFnProdDcl_t at id.location; + sigNames = []; +} + +concrete production productionSignature +top::ProductionSignature ::= cl::ConstraintList '=>' lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + top.unparse = s"${cl.unparse} => ${lhs.unparse} ::= ${rhs.unparse}"; + + cl.constraintPos = signaturePos(top.namedSignature); + rhs.env = occursEnv(cl.occursDefs, top.env); + + top.defs := lhs.defs ++ rhs.defs; + top.constraintDefs = cl.defs; + top.occursDefs := cl.occursDefs; + top.namedSignature = + namedSignature( + top.signatureName, + foldContexts(cl.contexts), + foldNamedSignatureElements(rhs.inputElements), + lhs.outputElement, + foldNamedSignatureElements(annotationsForNonterminal(lhs.outputElement.typerep, top.env))); +} action { + sigNames = foldNamedSignatureElements(lhs.outputElement :: rhs.inputElements).elementNames; +} + +concrete production productionSignatureNoCL +top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + top.unparse = s"${lhs.unparse} ::= ${rhs.unparse}"; + + forwards to productionSignature(nilConstraint(location=top.location), '=>', lhs, $2, rhs, location=top.location); +} action { + sigNames = foldNamedSignatureElements(lhs.outputElement :: rhs.inputElements).elementNames; +} + +concrete production productionLHS +top::ProductionLHS ::= id::Name '::' t::TypeExpr +{ + top.unparse = id.unparse ++ "::" ++ t.unparse; + + top.outputElement = namedSignatureElement(id.name, t.typerep); + + top.defs := [lhsDef(top.grammarName, t.location, id.name, t.typerep)]; + + top.errors <- + if length(getValueDclInScope(id.name, top.env)) > 1 + then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] + else []; +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +concrete production productionRHSNil +top::ProductionRHS ::= +{ + top.unparse = ""; + + top.inputElements = []; + top.elementCount = 0; +} + +concrete production productionRHSCons +top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS +{ + top.unparse = h.unparse ++ " " ++ t.unparse; + + top.inputElements = h.inputElements ++ t.inputElements; + top.elementCount = 1 + t.elementCount; + h.deterministicCount = t.elementCount; +} + +concrete production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + top.unparse = id.unparse ++ "::" ++ t.unparse; + + top.inputElements = [namedSignatureElement(id.name, t.typerep)]; + + top.defs := [childDef(top.grammarName, t.location, id.name, t.typerep)]; + + top.errors <- + if length(getValueDclInScope(id.name, top.env)) > 1 + then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] + else []; +} action { + insert semantic token IdSigNameDcl_t at id.location; +} + +concrete production productionRHSElemType +top::ProductionRHSElem ::= t::TypeExpr +{ + top.unparse = t.unparse; + + forwards to productionRHSElem(name("_G_" ++ toString(top.deterministicCount), t.location), '::', t, location=top.location); +} + diff --git a/grammars/silver/compiler/definition/core/Project.sv b/grammars/silver/compiler/definition/core/Project.sv new file mode 100644 index 000000000..0373d9a3d --- /dev/null +++ b/grammars/silver/compiler/definition/core/Project.sv @@ -0,0 +1,38 @@ +grammar silver:compiler:definition:core; + +exports silver:langutil; + +-- The following are grammar-wide imports for 'core' + +-- The 'Type' syntax. (I made this separate to try to make s:d:core less of a "dump everything here" grammar.) +imports silver:compiler:definition:type:syntax; + +-- Type Representation +imports silver:compiler:definition:type; + +-- Environment Representation +imports silver:compiler:definition:env; + +-- Utilities + +option silver:compiler:definition:concrete_syntax; +option silver:compiler:definition:flow:syntax; +option silver:compiler:modification:lambda_fn; +option silver:compiler:modification:let_fix; +option silver:compiler:modification:primitivepattern; +option silver:compiler:modification:ffi; +option silver:compiler:modification:copper; +option silver:compiler:modification:defaultattr; +option silver:compiler:modification:collection; +option silver:compiler:modification:copper_mda; + +-- The list extension doesn't need to be an option here, +-- it only needs to be one of silver:compiler:definition:type +-- option silver:compiler:modification:list; + +option silver:compiler:extension:testing; -- TODO this is about that buggy experiment of Eric's... + +-- These are somewhat less than desirable exports, due to the modularity analysis. +exports silver:compiler:analysis:typechecking:core; +exports silver:compiler:definition:flow:env; + diff --git a/grammars/silver/compiler/definition/core/QName.sv b/grammars/silver/compiler/definition/core/QName.sv new file mode 100644 index 000000000..daf2e7f41 --- /dev/null +++ b/grammars/silver/compiler/definition/core/QName.sv @@ -0,0 +1,268 @@ +grammar silver:compiler:definition:core; + +{-- + - Qualified names of the form 'a:b:c:d...' + -} +nonterminal QName with config, name, location, grammarName, env, unparse, qNameType, baseNameLoc; +{-- + - Qualified names where the LAST name has an upper case first letter. + -} +nonterminal QNameType with config, name, location, grammarName, env, unparse, baseNameLoc; + +flowtype decorate {env} on QName, QNameType; + +{-- + - The list of declarations resulting from looking up this QName + -} +synthesized attribute dcls :: [a]; + +synthesized attribute qNameType::QNameType; +synthesized attribute baseNameLoc::Location; + +-- TODO: for consistency, the order of these args should be flipped: +function qName +QName ::= l::Location s::String +{ + return qNameId(nameIdLower(terminal(IdLower_t, s, l), location=l), location=l); +} + +concrete production qNameId +top::QName ::= id::Name +{ + top.name = id.name; + top.unparse = id.unparse; + top.qNameType = qNameTypeId(terminal(IdUpper_t, id.name, id.location), location=id.location); + top.baseNameLoc = id.location; + + top.lookupValue = decorate customLookup("value", getValueDcl(top.name, top.env), top.name, top.location) with {}; + top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; + top.lookupAttribute = decorate customLookup("attribute", getAttrDcl(top.name, top.env), top.name, top.location) with {}; +} + +concrete production qNameCons +top::QName ::= id::Name ':' qn::QName +{ + top.name = id.name ++ ":" ++ qn.name; + top.unparse = id.unparse ++ ":" ++ qn.unparse; + top.qNameType = qNameTypeCons(id, ':', qn.qNameType, location=top.location); + top.baseNameLoc = qn.baseNameLoc; + + top.lookupValue = decorate customLookup("value", getValueDcl(top.name, top.env), top.name, top.location) with {}; + top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; + top.lookupAttribute = decorate customLookup("attribute", getAttrDcl(top.name, top.env), top.name, top.location) with {}; +} action { + insert semantic token IdGrammarName_t at id.location; +} + +abstract production qNameError +top::QName ::= msg::[Message] +{ + top.name = "err"; + top.unparse = ""; + top.qNameType = qNameTypeId(terminal(IdUpper_t, "Err", top.location), location=top.location); + top.baseNameLoc = top.location; + + top.lookupValue = decorate errorLookup(msg) with {}; + top.lookupType = decorate errorLookup(msg) with {}; + top.lookupAttribute = decorate errorLookup(msg) with {}; +} + +nonterminal QNameLookup with fullName, typeScheme, errors, dcls, dcl, found; + +synthesized attribute lookupValue :: Decorated QNameLookup occurs on QName; +synthesized attribute lookupType :: Decorated QNameLookup occurs on QName; +synthesized attribute lookupAttribute :: Decorated QNameLookup occurs on QName; + +flowtype QName = lookupValue {env}, lookupType {env}, lookupAttribute {env}; + +abstract production customLookup +attribute fullName {} occurs on a, +attribute typeScheme {} occurs on a, +annotation sourceLocation occurs on a => +top::QNameLookup ::= kindOfLookup::String dcls::[a] name::String l::Location +{ + top.dcls = dcls; + top.found = !null(top.dcls); -- currently accurate + top.dcl = + if top.found then head(top.dcls) + else error("INTERNAL ERROR: Accessing dcl of " ++ kindOfLookup ++ " " ++ name ++ " at " ++ l.unparse); + + top.fullName = if top.found then top.dcl.fullName else "undeclared:value:" ++ name; + + top.typeScheme = if top.found then top.dcl.typeScheme else monoType(errorType()); + + top.errors := + (if top.found then [] + else [err(l, "Undeclared " ++ kindOfLookup ++ " '" ++ name ++ "'.")]) ++ + (if length(top.dcls) <= 1 then [] + else [err(l, "Ambiguous reference to " ++ kindOfLookup ++ " '" ++ name ++ "'. Possibilities are:\n" ++ printPossibilities(top.dcls))]); +} + +abstract production errorLookup +top::QNameLookup ::= msg::[Message] +{ + top.dcls = []; + top.found = true; + top.dcl = error("dcl demanded from errorLookup"); + top.fullName = "err"; + top.typeScheme = monoType(errorType()); + top.errors := msg; +} + +function printPossibilities +attribute fullName {} occurs on a, +annotation sourceLocation occurs on a => +String ::= lst::[a] +{ + return implode("\n", map(dclinfo2possibility, lst)); +} +function dclinfo2possibility +attribute fullName {} occurs on a, +annotation sourceLocation occurs on a => +String ::= dcl::a +{ + -- TODO: perhaps some way of including types, when they are relevant (attributes, values) + return "\t" ++ dcl.fullName ++ " (" ++ dcl.sourceLocation.filename ++ ":" ++ toString(dcl.sourceLocation.line) ++ ")"; +} + + +---- Right now, this is only used for types and type classes: +attribute lookupType occurs on QNameType; + +concrete production qNameTypeId +top::QNameType ::= id::IdUpper_t +{ + top.name = id.lexeme; + top.unparse = id.lexeme; + top.baseNameLoc = id.location; + + top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; +} + +concrete production qNameTypeCons +top::QNameType ::= id::Name ':' qn::QNameType +{ + top.name = id.name ++ ":" ++ qn.name; + top.baseNameLoc = qn.baseNameLoc; + top.unparse = id.unparse ++ ":" ++ qn.unparse; + + top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; +} action { + insert semantic token IdGrammarName_t at id.location; +} + +{-- + - Qualified name looked up CONTEXTUALLY + -} +nonterminal QNameAttrOccur with config, name, location, grammarName, env, unparse, attrFor, errors, typerep, dcl, attrDcl, found, attrFound; + +flowtype QNameAttrOccur = decorate {grammarName, env, attrFor}, dcl {decorate}, attrDcl {decorate}; + +{-- + - For QNameAttrOccur, the name of the LHS to look up this attribute on. + - i.e. + -} +inherited attribute attrFor :: Type; +synthesized attribute attrDcl :: AttributeDclInfo; + +{-- + - Whether lookup was successful. Better than `null(_.errors)` because errors may be suppressed + -} +synthesized attribute found :: Boolean; + +{-- + - Whether we found the *attribute*. Sometimes we still want to know this even when the nonterminal was an error + -} +synthesized attribute attrFound :: Boolean; + +{-- + - Used like `x.`. + - @param at the name of an attribute + - @inh attrFor the type this attribute should be on + -} +concrete production qNameAttrOccur +top::QNameAttrOccur ::= at::QName +{ + top.name = at.name; + top.unparse = at.unparse; + + -- We start with all attributes we find with the name `at`: + local attrs :: [AttributeDclInfo] = at.lookupAttribute.dcls; + + -- Then we filter to just those that appear to have an occurrence on `top.attrFor`: + local narrowed :: [[OccursDclInfo]] = + -- The occurs dcls on this nonterminal for + map(getOccursDcl(_, top.attrFor.typeName, top.env), + -- the full names of each candidate + map((.fullName), attrs)); + -- TODO: BUG: this disambiguates, but doesn't find full-named that aren't in scope with short names! + -- i.e. 'import somthing as prefixed; something.a' won't find prefixed:a. + + -- Occurs dcls for `at` on `top.attrFor` (there should be only one) + local dclsNarrowed :: [OccursDclInfo] = concat(narrowed); + + -- Attribute dcls + local attrsNarrowed :: [AttributeDclInfo] = zipFilterDcls(attrs, narrowed); + + -- This basically has to mirror the logic in errors below! + top.found = + !(null(at.lookupAttribute.dcls) || + top.attrFor.isError || + null(dclsNarrowed) || + length(attrsNarrowed) != 1); + + top.attrFound = !null(attrs); + + top.errors := + -- If we fail to look up the attribute, just report that. + if null(at.lookupAttribute.dcls) then + at.lookupAttribute.errors + -- If we're looking up an attribute on `errorType`, an error is already raised, don't create noise + else if top.attrFor.isError then + [] + -- If no attribute occurs on this type, raise that error + else if null(dclsNarrowed) then + -- This is a heuristic error message for the situation where you have a type, but haven't imported + -- the grammar declaring that type. + (if lastIndexOf(":", top.attrFor.typeName) > 0 && null(getTypeDcl(top.attrFor.typeName, top.env)) then + [err(at.location, "Attribute '" ++ at.name ++ "' does not occur on '" ++ prettyType(top.attrFor) ++ "'. Perhaps import '" ++ substring(0, lastIndexOf(":", top.attrFor.typeName), top.attrFor.typeName) ++ "'?")] + else + [err(at.location, "Attribute '" ++ at.name ++ "' does not occur on '" ++ prettyType(top.attrFor) ++ "'. Looked at:\n" ++ printPossibilities(attrs))] + ) + -- If more than one attribute on the same _short name_ occurs, raise ambiguity + else if length(attrsNarrowed) > 1 then + [err(at.location, "Ambiguous reference to attribute occurring on '" ++ at.name ++ "'. Possibilities are:\n" ++ printPossibilities(attrsNarrowed))] + -- If this same attribute has multiple occurences (must be due to orphaned occurs) + else []; {-if length(dclsNarrowed) > 1 then + [err(at.location, "There are erroneously multiple attribute occurrences for '" ++ at.name ++ "'. Possibilities are:\n" ++ printPossibilities(dclsNarrowed))] + else [];-} + -- TODO: This last bit is disabled because we have problems with importing grammars multiple times. + -- TODO FIXME: enable this, and fix the grammar import issues! + + production resolvedDcl::OccursDclInfo = if top.found then head(dclsNarrowed) else + error("INTERNAL ERROR: Accessing dcl of occurrence " ++ at.name ++ " at " ++ top.grammarName ++ " " ++ top.location.unparse); + resolvedDcl.givenNonterminalType = top.attrFor; + production resolvedTypeScheme::PolyType = resolvedDcl.typeScheme; + production requiredContexts::Contexts = foldContexts(resolvedTypeScheme.contexts); + requiredContexts.env = top.env; + + top.typerep = if top.found then determineAttributeType(head(dclsNarrowed), top.attrFor) else errorType(); + top.dcl = resolvedDcl; + top.attrDcl = if top.found then head(attrsNarrowed) else + -- Workaround fix for proper error reporting - appairently there are some places where this is still demanded. + if !null(attrs) then head(attrs) else + error("INTERNAL ERROR: Accessing dcl of attribute " ++ at.name ++ " at " ++ top.grammarName ++ " " ++ top.location.unparse); +} + +{-- + - `at` is a list of attribute declarations + - `occ` is a mapped list of occurrence declarations for the corresponding attribute + - we return only those `at` which have a non-empty element in `occ` + -} +function zipFilterDcls +[AttributeDclInfo] ::= at::[AttributeDclInfo] occ::[[OccursDclInfo]] +{ + return if null(at) then [] + else if null(head(occ)) then zipFilterDcls(tail(at), tail(occ)) + else head(at) :: zipFilterDcls(tail(at), tail(occ)); +} diff --git a/grammars/silver/compiler/definition/core/Root.sv b/grammars/silver/compiler/definition/core/Root.sv new file mode 100644 index 000000000..88a857877 --- /dev/null +++ b/grammars/silver/compiler/definition/core/Root.sv @@ -0,0 +1,72 @@ +grammar silver:compiler:definition:core; + +{-- + - Root represents one textual file of Silver source. + -} +nonterminal Root with + -- Global-level inherited attributes + config, compiledGrammars, + -- Grammar-level inherited attributes + grammarName, env, globalImports, grammarDependencies, + -- File-level inherited attributes + -- Synthesized attributes + declaredName, unparse, location, errors, defs, occursDefs, moduleNames, importedDefs, importedOccursDefs, + exportedGrammars, optionalGrammars, condBuild, jarName; + +flowtype Root = decorate {config, compiledGrammars, grammarName, env, globalImports, grammarDependencies, flowEnv}; + +nonterminal GrammarDcl with + declaredName, grammarName, location, unparse, errors; + +propagate errors on Root, GrammarDcl; +propagate moduleNames on Root; + +concrete production root +top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls +{ + ims.compiledGrammars = top.compiledGrammars; + ims.grammarDependencies = top.grammarDependencies; + ims.grammarName = top.grammarName; + ims.config = top.config; + + top.unparse = gdcl.unparse ++ "\n\n" ++ ms.unparse ++ "\n\n" ++ ims.unparse ++ "\n\n" ++ ags.unparse; + top.declaredName = gdcl.declaredName; + + top.defs := ags.defs; + top.occursDefs := ags.occursDefs; + + top.importedDefs := ms.defs; + top.importedOccursDefs := ms.occursDefs; + top.exportedGrammars := ms.exportedGrammars; + top.optionalGrammars := ms.optionalGrammars; + top.condBuild := ms.condBuild; + top.jarName := ags.jarName; + + -- We have an mismatch in how the environment gets put together: + -- Outermost, we have grammar-wide imports in one sope. That's top.globalImports here. + -- THEN, we have this particular file's list of local imports. That's ims.defs here. + -- THEN, we have the grammar-wide definitions, from the whole grammr. That's top.env here. + -- So we're kind of injecting local imports in between two grammar-wide things there. + ags.env = appendEnv(top.env, newScopeEnv(ims.defs, occursEnv(ims.occursDefs, top.globalImports))); +} + +concrete production noGrammarDcl +top::GrammarDcl ::= +{ + top.unparse = ""; + top.declaredName = top.grammarName; +} + +concrete production grammarDcl_c +top::GrammarDcl ::= 'grammar' qn::QName ';' +{ + top.unparse = "grammar " ++ qn.unparse ++ ";"; + + top.declaredName = qn.name; + top.errors <- + if qn.name == top.grammarName then [] + else [err(top.location, "Grammar declaration is incorrect: " ++ qn.name)]; +} action { + insert semantic token IdGrammarName_t at qn.baseNameLoc; +} + diff --git a/grammars/silver/compiler/definition/core/Terminals.sv b/grammars/silver/compiler/definition/core/Terminals.sv new file mode 100644 index 000000000..b435bec1d --- /dev/null +++ b/grammars/silver/compiler/definition/core/Terminals.sv @@ -0,0 +1,135 @@ +grammar silver:compiler:definition:core; + +imports silver:langutil:lsp as lsp; + +lexer class Silver + prefix separator ":"; + +lexer class IDENTIFIER extends Silver; +lexer class RESERVED dominates IDENTIFIER; + +lexer class COMMENT extends {Silver, lsp:Comment}; +lexer class LITERAL extends Silver; +lexer class KEYWORD extends {Silver, lsp:Keyword}; +lexer class MODSTMT extends {Silver}; +lexer class OP extends {Silver, lsp:Operator}; +lexer class SPECOP extends {Silver, lsp:Keyword}; +lexer class BUILTIN extends {Silver, lsp:Keyword}; +lexer class TYPE extends {Silver, lsp:Type}; +lexer class MODIFIER extends {Silver, lsp:Modifier}; + +terminal As_kwd 'as' lexer classes {MODSTMT, lsp:Modifier, RESERVED}; +terminal Exports_kwd 'exports' lexer classes {MODSTMT, lsp:Keyword}; +terminal Grammar_kwd 'grammar' lexer classes {MODSTMT, lsp:Keyword, RESERVED}; +terminal Hiding_kwd 'hiding' lexer classes {MODSTMT, lsp:Modifier, RESERVED}; +terminal Import_kwd 'import' lexer classes {MODSTMT, lsp:Keyword}; +terminal Imports_kwd 'imports' lexer classes {MODSTMT, lsp:Keyword}; +terminal Only_kwd 'only' lexer classes {MODSTMT, lsp:Modifier, RESERVED}; +terminal Optional_kwd 'option' lexer classes {MODSTMT, lsp:Keyword}; +-- TODO with + +-- TODO A substantial fraction of these don't need to be reserved! +terminal Abstract_kwd 'abstract' lexer classes {KEYWORD,RESERVED}; +terminal Aspect_kwd 'aspect' lexer classes {KEYWORD,RESERVED}; +terminal Attribute_kwd 'attribute' lexer classes {KEYWORD,RESERVED}; +terminal Class_kwd 'class' lexer classes {KEYWORD}; +terminal Closed_kwd 'closed' lexer classes {KEYWORD}; +terminal Tracked_kwd 'tracked' lexer classes {KEYWORD}; +terminal Concrete_kwd 'concrete' lexer classes {KEYWORD,RESERVED}; +terminal Decorate_kwd 'decorate' lexer classes {KEYWORD,RESERVED}; +terminal Else_kwd 'else' lexer classes {KEYWORD,RESERVED}, precedence = 4, association = left; -- Association needed for dangling else in action code. +terminal End_kwd 'end' lexer classes {KEYWORD,RESERVED}; +terminal Forwarding_kwd 'forwarding' lexer classes {KEYWORD,RESERVED}; +terminal Forward_kwd 'forward' lexer classes {KEYWORD,RESERVED}; +terminal Forwards_kwd 'forwards' lexer classes {KEYWORD,RESERVED}; +terminal Function_kwd 'function' lexer classes {KEYWORD,RESERVED}; +terminal Global_kwd 'global' lexer classes {KEYWORD,RESERVED}; +terminal If_kwd 'if' lexer classes {KEYWORD,RESERVED}; +terminal Inherited_kwd 'inherited' lexer classes {KEYWORD,RESERVED}; +terminal Instance_kwd 'instance' lexer classes {KEYWORD}; +terminal Local_kwd 'local' lexer classes {KEYWORD,RESERVED}; +terminal NonTerminal_kwd 'nonterminal' lexer classes {KEYWORD,RESERVED}; +terminal Occurs_kwd 'occurs' lexer classes {KEYWORD,RESERVED}; +terminal On_kwd 'on' lexer classes {KEYWORD,RESERVED}; +terminal Production_kwd 'production' lexer classes {KEYWORD,RESERVED}; +terminal Return_kwd 'return' lexer classes {KEYWORD,RESERVED}; +terminal Synthesized_kwd 'synthesized' lexer classes {KEYWORD,RESERVED}; +terminal Terminal_kwd 'terminal' lexer classes {KEYWORD,RESERVED}; +terminal Then_kwd 'then' lexer classes {KEYWORD,RESERVED}; +terminal To_kwd 'to' lexer classes {KEYWORD,RESERVED}; +terminal Type_t 'type' lexer classes {KEYWORD}; +terminal With_kwd 'with' lexer classes {KEYWORD,RESERVED}, precedence = 3; -- Precedence to fix Decorated Decorated Expr with {}, which is a semantic error either way + +terminal AttachNote_kwd 'attachNote' lexer classes {BUILTIN,RESERVED}; + +terminal Comma_t ',' precedence = 4; +terminal Or_t '||' lexer classes {OP}, precedence = 5, association = left; +terminal And_t '&&' lexer classes {OP}, precedence = 6, association = left; +terminal Not_t '!' lexer classes {OP}, precedence = 7; +terminal GT_t '>' lexer classes {OP}, precedence = 9, association = left; +terminal LT_t '<' lexer classes {OP}, precedence = 9, association = left; +terminal GTEQ_t '>=' lexer classes {OP}, precedence = 9, association = left; +terminal LTEQ_t '<=' lexer classes {OP}, precedence = 9, association = left; +terminal EQEQ_t '==' lexer classes {OP}, precedence = 9, association = left; +terminal NEQ_t '!=' lexer classes {OP}, precedence = 9, association = left; +terminal PlusPlus_t '++' lexer classes {OP}, precedence = 10, association = right; -- right because that's generally more efficient. +terminal Plus_t '+' lexer classes {OP}, precedence = 11, association = left; +terminal Minus_t '-' lexer classes {OP}, precedence = 11, association = left; +terminal Multiply_t '*' lexer classes {OP}, precedence = 12, association = left; +terminal Divide_t '/' lexer classes {OP}, precedence = 12, association = left; +terminal Modulus_t '%' lexer classes {OP}, precedence = 12, association = left; +terminal ColonColon_t '::' lexer classes {OP}, precedence = 14, association = right; -- HasType AND cons. right due to cons. +terminal LParen_t '(' precedence = 24; +terminal RParen_t ')' precedence = 1, association = left; -- Precedence and association eeded for dangling else in action code. +terminal LCurly_t '{' ; +terminal RCurly_t '}' ; +terminal Dot_t '.' precedence = 25, association = left; +terminal Semi_t ';' ; +terminal Colon_t ':' ; +terminal UnderScore_t '_' ; + +terminal CCEQ_t '::=' lexer classes {SPECOP}; +terminal Equal_t '=' lexer classes {SPECOP}; +terminal CtxArrow_t '=>' lexer classes {SPECOP}; + +-- Unused infix operators: ~ ` @ # % ^ & | \ +-- $ is used by convenience. + + -- this is a very careful regex. beware: +--ignore terminal BlockComments /\{\-([^\-]|\-+[^\}\-])*\-+\}/ lexer classes {COMMENT}; +ignore terminal BlockComments /\{\-(\{\-([^\-]|\-+[^\}\-])*\-+\}|[^\-]|\-+[^\}\-])*\-+\}/ lexer classes {COMMENT}; -- Allows (one level of) nested comments. +ignore terminal Comments /([\-][\-].*)/ lexer classes {COMMENT}; + +ignore terminal WhiteSpace /[\r\n\t\ ]+/; + +ignore terminal LocationTag_t /#line -?[0-9]+/ + action { + line = toInteger(substring(6, length(lexeme), lexeme)); + }; +ignore terminal WarnTag_t /#warn [^\r\n]+/ + action { + print s"${filename}:${toString(line)}: warning: ${substring(6, length(lexeme), lexeme)}"; + }; + +terminal IdLower_t /[a-z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; +terminal IdUpper_t /[A-Z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; + +-- Identifier terminals emitted as semantic tokens +terminal IdGrammarName_t '' lexer classes {IDENTIFIER, lsp:Namespace}; +terminal IdVariable_t '' lexer classes {IDENTIFIER, lsp:Variable}; +terminal IdSigName_t '' lexer classes {IDENTIFIER, lsp:Parameter}; +terminal IdSigNameDcl_t '' lexer classes {IDENTIFIER, lsp:Parameter, lsp:Declaration}; +terminal IdFnProd_t '' lexer classes {IDENTIFIER, lsp:Function}; +terminal IdFnProdDcl_t '' lexer classes {IDENTIFIER, lsp:Function, lsp:Declaration}; +terminal IdType_t '' lexer classes {IDENTIFIER, lsp:Type}; +terminal IdTypeDcl_t '' lexer classes {IDENTIFIER, lsp:Type, lsp:Declaration}; +terminal IdTypeClass_t '' lexer classes {IDENTIFIER, lsp:Interface}; +terminal IdTypeClassDcl_t '' lexer classes {IDENTIFIER, lsp:Interface, lsp:Declaration}; +terminal IdTypeClassMember_t '' lexer classes {IDENTIFIER, lsp:Method}; +terminal IdTypeClassMemberDcl_t '' lexer classes {IDENTIFIER, lsp:Method, lsp:Declaration}; + +terminal True_kwd 'true' lexer classes {LITERAL,RESERVED, lsp:Keyword}; +terminal False_kwd 'false' lexer classes {LITERAL,RESERVED, lsp:Keyword}; +terminal Int_t /[\-]?[0-9]+/ lexer classes {LITERAL, lsp:Number}; +terminal Float_t /[\-]?[0-9]+[\.][0-9]+/ lexer classes {LITERAL, lsp:Number}; +terminal String_t /[\"]([^\r\n\"\\]|[\\][\"]|[\\][\\]|[\\]b|[\\]n|[\\]r|[\\]f|[\\]t)*[\"]/ lexer classes {LITERAL, lsp:String_}; diff --git a/grammars/silver/compiler/definition/core/Type.sv b/grammars/silver/compiler/definition/core/Type.sv new file mode 100644 index 000000000..f63b6f3e5 --- /dev/null +++ b/grammars/silver/compiler/definition/core/Type.sv @@ -0,0 +1,85 @@ +grammar silver:compiler:definition:core; + +-- LHS type gives this to 'application' for "foo(...)" calls. +synthesized attribute applicationDispatcher :: (Expr ::= PartiallyDecorated Expr PartiallyDecorated AppExprs PartiallyDecorated AnnoAppExprs Location); +-- LHS type gives this to 'access' for "foo.some" accesses. +-- (See DclInfo for the next step) +synthesized attribute accessHandler :: (Expr ::= PartiallyDecorated Expr PartiallyDecorated QNameAttrOccur Location); + +-- Used for poor man's type classes +-- TODO: Finish removing these and replace with real type classes +synthesized attribute instanceNum :: Boolean; + +attribute applicationDispatcher, accessHandler, instanceNum occurs on Type; + +aspect default production +top::Type ::= +{ + top.applicationDispatcher = errorApplication(_, _, _, location=_); + top.accessHandler = errorAccessHandler(_, _, location=_); + top.instanceNum = false; +} + +aspect production errorType +top::Type ::= +{ + -- Allow these, to suppress raising additional unnecessary errors. + top.instanceNum = true; +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.applicationDispatcher = c.applicationDispatcher; + top.accessHandler = c.accessHandler; + top.instanceNum = c.instanceNum; +} + +aspect production skolemType +top::Type ::= _ +{ + top.accessHandler = undecoratedAccessHandler(_, _, location=_); +} + +aspect production intType +top::Type ::= +{ + top.instanceNum = true; +} + +aspect production floatType +top::Type ::= +{ + top.instanceNum = true; +} + +aspect production nonterminalType +top::Type ::= fn::String _ _ +{ + top.accessHandler = undecoratedAccessHandler(_, _, location=_); +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.accessHandler = terminalAccessHandler(_, _, location=_); +} + +aspect production decoratedType +top::Type ::= te::Type _ +{ + top.accessHandler = decoratedAccessHandler(_, _, location=_); +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type _ +{ + top.accessHandler = decoratedAccessHandler(_, _, location=_); +} + +aspect production functionType +top::Type ::= _ _ +{ + top.applicationDispatcher = functionApplication(_, _, _, location=_); +} + diff --git a/grammars/silver/compiler/definition/core/TypeDecl.sv b/grammars/silver/compiler/definition/core/TypeDecl.sv new file mode 100644 index 000000000..9d18d63d3 --- /dev/null +++ b/grammars/silver/compiler/definition/core/TypeDecl.sv @@ -0,0 +1,36 @@ +grammar silver:compiler:definition:core; + +concrete production typeAliasDecl +top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs '=' te::TypeExpr ';' +{ + top.unparse = "type " ++ id.unparse ++ tl.unparse ++ "=" ++ te.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ id.name; + + local isCircular::Boolean = contains(fName, te.mentionedAliases); + top.defs := [typeAliasDef( + top.grammarName, id.location, fName, te.mentionedAliases, tl.freeVariables, + if isCircular then errorType() else te.typerep)]; + + top.errors <- tl.errorsTyVars; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + -- Redefinition check of the name + top.errors <- + if length(getTypeDclAll(fName, top.env)) > 1 + then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- + if isLower(substring(0,1,id.name)) + then [err(id.location, "Types must be capitalized. Invalid nonterminal name " ++ id.name)] + else []; + + top.errors <- if isCircular then [err(te.location, s"Definition of ${fName} is self-referential")] else []; +} action { + insert semantic token IdTypeDcl_t at id.location; +} diff --git a/grammars/silver/compiler/definition/env/Attributes.sv b/grammars/silver/compiler/definition/env/Attributes.sv new file mode 100644 index 000000000..5927de124 --- /dev/null +++ b/grammars/silver/compiler/definition/env/Attributes.sv @@ -0,0 +1,96 @@ +grammar silver:compiler:definition:env; + +-- TODO: it'd be nice to find a way to rejigger things so these imports can go away. +import silver:util:cmdargs only CmdArgs; +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType; +import silver:compiler:driver:util only RootSpec; + +{- This grammar contains common definitions of attributes that + are widely used in the Silver compiler. + + Attributes should NOT be added here lightly! + Include a justification for each one. + + These are truely cross-cutting attributes, not simple + things that occurs on a few pieces of syntax. + A good rule of thumb is: is it used by RootSpec Root and IRoot? + --------------------------------------------------------------------} + +-- +-- RootSpec, Root and IRoot grammar graph-related attributes. +-- + +{-- + - The name of the grammar this RootSpec represents. + -} +synthesized attribute declaredName :: String; +{-- + - Grammars directly depended upon by this grammar. + - i.e. imports, exports, parser components, etc. + - NOT options, or triggers, or transitive dependencies. + -} +monoid attribute moduleNames :: [String]; +{-- + - Grammars DIRECTLY exported by this grammar. + -} +monoid attribute exportedGrammars :: [String]; +{-- + - Grammars this grammar specifies as optional modifications. + - (i.e. grammars that introduce more productions that do not forward) + -} +monoid attribute optionalGrammars :: [String]; +{-- + - A list of triggered builds. Format is actually [ [build x, with gram], ... ] + -} +monoid attribute condBuild :: [[String]]; +{-- + - A list of TRUE dependencies of this grammar. + - Closes over moduleNames using exports & triggers. + -} +monoid attribute allGrammarDependencies :: [String]; +{-- + - A list of attribute occurences that are exported by this particular grammar. + - Seperate from defs as a workaround for circular dependency issues. + -} +monoid attribute occursDefs :: [OccursDclInfo]; + + +-- +-- Standard "little attributes," used almost universally in the compiler. +-- + +{-- + - A list of definitions exported by this particular grammar. + -} +monoid attribute defs :: [Def]; +{-- + - The environment. Dun dun dunnn. + -} +autocopy attribute env :: Decorated Env; + +-- +-- Top-level, compiler-wide information passed down by the build process +-- TODO: these don't necessarily make sense. Can we move them pleaaase?! + +{-- +- All grammars Silver looked at. Despite the name, including interface files. +-} +autocopy attribute compiledGrammars :: EnvTree; +{-- +- Compiler configuration information, made available everywhere. +-} +autocopy attribute config :: Decorated CmdArgs; +{-- +- Flow information computed for this grammar +-} +autocopy attribute productionFlowGraphs :: EnvTree; +autocopy attribute grammarFlowTypes :: EnvTree; + +{-- + - The path to the origin of this root spec + -} +synthesized attribute grammarSource :: String; +{-- + - The modification time of the source .sv files of this grammar. + -} +synthesized attribute grammarTime :: Integer; diff --git a/grammars/silver/compiler/definition/env/Context.sv b/grammars/silver/compiler/definition/env/Context.sv new file mode 100644 index 000000000..3a1886525 --- /dev/null +++ b/grammars/silver/compiler/definition/env/Context.sv @@ -0,0 +1,268 @@ +grammar silver:compiler:definition:env; + +import silver:compiler:definition:core only frame, grammarName, compiledGrammars; + +-- Context lookup/resolution stuff lives here + +attribute env occurs on Context; + +-- This mostly exists as a convenient way to perform multiple env-dependant operations +-- on a list of contexts without re-decorating them and repeating context resolution. +nonterminal Contexts with env, config, compiledGrammars, grammarFlowTypes, contexts, freeVariables, boundVariables; +abstract production consContext +top::Contexts ::= h::Context t::Contexts +{ + top.contexts = h :: t.contexts; + top.freeVariables = setUnionTyVars(h.freeVariables, t.freeVariables); +} +abstract production nilContext +top::Contexts ::= +{ + top.contexts = []; + top.freeVariables = []; +} + +global foldContexts::(Contexts ::= [Context]) = foldr(consContext, nilContext(), _); + +synthesized attribute contextSuperDefs::([Def] ::= InstDclInfo String Location) occurs on Context; -- Instances from context's superclasses +synthesized attribute contextMemberDefs::([Def] ::= [TyVar] String Location) occurs on Context; -- Instances from a context on a class member +synthesized attribute contextSigDefs::([Def] ::= NamedSignature String Location) occurs on Context; -- Instances from a context in an aspect signature +synthesized attribute contextSuperOccursDefs::([OccursDclInfo] ::= InstDclInfo String Location) occurs on Context; -- Attribute occurences from context's superclasses +synthesized attribute contextMemberOccursDefs::([OccursDclInfo] ::= [TyVar] String Location) occurs on Context; -- Attribute occurences from a context on a class member +synthesized attribute contextSigOccursDefs::([OccursDclInfo] ::= NamedSignature String Location) occurs on Context; -- Attribute occurences from a context in an aspect signature +synthesized attribute contextClassName::Maybe occurs on Context; + +synthesized attribute resolved::[InstDclInfo] occurs on Context; +synthesized attribute resolvedOccurs::[OccursDclInfo] occurs on Context; + +monoid attribute isTypeError::Boolean with false, || occurs on Contexts, Context; +propagate isTypeError on Contexts, Context; + +attribute config, compiledGrammars, grammarFlowTypes occurs on Context; + +aspect default production +top::Context ::= +{ + top.resolved = []; + top.resolvedOccurs = []; +} + +aspect production instContext +top::Context ::= cls::String t::Type +{ + top.contextSuperDefs = \ d::InstDclInfo g::String l::Location -> + [tcInstDef(instSuperDcl(cls, d, sourceGrammar=g, sourceLocation=l))]; + top.contextMemberDefs = \ tvs::[TyVar] g::String l::Location -> + [tcInstDef(instConstraintDcl(cls, t, tvs, sourceGrammar=g, sourceLocation=l))]; -- Could be a different kind of def, but these are essentially the same as regular instance constraints + top.contextSigDefs = \ ns::NamedSignature g::String l::Location -> + [tcInstDef(sigConstraintDcl(cls, t, ns, sourceGrammar=g, sourceLocation=l))]; + top.contextSuperOccursDefs = \ InstDclInfo String Location -> []; + top.contextMemberOccursDefs = \ [TyVar] String Location -> []; + top.contextSigOccursDefs = \ NamedSignature String Location -> []; + top.contextClassName = just(cls); + + -- Here possibly-decorated types that are still unspecialized at this point + -- are specialized as decorated. Why? Instance resolution happens after + -- final types have been computed, and the default is to be decorated, + -- so we can't allow this to match an instance for the undecorated type. + production decT::Type = t.defaultSpecialization; + + -- Somewhat inefficient, since we try unifying with all the instances of the class. + -- But occurs-on lookup works this way too and isn't too bad? + -- TODO: This does unification twice, once for filtering and once when we find + -- the instance. Probably unavoidable? + local matching::[InstDclInfo] = + filter( + \ d::InstDclInfo -> !unifyDirectional(d.typeScheme.typerep, decT).failure && !d.typeScheme.typerep.isError, + searchEnvTree(cls, top.env.instTree)); + top.resolved = + removeAllBy( + \ d1::InstDclInfo d2::InstDclInfo -> isMoreSpecific(d1.typeScheme.typerep, d2.typeScheme.typerep), + matching, matching); + + production resolvedDcl::InstDclInfo = head(top.resolved); + production resolvedTypeScheme::PolyType = resolvedDcl.typeScheme; + production resolvedSubst::Substitution = unifyDirectional(resolvedTypeScheme.typerep, decT); + production requiredContexts::Contexts = + foldContexts(map(performContextRenaming(_, resolvedSubst), resolvedTypeScheme.contexts)); + requiredContexts.env = top.env; + requiredContexts.frame = top.frame; + requiredContexts.config = top.config; + requiredContexts.grammarName = top.grammarName; + requiredContexts.compiledGrammars = top.compiledGrammars; +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextSuperDefs = \ InstDclInfo String Location -> []; + top.contextMemberDefs = \ [TyVar] String Location -> []; + top.contextSigDefs = \ NamedSignature String Location -> []; + top.contextSuperOccursDefs = \ d::InstDclInfo g::String l::Location -> + [occursSuperDcl(attr, atty, d, sourceGrammar=g, sourceLocation=l)]; + top.contextMemberOccursDefs = \ tvs::[TyVar] g::String l::Location -> + [occursInstConstraintDcl(attr, ntty, atty, tvs, sourceGrammar=g, sourceLocation=l)]; + top.contextSigOccursDefs = \ ns::NamedSignature g::String l::Location -> + [occursSigConstraintDcl(attr, ntty, atty, ns, sourceGrammar=g, sourceLocation=l)]; + top.contextClassName = nothing(); + + top.resolvedOccurs = getOccursDcl(attr, ntty.typeName, top.env); + production resolvedDcl::OccursDclInfo = head(top.resolvedOccurs); + resolvedDcl.givenNonterminalType = ntty; + production resolvedTypeScheme::PolyType = resolvedDcl.typeScheme; + production resolvedSubst::Substitution = unifyDirectional(resolvedTypeScheme.typerep, atty); + production requiredContexts::Contexts = + foldContexts(map(performContextRenaming(_, resolvedSubst), resolvedTypeScheme.contexts)); + requiredContexts.env = top.env; + requiredContexts.frame = top.frame; + requiredContexts.config = top.config; + requiredContexts.grammarName = top.grammarName; + requiredContexts.compiledGrammars = top.compiledGrammars; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.contextSuperDefs = \ InstDclInfo String Location -> []; + top.contextMemberDefs = \ [TyVar] String Location -> []; + top.contextSigDefs = \ NamedSignature String Location -> []; + top.contextSuperOccursDefs = \ d::InstDclInfo g::String l::Location -> + [occursSuperDcl(attr, atty, d, sourceGrammar=g, sourceLocation=l)]; + top.contextMemberOccursDefs = \ tvs::[TyVar] g::String l::Location -> + [occursInstConstraintDcl(attr, ntty, atty, tvs, sourceGrammar=g, sourceLocation=l)]; + top.contextSigOccursDefs = \ ns::NamedSignature g::String l::Location -> + [occursSigConstraintDcl(attr, ntty, atty, ns, sourceGrammar=g, sourceLocation=l)]; + top.contextClassName = nothing(); + + top.resolvedOccurs = getOccursDcl(attr, ntty.typeName, top.env); + production resolvedDcl::OccursDclInfo = head(top.resolvedOccurs); + resolvedDcl.givenNonterminalType = ntty; + production resolvedTypeScheme::PolyType = resolvedDcl.typeScheme; + production resolvedSubst::Substitution = unifyDirectional(resolvedTypeScheme.typerep, atty); + production requiredContexts::Contexts = + foldContexts(map(performContextRenaming(_, resolvedSubst), resolvedTypeScheme.contexts)); + requiredContexts.env = top.env; + requiredContexts.frame = top.frame; + requiredContexts.config = top.config; + requiredContexts.grammarName = top.grammarName; + requiredContexts.compiledGrammars = top.compiledGrammars; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextSuperDefs = \ InstDclInfo String Location -> []; + top.contextMemberDefs = \ [TyVar] String Location -> []; + top.contextSigDefs = \ NamedSignature String Location -> []; + top.contextSuperOccursDefs = \ d::InstDclInfo g::String l::Location -> + [annoSuperDcl(attr, atty, d, sourceGrammar=g, sourceLocation=l)]; + top.contextMemberOccursDefs = \ tvs::[TyVar] g::String l::Location -> + [annoInstConstraintDcl(attr, ntty, atty, tvs, sourceGrammar=g, sourceLocation=l)]; + top.contextSigOccursDefs = \ ns::NamedSignature g::String l::Location -> + [annoSigConstraintDcl(attr, ntty, atty, ns, sourceGrammar=g, sourceLocation=l)]; + top.contextClassName = nothing(); + + top.resolvedOccurs = getOccursDcl(attr, ntty.typeName, top.env); + production resolvedDcl::OccursDclInfo = head(top.resolvedOccurs); + resolvedDcl.givenNonterminalType = ntty; + production resolvedTypeScheme::PolyType = resolvedDcl.typeScheme; + production resolvedSubst::Substitution = unifyDirectional(resolvedTypeScheme.typerep, atty); + production requiredContexts::Contexts = + foldContexts(map(performContextRenaming(_, resolvedSubst), resolvedTypeScheme.contexts)); + requiredContexts.env = top.env; + requiredContexts.frame = top.frame; + requiredContexts.config = top.config; + requiredContexts.grammarName = top.grammarName; + requiredContexts.compiledGrammars = top.compiledGrammars; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + top.contextSuperDefs = \ d::InstDclInfo g::String l::Location -> + [tcInstDef(typeableSuperDcl(d, sourceGrammar=g, sourceLocation=l))]; + top.contextMemberDefs = \ tvs::[TyVar] g::String l::Location -> + [tcInstDef(typeableInstConstraintDcl(t, tvs, sourceGrammar=g, sourceLocation=l))]; -- Could be a different kind of def, but these are essentially the same as regular instance constraints + top.contextSigDefs = \ ns::NamedSignature g::String l::Location -> + [tcInstDef(typeableSigConstraintDcl(t, ns, sourceGrammar=g, sourceLocation=l))]; + top.contextSuperOccursDefs = \ InstDclInfo String Location -> []; + top.contextMemberOccursDefs = \ [TyVar] String Location -> []; + top.contextSigOccursDefs = \ NamedSignature String Location -> []; + top.contextClassName = nothing(); + + top.resolved = + filter( + \ d::InstDclInfo -> !unifyDirectional(d.typeScheme.typerep, t).failure && !d.typeScheme.typerep.isError, + searchEnvTree("typeable", top.env.instTree)); + + production resolvedDcl::InstDclInfo = head(top.resolved); -- resolvedDcl.typeScheme should not bind any type variables! + production requiredContexts::Contexts = + foldContexts( + if null(top.resolved) + then map(compose(typeableContext, skolemType), t.freeSkolemVars) + else resolvedDcl.typeScheme.contexts); + requiredContexts.env = top.env; + requiredContexts.frame = top.frame; + requiredContexts.config = top.config; + requiredContexts.grammarName = top.grammarName; + requiredContexts.compiledGrammars = top.compiledGrammars; +} + +synthesized attribute isTypeable::Boolean occurs on Type; +aspect isTypeable on Type of +| skolemType(_) -> false +| _ -> true +end; + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.contextSuperDefs = error("subset can't appear as superclass"); + top.contextMemberDefs = \ tvs::[TyVar] g::String l::Location -> + [tcInstDef(inhSubsetInstConstraintDcl(i1, i2, tvs, sourceGrammar=g, sourceLocation=l))]; -- Could be a different kind of def, but these are essentially the same as regular instance constraints + top.contextSigDefs = \ ns::NamedSignature g::String l::Location -> + [tcInstDef(inhSubsetSigConstraintDcl(i1, i2, ns, sourceGrammar=g, sourceLocation=l))]; + top.contextSuperOccursDefs = \ InstDclInfo String Location -> []; + top.contextMemberOccursDefs = \ [TyVar] String Location -> []; + top.contextSigOccursDefs = \ NamedSignature String Location -> []; + top.contextClassName = nothing(); + + top.resolved = + filter( + \ d::InstDclInfo -> + !unifyDirectional(d.typeScheme.monoType, i1).failure && !d.typeScheme.monoType.isError && + !unifyDirectional(d.typerep2, i2).failure && !d.typerep2.isError, + searchEnvTree("subset", top.env.instTree)); +} + +aspect production typeErrorContext +top::Context ::= msg::String +{ + top.contextSuperDefs = \ InstDclInfo String Location -> []; + top.contextMemberDefs = \ [TyVar] String Location -> []; + top.contextSigDefs = \ NamedSignature String Location -> []; + top.contextSuperOccursDefs = \ d::InstDclInfo g::String l::Location -> []; + top.contextMemberOccursDefs = \ tvs::[TyVar] g::String l::Location -> []; + top.contextSigOccursDefs = \ ns::NamedSignature g::String l::Location -> []; + top.contextClassName = nothing(); + top.resolved = []; + top.isTypeError <- true; +} + +-- Invariant: This should be called when a and b are unifyable +function isMoreSpecific +Boolean ::= a::Type b::Type +{ + return + case a, b of + | varType(_), varType(_) -> false + | _, varType(_) -> true + | appType(c1, a1), appType(c2, a2) -> + (isMoreSpecific(c1, c2) || isMoreSpecific(a1, a2)) && !(isMoreSpecific(c2, c1) || isMoreSpecific(a2, a1)) + | decoratedType(t1, i1), decoratedType(t2, i2) -> + (isMoreSpecific(t1, t2) || isMoreSpecific(i1, i2)) && !(isMoreSpecific(t2, t1) || isMoreSpecific(i2, i1)) + | partiallyDecoratedType(t1, i1), partiallyDecoratedType(t2, i2) -> + (isMoreSpecific(t1, t2) || isMoreSpecific(i1, i2)) && !(isMoreSpecific(t2, t1) || isMoreSpecific(i2, i1)) + | _, _ -> false + end; +} diff --git a/grammars/silver/compiler/definition/env/DclInfo.sv b/grammars/silver/compiler/definition/env/DclInfo.sv new file mode 100644 index 000000000..e65c9e002 --- /dev/null +++ b/grammars/silver/compiler/definition/env/DclInfo.sv @@ -0,0 +1,537 @@ +grammar silver:compiler:definition:env; + +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax only mentionedAliases; +imports silver:regex; + +-- Some of these nonterminals are closed, but the dispatch attributes are +-- defined in silver:compiler:definition:core, and we don't want to have defaults for those: +option silver:compiler:definition:core; + +annotation sourceGrammar :: String; +annotation sourceLocation :: Location; +synthesized attribute fullName :: String; +synthesized attribute typeScheme :: PolyType; + +-- types +synthesized attribute isType :: Boolean; +synthesized attribute isTypeAlias :: Boolean; +synthesized attribute isClass :: Boolean; +synthesized attribute classMembers :: [Pair]; + +-- instances +inherited attribute givenInstanceType :: Type; +synthesized attribute superContexts :: [Context]; +synthesized attribute typerep2 :: Type; -- Used for binary constraint instances +synthesized attribute definedMembers :: [String]; + +-- values +synthesized attribute namedSignature :: NamedSignature; +synthesized attribute hasForward :: Boolean; + +-- occurs +synthesized attribute attrOccurring :: String; +inherited attribute givenNonterminalType :: Type; + +synthesized attribute isAnnotation :: Boolean; -- also "attrs" + +-- attrs +synthesized attribute isSynthesized :: Boolean; +synthesized attribute isInherited :: Boolean; + +-- production attribute +synthesized attribute prodDefs :: [Def]; +-- production attribute substitutions +synthesized attribute substitutedDclInfo :: ValueDclInfo; +inherited attribute givenSubstitution :: Substitution; + +closed nonterminal ValueDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, isEqual, + typeScheme, namedSignature, hasForward, substitutedDclInfo, givenSubstitution; +propagate isEqual on ValueDclInfo excluding globalValueDcl, classMemberDcl; + +aspect default production +top::ValueDclInfo ::= +{ + -- Values that are not fun/prod have this valid default. + top.namedSignature = bogusNamedSignature(); + top.hasForward = false; + + top.substitutedDclInfo = error("Internal compiler error: must be defined for all value declarations that are production attributes"); +} + +-- ValueDclInfos that can NEVER appear in interface files: +abstract production childDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + + top.typeScheme = monoType(ty); +} +abstract production lhsDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + + top.typeScheme = monoType(ty); +} + +-- ValueDclInfos that CAN appear in interface files, but only via "production attributes:" +abstract production localDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + + top.typeScheme = monoType(ty); + + top.substitutedDclInfo = localDcl( fn, performRenaming(ty, top.givenSubstitution), sourceGrammar=top.sourceGrammar, sourceLocation=top.sourceLocation); +} +abstract production forwardDcl +top::ValueDclInfo ::= ty::Type +{ + top.fullName = "forward"; + + top.typeScheme = monoType(ty); + + top.substitutedDclInfo = forwardDcl( performRenaming(ty, top.givenSubstitution), sourceGrammar=top.sourceGrammar, sourceLocation=top.sourceLocation); +} + +-- ValueDclInfos that DO appear in interface files: +abstract production prodDcl +top::ValueDclInfo ::= ns::NamedSignature hasForward::Boolean +{ + top.fullName = ns.fullName; + + top.namedSignature = ns; + top.typeScheme = ns.typeScheme; + top.hasForward = hasForward; +} +abstract production funDcl +top::ValueDclInfo ::= ns::NamedSignature +{ + top.fullName = ns.fullName; + + top.namedSignature = ns; + top.typeScheme = ns.typeScheme; + top.hasForward = false; +} +abstract production classMemberDcl +top::ValueDclInfo ::= fn::String bound::[TyVar] clsHead::Context contexts::[Context] ty::Type +{ + top.fullName = fn; + top.typeScheme = constraintType(bound, clsHead :: contexts, ty); + + top.isEqual = + case top.compareTo of + | classMemberDcl(fn2, _, _, _, _) -> fn == fn2 && top.typeScheme == top.compareTo.typeScheme + | _ -> false + end; +} +abstract production globalValueDcl +top::ValueDclInfo ::= fn::String bound::[TyVar] contexts::[Context] ty::Type +{ + top.fullName = fn; + top.typeScheme = constraintType(bound, contexts, ty); + + top.isEqual = + case top.compareTo of + | globalValueDcl(fn2, _, _, _) -> fn == fn2 && top.typeScheme == top.compareTo.typeScheme + | _ -> false + end; +} +abstract production termIdDcl +top::ValueDclInfo ::= fn::String +{ + top.fullName = fn; + + top.typeScheme = monoType(terminalIdType()); +} + +closed nonterminal TypeDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, isEqual, + typeScheme, kindrep, givenNonterminalType, isType, isTypeAlias, mentionedAliases, isClass, classMembers, givenInstanceType, superContexts; +propagate isEqual, compareTo on TypeDclInfo excluding typeAliasDcl, clsDcl; + +aspect default production +top::TypeDclInfo ::= +{ + top.kindrep = starKind(); + top.isType = false; + top.isTypeAlias = false; + top.mentionedAliases := []; + top.isClass = false; + top.classMembers = []; + top.superContexts = []; +} + +abstract production ntDcl +top::TypeDclInfo ::= fn::String ks::[Kind] closed::Boolean tracked::Boolean +{ + top.fullName = fn; + + top.typeScheme = monoType(nonterminalType(fn, ks, tracked)); + top.kindrep = foldr(arrowKind, starKind(), ks); + top.isType = true; +} +abstract production termDcl +top::TypeDclInfo ::= fn::String regex::Regex easyName::Maybe genRepeatProb::Maybe +{ + top.fullName = fn; + + top.typeScheme = monoType(terminalType(fn)); + top.isType = true; +} +abstract production lexTyVarDcl +top::TypeDclInfo ::= fn::String isAspect::Boolean tv::TyVar +{ + top.fullName = fn; + + -- Lexical type vars in aspects aren't skolemized, since they unify with the real (skolem) types. + -- See comment in silver:compiler:definition:type:syntax:AspectDcl.sv + top.typeScheme = monoType(if isAspect then varType(tv) else skolemType(tv)); + top.kindrep = tv.kindrep; + top.isType = true; +} +abstract production typeAliasDcl +top::TypeDclInfo ::= fn::String mentionedAliases::[String] bound::[TyVar] ty::Type +{ + top.fullName = fn; + top.isEqual = + case top.compareTo of + | typeAliasDcl(fn2, ma2, _, _) -> + fn == fn2 && mentionedAliases == ma2 && top.typeScheme == top.compareTo.typeScheme + | _ -> false + end; + + top.isType = null(bound); + top.isTypeAlias = true; + top.mentionedAliases := mentionedAliases; + top.typeScheme = if null(bound) then monoType(ty) else polyType(bound, ty); + top.kindrep = foldr(arrowKind, ty.kindrep, map((.kindrep), bound)); +} +abstract production clsDcl +top::TypeDclInfo ::= fn::String supers::[Context] tv::TyVar k::Kind members::[Pair] +{ + top.fullName = fn; + top.isEqual = + case top.compareTo of + | clsDcl(fn2, s2, tv2, k2, m2) -> + fn == fn2 && k == k2 && supers == map(performContextRenaming(_, subst(tv2, skolemType(tv))), s2) && members == m2 + | _ -> false + end; + + -- These are in the type namespace but shouldn't actually be used as such, + -- this is only used to report the kind. + top.typeScheme = monoType(varType(freshTyVar(k))); + top.isClass = true; + + local tvSubst :: Substitution = subst(tv, top.givenInstanceType); + top.superContexts = map(performContextRenaming(_, tvSubst), supers); + top.classMembers = members; +} + +closed nonterminal AttributeDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, compareKey, isEqual, + typeScheme, isInherited, isSynthesized, isAnnotation; +propagate compareKey on AttributeDclInfo; + +aspect default production +top::AttributeDclInfo ::= +{ + top.isEqual = + top.compareKey == top.compareTo.compareKey && + top.fullName == top.compareTo.fullName && + top.typeScheme == top.compareTo.typeScheme; + + top.isSynthesized = false; + top.isInherited = false; + top.isAnnotation = false; +} + +abstract production synDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.fullName = fn; + + top.typeScheme = polyType(bound, ty); + top.isSynthesized = true; +} +abstract production inhDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.fullName = fn; + + top.typeScheme = polyType(bound, ty); + top.isInherited = true; +} +abstract production annoDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.fullName = fn; + + top.typeScheme = polyType(bound, ty); + top.isAnnotation = true; +} + +nonterminal ProductionAttrDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, isEqual, prodDefs, namedSignature; + +abstract production paDcl +top::ProductionAttrDclInfo ::= ns::NamedSignature{-fn::String outty::Type intys::[Type]-} dcls::[Def] +{ + top.fullName = ns.fullName; + top.isEqual = ns == top.compareTo.namedSignature && dcls == defsFromPADcls([new(top.compareTo)], ns); + + top.prodDefs = dcls; + + -- This is used by the function that computes the substituted defs. + top.namedSignature = ns; +} + +nonterminal OccursDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, isEqual, + typeScheme, givenNonterminalType, attrOccurring, isAnnotation; +propagate compareTo, isEqual on OccursDclInfo excluding occursDcl; + +aspect default production +top::OccursDclInfo ::= +{ + top.isAnnotation = false; +} + +abstract production occursDcl +top::OccursDclInfo ::= fnnt::String fnat::String ntty::Type atty::Type +{ + top.fullName = fnnt; + top.isEqual = + case top.compareTo of + | occursDcl(fnnt2, fnat2, ntty2, atty2) -> + fnnt == fnnt2 && fnat == fnat2 && + polyType(ntty.freeVariables, ntty) == polyType(ntty2.freeVariables, ntty2) && + polyType(ntty.freeVariables, atty) == polyType(ntty2.freeVariables, atty2) + | _ -> false + end; + + -- There should be no type variables in atty that aren't in ntty. (Important constraint!) + -- that's why we only use ntty.FV above. + + -- ALSO IMPORTANT: ntty and atty should be tyvar'd up, not skolem'd up. You dig? + + -- Here we use givenNonterminalType to find the attribute type: + local subst :: Substitution = unifyDirectional(ntty, top.givenNonterminalType); -- must rewrite FROM ntty TO gNT + + top.typeScheme = + if subst.failure + then polyType(atty.freeVariables, atty) -- We didn't get a sensible type for givenNonterminalType. Let's do our best? (This error should already be caught!) + else monoType(performRenaming(atty, subst)); + + top.attrOccurring = fnat; +} + +abstract production occursInstConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type tvs::[TyVar] +{ + top.fullName = ntty.typeName; + top.attrOccurring = fnat; + + top.typeScheme = monoType(atty); + + ntty.boundVariables = tvs; +} +abstract production occursSigConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type ns::NamedSignature +{ + top.fullName = ntty.typeName; + top.attrOccurring = fnat; + + top.typeScheme = monoType(atty); + + ntty.boundVariables = ns.freeVariables; +} +abstract production occursSuperDcl +top::OccursDclInfo ::= fnat::String atty::Type baseDcl::InstDclInfo +{ + top.fullName = baseDcl.typeScheme.typerep.typeName; + top.attrOccurring = fnat; + + top.typeScheme = constraintType(baseDcl.typeScheme.boundVars, baseDcl.typeScheme.contexts, atty); +} + +abstract production annoInstanceDcl +top::OccursDclInfo ::= fnnt::String fnat::String ntty::Type atty::Type +{ + top.fullName = fnnt; + + -- There should be no type variables in atty that aren't in ntty. (Important constraint!) + -- that's why we only use ntty.FV above. + + -- ALSO IMPORTANT: ntty and atty should be tyvar'd up, not skolem'd up. You dig? + + -- Here we use givenNonterminalType to find the attribute type: + local subst :: Substitution = unifyDirectional(ntty, top.givenNonterminalType); -- must rewrite FROM ntty TO gNT + + top.typeScheme = + if subst.failure + then polyType(atty.freeVariables, atty) -- We didn't get a sensible type for givenNonterminalType. Let's do our best? (This error should already be caught!) + else monoType(performRenaming(atty, subst)); + + top.attrOccurring = fnat; + + -- UGH - bit of a short hand here... + top.isAnnotation = true; +} +abstract production annoInstConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type tvs::[TyVar] +{ + top.fullName = ntty.typeName; + top.attrOccurring = fnat; + top.isAnnotation = true; + + top.typeScheme = monoType(atty); + + ntty.boundVariables = tvs; +} +abstract production annoSigConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type ns::NamedSignature +{ + top.fullName = ntty.typeName; + top.attrOccurring = fnat; + top.isAnnotation = true; + + top.typeScheme = monoType(atty); + + ntty.boundVariables = ns.freeVariables; +} +abstract production annoSuperDcl +top::OccursDclInfo ::= fnat::String atty::Type baseDcl::InstDclInfo +{ + top.fullName = baseDcl.typeScheme.typerep.typeName; + top.attrOccurring = fnat; + top.isAnnotation = true; + + top.typeScheme = constraintType(baseDcl.typeScheme.boundVars, baseDcl.typeScheme.contexts, atty); +} + +nonterminal InstDclInfo with + sourceGrammar, sourceLocation, fullName, compareTo, isEqual, + typeScheme, typerep2, isTypeError, definedMembers; + +aspect default production +top::InstDclInfo ::= +{ + top.isTypeError := false; + top.definedMembers = []; + top.typerep2 = error("Internal compiler error: must be defined for all binary constraint instances"); + top.isEqual = + top.fullName == top.compareTo.fullName && + top.typeScheme == top.compareTo.typeScheme && + top.definedMembers == top.compareTo.definedMembers; +} + +-- Class instances +abstract production instDcl +top::InstDclInfo ::= fn::String bound::[TyVar] contexts::[Context] ty::Type definedMembers::[String] +{ + top.fullName = fn; + + top.typeScheme = constraintType(bound, contexts, ty); + + top.isTypeError := any(map((.isTypeError), contexts)); + top.definedMembers = definedMembers; +} +abstract production instConstraintDcl +top::InstDclInfo ::= fntc::String ty::Type tvs::[TyVar] +{ + top.fullName = fntc; + + top.typeScheme = monoType(ty); +} +abstract production sigConstraintDcl +top::InstDclInfo ::= fntc::String ty::Type ns::NamedSignature +{ + top.fullName = fntc; + + top.typeScheme = monoType(ty); +} +abstract production currentInstDcl +top::InstDclInfo ::= fntc::String ty::Type +{ + top.fullName = fntc; + + top.typeScheme = monoType(ty); +} +abstract production instSuperDcl +top::InstDclInfo ::= fntc::String baseDcl::InstDclInfo +{ + top.fullName = fntc; + + top.typeScheme = baseDcl.typeScheme; +} + +-- typeable instances +abstract production typeableInstConstraintDcl +top::InstDclInfo ::= ty::Type tvs::[TyVar] +{ + top.fullName = "typeable"; + + top.typeScheme = monoType(ty); +} +abstract production typeableSigConstraintDcl +top::InstDclInfo ::= ty::Type ns::NamedSignature +{ + top.fullName = "typeable"; + + top.typeScheme = monoType(ty); +} +abstract production typeableSuperDcl +top::InstDclInfo ::= baseDcl::InstDclInfo +{ + top.fullName = "typeable"; + + top.typeScheme = baseDcl.typeScheme; +} + +-- inhSubset instances +abstract production inhSubsetInstConstraintDcl +top::InstDclInfo ::= i1::Type i2::Type tvs::[TyVar] +{ + top.fullName = "subset"; + + top.typeScheme = monoType(i1); + top.typerep2 = i2; +} +abstract production inhSubsetSigConstraintDcl +top::InstDclInfo ::= i1::Type i2::Type ns::NamedSignature +{ + top.fullName = "subset"; + + top.typeScheme = monoType(i1); + top.typerep2 = i2; +} + +-- TODO: this should probably go elsewhere? +function determineAttributeType +Type ::= occursDclInfo::OccursDclInfo ntty::Type +{ + occursDclInfo.givenNonterminalType = ntty; + return occursDclInfo.typeScheme.typerep; +} + +-- Dealing with substitutions for production attributes. +function performSubstitutionDclInfo +ValueDclInfo ::= valueDclInfo::ValueDclInfo s::Substitution +{ + valueDclInfo.givenSubstitution = s; + return valueDclInfo.substitutedDclInfo; +} + +function defsFromPADcls +[Def] ::= dcls::[ProductionAttrDclInfo] s::NamedSignature +{ + -- We want to rewrite FROM the sig these PAs were declared with, TO the given sig + local subst :: Substitution = unifyNamedSignature(head(dcls).namedSignature, s); + + return if null(dcls) then [] + else map(performSubstitutionDef(_, subst), head(dcls).prodDefs) ++ defsFromPADcls(tail(dcls), s); +} + diff --git a/grammars/silver/compiler/definition/env/Defs.sv b/grammars/silver/compiler/definition/env/Defs.sv new file mode 100644 index 000000000..382c8a217 --- /dev/null +++ b/grammars/silver/compiler/definition/env/Defs.sv @@ -0,0 +1,273 @@ +grammar silver:compiler:definition:env; + +nonterminal Defs with defs, typeList, valueList, attrList, instList, prodOccursList, prodDclList, filterItems, filterOnly, filterHiding, withRenames, renamed, pfx, prepended; + +-- The standard namespaces +synthesized attribute typeList :: [EnvItem]; +synthesized attribute valueList :: [EnvItem]; +synthesized attribute attrList :: [EnvItem]; + +-- Type class instances +synthesized attribute instList :: [InstDclInfo]; + +-- Production attributes. +synthesized attribute prodOccursList :: [ProductionAttrDclInfo]; + +-- Extra space for production list +synthesized attribute prodDclList :: [ValueDclInfo]; + +-- Transformations on lists of Def +-- This is to support computing the defs introduced by qualified imports +-- (import foo only bar, import foo as bar, import foo with bar as baz) +synthesized attribute filterOnly :: Defs; +synthesized attribute filterHiding :: Defs; + +propagate filterItems, withRenames, renamed, pfx, prepended on Defs; + +abstract production nilDefs +top::Defs ::= +{ + top.defs := []; + + top.typeList = []; + top.valueList = []; + top.attrList = []; + top.instList = []; + + top.prodOccursList = []; + + top.prodDclList = []; + + top.filterOnly = top; + top.filterHiding = top; +} + +abstract production consDefs +top::Defs ::= e1::Def e2::Defs +{ + top.defs := e1 :: e2.defs; + + top.typeList = e1.typeList ++ e2.typeList; + top.valueList = e1.valueList ++ e2.valueList; + top.attrList = e1.attrList ++ e2.attrList; + top.instList = e1.instList ++ e2.instList; + + top.prodOccursList = e1.prodOccursList ++ e2.prodOccursList; + + top.prodDclList = e1.prodDclList ++ e2.prodDclList; + + top.filterOnly = if e1.filterIncludeOnly then consDefs(e1, e2.filterOnly) else e2.filterOnly; + top.filterHiding = if e1.filterIncludeHiding then consDefs(e1, e2.filterHiding) else e2.filterHiding; +} + +-------------------------------------------------------------------------------- + +closed nonterminal Def with + typeList, valueList, attrList, instList, prodOccursList, prodDclList, + filterItems, filterIncludeOnly, filterIncludeHiding, withRenames, renamed, pfx, prepended, + compareTo, isEqual; + +propagate filterItems, filterIncludeOnly, filterIncludeHiding, withRenames, renamed, pfx, prepended, compareTo, isEqual on Def; + +aspect default production +top::Def ::= +{ + top.typeList = []; + top.valueList = []; + top.attrList = []; + top.instList = []; + + top.prodOccursList = []; + + top.prodDclList = []; +} +abstract production typeDef +top::Def ::= d::EnvItem +{ + top.typeList = [d]; +} +abstract production valueDef +top::Def ::= d::EnvItem +{ + top.valueList = [d]; +} +abstract production typeValueDef +top::Def ::= td::EnvItem vd::EnvItem +{ + top.typeList = [td]; + top.valueList = [vd]; +} +abstract production attrDef +top::Def ::= d::EnvItem +{ + top.attrList = [d]; +} +abstract production prodDclDef +top::Def ::= d::EnvItem +{ + top.valueList = [d]; + -- unlike normal valueDef, also affect production lookups: + top.prodDclList = [d.dcl]; +} +abstract production paDef +top::Def ::= d::ProductionAttrDclInfo +{ + top.prodOccursList = [d]; +} +abstract production tcInstDef +top::Def ::= d::InstDclInfo +{ + top.instList = [d]; +} + +function childDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(childDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function lhsDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(lhsDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function localDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(localDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function prodDef +Def ::= sg::String sl::Location ns::NamedSignature hasForward::Boolean +{ + return prodDclDef(defaultEnvItem(prodDcl(ns,hasForward,sourceGrammar=sg,sourceLocation=sl))); +} +function funDef +Def ::= sg::String sl::Location ns::NamedSignature +{ + return valueDef(defaultEnvItem(funDcl(ns,sourceGrammar=sg,sourceLocation=sl))); +} +function globalDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] contexts::[Context] ty::Type +{ + return valueDef(defaultEnvItem(globalValueDcl(fn, bound, contexts, ty,sourceGrammar=sg,sourceLocation=sl))); +} +function classMemberDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] head::Context contexts::[Context] ty::Type +{ + return valueDef(defaultEnvItem(classMemberDcl(fn,bound,head,contexts,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function ntDef +Def ::= sg::String sl::Location fn::String ks::[Kind] closed::Boolean tracked::Boolean +{ + return typeDef(defaultEnvItem(ntDcl(fn,ks,closed,tracked,sourceGrammar=sg,sourceLocation=sl))); +} +function termDef +Def ::= sg::String sl::Location fn::String regex::Regex easyName::Maybe genRepeatProb::Maybe +{ + -- Terminals are also in the value namespace as terminal identifiers + return typeValueDef( + defaultEnvItem(termDcl(fn,regex,easyName,genRepeatProb,sourceGrammar=sg,sourceLocation=sl)), + defaultEnvItem(termIdDcl(fn,sourceGrammar=sg,sourceLocation=sl))); +} +function lexTyVarDef +Def ::= sg::String sl::Location fn::String tv::TyVar +{ + return typeDef(defaultEnvItem(lexTyVarDcl(fn,false,tv,sourceGrammar=sg,sourceLocation=sl))); +} +function aspectLexTyVarDef +Def ::= sg::String sl::Location fn::String tv::TyVar +{ + return typeDef(defaultEnvItem(lexTyVarDcl(fn,true,tv,sourceGrammar=sg,sourceLocation=sl))); +} +function typeAliasDef +Def ::= sg::String sl::Location fn::String mentionedAliases::[String] bound::[TyVar] ty::Type +{ + return typeDef(defaultEnvItem(typeAliasDcl(fn,mentionedAliases,bound,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function synDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(synDcl(fn,bound,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function inhDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(inhDcl(fn,bound,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function prodOccursDef +Def ::= sg::String sl::Location ns::NamedSignature dcls::[Def] +{ + return paDef(paDcl(ns,dcls,sourceGrammar=sg,sourceLocation=sl)); +} +function forwardDef +Def ::= sg::String sl::Location ty::Type +{ + return valueDef(defaultEnvItem(forwardDcl(ty,sourceGrammar=sg,sourceLocation=sl))); +} +-- These aliased functions are used for aspects. +function aliasedLhsDef +Def ::= sg::String sl::Location fn::String ty::Type alias::String +{ + return valueDef(onlyRenamedEnvItem(alias, lhsDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function aliasedChildDef +Def ::= sg::String sl::Location fn::String ty::Type alias::String +{ + return valueDef(onlyRenamedEnvItem(alias, childDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function annoDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(annoDcl(fn,bound,ty,sourceGrammar=sg,sourceLocation=sl))); +} +function classDef +Def ::= sg::String sl::Location fn::String supers::[Context] tv::TyVar k::Kind members::[Pair] +{ + return typeDef(defaultEnvItem(clsDcl(fn,supers,tv,k,members,sourceGrammar=sg,sourceLocation=sl))); +} +function instDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] contexts::[Context] ty::Type definedMembers::[String] +{ + return tcInstDef(instDcl(fn,bound,contexts,ty,definedMembers,sourceGrammar=sg,sourceLocation=sl)); +} +function sigConstraintDef +Def ::= sg::String sl::Location fn::String ty::Type ns::NamedSignature +{ + return tcInstDef(sigConstraintDcl(fn,ty,ns,sourceGrammar=sg,sourceLocation=sl)); +} +function currentInstDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return tcInstDef(currentInstDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl)); +} +function instSuperDef +Def ::= sg::String sl::Location fn::String baseDcl::InstDclInfo +{ + return tcInstDef(instSuperDcl(fn,baseDcl,sourceGrammar=sg,sourceLocation=sl)); +} +function typeableSuperDef +Def ::= sg::String sl::Location baseDcl::InstDclInfo +{ + return tcInstDef(typeableSuperDcl(baseDcl,sourceGrammar=sg,sourceLocation=sl)); +} + + +-- I'm leaving "Defsironment" here just for the lols +---------------------------------------------------------------------------------------------------- +--Defsironment creation functions-------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +{-- + - Used only on what we get from production attributes. + - We encode those assumptions: + - 1. We expect ONLY valueDefs. + - 2. We expect ONLY 'defaultEnvItems' + -} +function performSubstitutionDef +Def ::= d::Def s::Substitution +{ + return + case d of + | valueDef(ei) -> valueDef(defaultEnvItem(performSubstitutionDclInfo(ei.dcl, s))) + | _ -> error("Prod attr def not a valueDef") + end; +} diff --git a/grammars/silver/compiler/definition/env/Env.sv b/grammars/silver/compiler/definition/env/Env.sv new file mode 100644 index 000000000..4f17899a8 --- /dev/null +++ b/grammars/silver/compiler/definition/env/Env.sv @@ -0,0 +1,365 @@ +grammar silver:compiler:definition:env; + + +-- emptyEnv Decorated Env ::= +-- toEnv Decorated Env ::= d::Defs +-- appendEnv Decorated Env ::= e1::Decorated Env e2::Decorated Env +-- newScopeEnv Decorated Env ::= e1::Defs e2::Decorated Env + +-- [DclInfo] ::= search::String e::Decorated Env +-- getValueDclInScope getValueDcl getValueDclAll +-- getTypeDcl +-- getAttrDcl + +-- getProdAttrs [DclInfo] ::= prod::String e::Decorated Env + +nonterminal Env with typeTree, valueTree, attrTree, instTree, prodOccursTree, occursTree, prodsForNtTree; + +synthesized attribute typeTree :: [EnvTree]; -- Expr is type tau +synthesized attribute valueTree :: [EnvTree]; -- x has type tau +synthesized attribute attrTree :: [EnvTree]; -- attr a has type tau + +synthesized attribute instTree :: EnvTree; -- class on type +synthesized attribute prodOccursTree :: EnvTree; -- value on prod +synthesized attribute occursTree :: EnvTree; -- attr on NT + +synthesized attribute prodsForNtTree :: [EnvTree]; -- maps nt fname to prods known to construct it + +---------------------------------------------------------------------------------------------------- +--Environment creation functions-------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function emptyEnv +Decorated Env ::= +{ + return decorate i_emptyEnv() with {}; +} +abstract production i_emptyEnv +top::Env ::= +{ + top.typeTree = [emptyEnvTree()]; + top.valueTree = [emptyEnvTree()]; + top.attrTree = [emptyEnvTree()]; + + top.instTree = emptyEnvTree(); + top.prodOccursTree = emptyEnvTree(); + top.occursTree = emptyEnvTree(); + + top.prodsForNtTree = [emptyEnvTree()]; +} + +function toEnv +Decorated Env ::= d::[Def] +{ + return newScopeEnv(d, emptyEnv()); +} + +{-- + - appendEnv exists because we do a weird scope swizzling. + - Our scopes go [global, import, grammar] but hierarchically we + - should have [global, grammar, import] because grammar-wide names would + - seem to have wider scope than per-file imports. + - But this would be horrible semantics: the file in question is even included + - in the grammar-wide scope. So imports would hide things in the local file, even. + - + - Instead, we build these three scopes as essentially separate environments, + - and append them together in the correct order. + -} +function appendEnv +Decorated Env ::= e1::Decorated Env e2::Decorated Env +{ + return decorate i_appendEnv(e1, e2) with {}; +} +abstract production i_appendEnv +top::Env ::= e1::Decorated Env e2::Decorated Env +{ + top.typeTree = e1.typeTree ++ e2.typeTree; + top.valueTree = e1.valueTree ++ e2.valueTree; + top.attrTree = e1.attrTree ++ e2.attrTree; + + top.instTree = appendEnvTree(e1.instTree, e2.instTree); + top.prodOccursTree = appendEnvTree(e1.prodOccursTree, e2.prodOccursTree); + top.occursTree = appendEnvTree(e1.occursTree, e2.occursTree); + + top.prodsForNtTree = e1.prodsForNtTree ++ e2.prodsForNtTree; +} + +{-- + - The usual means of introducing new defs to an environment, by creating a new nested scope. + -} +function newScopeEnv +Decorated Env ::= d::[Def] e::Decorated Env +{ + return decorate i_newScopeEnv(foldr(consDefs, nilDefs(), d), e) with {}; +} +abstract production i_newScopeEnv +top::Env ::= d::Defs e::Decorated Env +{ + top.typeTree = buildTree(d.typeList) :: e.typeTree; + top.valueTree = buildTree(d.valueList) :: e.valueTree; + top.attrTree = buildTree(d.attrList) :: e.attrTree; + + top.instTree = consEnvTree(mapFullnameDcls(d.instList), e.instTree); + top.prodOccursTree = consEnvTree(mapFullnameDcls(d.prodOccursList), e.prodOccursTree); + top.occursTree = e.occursTree; + + top.prodsForNtTree = + directBuildTree(map(\ di::ValueDclInfo -> (di.namedSignature.outputElement.typerep.typeName, di), d.prodDclList)) :: + e.prodsForNtTree; +} + +{-- + - Introduces new occurs defs to an environment. + - This is seperate from newScopeEnv as we must be able to build the other env trees without having the occurs tree. + -} +function occursEnv +Decorated Env ::= d::[OccursDclInfo] e::Decorated Env +{ + return decorate i_occursEnv(d, e) with {}; +} +abstract production i_occursEnv +top::Env ::= d::[OccursDclInfo] e::Decorated Env +{ + top.typeTree = e.typeTree; + top.valueTree = e.valueTree; + top.attrTree = e.attrTree; + + top.instTree = e.instTree; + top.prodOccursTree = e.prodOccursTree; + top.occursTree = consEnvTree(mapFullnameDcls(d), e.occursTree); + + top.prodsForNtTree = e.prodsForNtTree; +} + +---------------------------------------------------------------------------------------------------- +--Environment query functions----------------------------------------------------------------------- +---------------------------------------------------------------------------------------------------- + +function searchEnvAll +[a] ::= search::String e::[EnvTree] +{ + return flatMap(searchEnvTree(search, _), e); +} + +function searchEnv +[a] ::= search::String e::[EnvTree] +{ + local found :: [a] = searchEnvTree(search, head(e)); + + return if null(e) then [] + else if null(found) then searchEnv(search, tail(e)) + else found; +} + +function getValueDclInScope +[ValueDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvTree(search, head(e.valueTree)); +} +function getValueDcl +[ValueDclInfo] ::= search::String e::Decorated Env +{ + return searchEnv(search, e.valueTree); +} +function getValueDclAll +[ValueDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvAll(search, e.valueTree); +} + +function getTypeDclInScope +[TypeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvTree(search, head(e.typeTree)); +} +function getTypeDcl +[TypeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnv(search, e.typeTree); +} +function getTypeDclAll +[TypeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvAll(search, e.typeTree); +} + +function getAttrDclInScope +[AttributeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvTree(search, head(e.attrTree)); +} +function getAttrDcl +[AttributeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnv(search, e.attrTree); +} +function getAttrDclAll +[AttributeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvAll(search, e.attrTree); +} + +function getOccursDcl +[OccursDclInfo] ::= fnat::String fnnt::String e::Decorated Env +{ + -- retrieve all attribute Dcls on NT fnnt + return occursOnHelp(searchEnvTree(fnnt, e.occursTree), fnat); +} +function occursOnHelp +[OccursDclInfo] ::= i::[OccursDclInfo] fnat::String +{ + -- Inefficiency. Linear search for attribute on a nonterminal + return if null(i) then [] + else if head(i).attrOccurring == fnat + then head(i) :: occursOnHelp(tail(i), fnat) + else occursOnHelp(tail(i), fnat); +} + +-- Determines whether a type is automatically promoted to a decorated type +-- and whether a type may be supplied with inherited attributes. +-- Used by expression (id refs), decorate type checking, and translations. +function isDecorable +Boolean ::= t::Type e::Decorated Env +{ + return + case t of + | skolemType(_) -> !null(searchEnvTree(t.typeName, e.occursTree)) + | varType(_) -> !null(searchEnvTree(t.typeName, e.occursTree)) -- Can happen when pattern matching on a prod with occurs contexts + | partiallyDecoratedType(nt, _) -> isDecorable(nt, e) + | _ -> t.isNonterminal + end; +} + +function getProdAttrs +[ProductionAttrDclInfo] ::= fnprod::String e::Decorated Env +{ + return searchEnvTree(fnprod, e.prodOccursTree); +} + +{-- + - Get all productions for a nonterminal known to local environment. + - (forwarding and non-forwarding.) + - Current known use cases: + - 1. Interference testing code generation (randomly generate trees) + - - Because we test each production independently, we may not actually need this? + - 2. MWDA checking known forwarding productions on attribute occurrence declaration. + - - We need to be able to look from forwarding to occurs, and from + - occurs to forwarding to cover all cases. + - 3. For the automatic attributes extension + - - to implement propagate on all the known non-forwarding productions of a nonterminal. + - You should probably have a good reason for using this, and document it here if you do. + -} +function getKnownProds +[ValueDclInfo] ::= fnnt::String e::Decorated Env +{ + return searchEnvAll(fnnt, e.prodsForNtTree); +} + +-- The list of non-forwarding productions may contain productions from `options` not +-- imported locally, and so we must consult the "flow environment" for that information: +--function getNonforwardingProds + +{-- + - Returns all attributes known locally to occur on a nonterminal. + - Obviously we can never know all attributes, but we generally don't need to for + - any reason. + -} +function getAttrsOn +[OccursDclInfo] ::= fnnt::String e::Decorated Env +{ + return searchEnvTree(fnnt, e.occursTree); +} + +-- This ensure the annotation list is in the properly sorted order! +function annotationsForNonterminal +[NamedSignatureElement] ::= nt::Type env::Decorated Env +{ + local annos :: [OccursDclInfo] = + filter((.isAnnotation), getAttrsOn(nt.typeName, env)); + + return sortBy(namedSignatureElementLte, map(annoInstanceToNamed(nt, _), annos)); +} +-- only used by the above +function annoInstanceToNamed +NamedSignatureElement ::= nt::Type anno::OccursDclInfo +{ + -- Used to compute the local typerep for this nonterminal + anno.givenNonterminalType = nt; + + return namedSignatureElement(anno.attrOccurring, anno.typeScheme.typerep); +} + +-- Looks up class instances matching a type +function getInstanceDcl +[InstDclInfo] ::= fntc::String t::Type e::Decorated Env +{ + local c::Context = instContext(fntc, t); + c.env = e; + return c.resolved; +} + +-- Compute a lower bound on the members of an InhSet type, including transitive ones arising from subset constraints +function getMinInhSetMembers +([String], [TyVar]) ::= seen::[TyVar] t::Type e::Decorated Env +{ + local c::Context = inhSubsetContext(varType(freshTyVar(inhSetKind())), t); + c.env = e; + + local recurse::[([String], [TyVar])] = + map( + \ d::InstDclInfo -> getMinInhSetMembers(t.freeVariables ++ seen, d.typeScheme.monoType, e), + c.resolved); + + return + case t of + | skolemType(tv) when contains(tv, seen) -> ([], []) + | varType(_) -> ([], []) -- If an InhSet is unspecialized after type checking, assume it is empty + | _ -> (sort(unions(t.inhSetMembers :: map(fst, recurse))), unions(t.freeVariables :: map(snd, recurse))) + end; +} + +function getMinRefSet +[String] ::= t::Type e::Decorated Env +{ + return + case t of + | decoratedType(_, i) -> getMinInhSetMembers([], i, e).fst + | partiallyDecoratedType(_, i) -> getMinInhSetMembers([], i, e).fst + | _ -> [] + end; +} + +-- Try to compute an upper bound on the members of an InhSet type, including transitive ones arising from subset constraints +function getMaxInhSetMembers +(Maybe<[String]>, [TyVar]) ::= seen::[TyVar] t::Type e::Decorated Env +{ + local c::Context = inhSubsetContext(t, varType(freshTyVar(inhSetKind()))); + c.env = e; + + local recurse::[(Maybe<[String]>, [TyVar])] = + map( + \ d::InstDclInfo -> getMaxInhSetMembers(t.freeVariables ++ seen, d.typerep2, e), + c.resolved); + + return + case t of + | skolemType(tv) when contains(tv, seen) -> (nothing(), []) + | varType(_) -> (just([]), []) -- If an InhSet is unspecialized after type checking, assume it is empty + | inhSetType(inhs) -> (just(inhs), []) + | _ -> (map(sort, foldr( + \ inhs1::Maybe<[String]> inhs2::Maybe<[String]> -> alt(lift2(intersect, inhs1, inhs2), alt(inhs1, inhs2)), + empty, map(fst, recurse))), + unions(t.freeVariables :: map(snd, recurse))) + end; +} + +function getMaxRefSet +Maybe<[String]> ::= t::Type e::Decorated Env +{ + return + case t of + | decoratedType(_, i) -> getMaxInhSetMembers([], i, e).fst + | partiallyDecoratedType(_, i) -> getMaxInhSetMembers([], i, e).fst + | _ -> just([]) + end; +} diff --git a/grammars/silver/compiler/definition/env/EnvItem.sv b/grammars/silver/compiler/definition/env/EnvItem.sv new file mode 100644 index 000000000..c8735b4c8 --- /dev/null +++ b/grammars/silver/compiler/definition/env/EnvItem.sv @@ -0,0 +1,119 @@ +grammar silver:compiler:definition:env; + +{-- + - An entry in the environment. + -} +nonterminal EnvItem with + itemName, dcl, envContribs, + filterItems, filterIncludeOnly, filterIncludeHiding, withRenames, renamed, pfx, prepended, + compareTo, isEqual; + +synthesized attribute itemName :: String; +synthesized attribute dcl :: a; +synthesized attribute envContribs :: [Pair]; + +inherited attribute filterItems::[String]; +monoid attribute filterIncludeOnly::Boolean with true, &&; +monoid attribute filterIncludeHiding::Boolean with true, &&; + +inherited attribute withRenames::[(String, String)]; +functor attribute renamed; +inherited attribute pfx::String; +functor attribute prepended; + +{-- + - Rare case: use of `import _ with _ as _` or `import _ as _` to rename. + - Common case: `grammar:full:name` aka `name`. See `defaultEnvItem`. + -} +abstract production renamedEnvItem +attribute fullName {} occurs on a, +attribute compareTo occurs on a, +attribute isEqual {compareTo} occurs on a => +ei::EnvItem ::= newname::String di::a +{ + ei.itemName = newname; + ei.dcl = di; + ei.envContribs = + if newname != di.fullName + then [pair(newname, di), pair(di.fullName, di)] + else [pair(newname, di)]; + + ei.filterIncludeOnly := contains(newname, ei.filterItems); + ei.filterIncludeHiding := !contains(newname, ei.filterItems); + ei.renamed = + case lookup(newname, ei.withRenames) of + | nothing() -> ei + | just(result) -> renamedEnvItem(result, di) + end; + ei.prepended = renamedEnvItem(ei.pfx ++ newname, di); + + propagate compareTo, isEqual; +} +{-- + - Entries at fullname ONLY. + - Used for occurrences & production attributes, which are looked up + - by the full nonterminal name (or production name) only, and a shortname is nonsense. + -} +abstract production fullNameEnvItem +attribute fullName {} occurs on a, +attribute compareTo occurs on a, +attribute isEqual {compareTo} occurs on a => +ei::EnvItem ::= di::a +{ + ei.itemName = di.fullName; + ei.dcl = di; + ei.envContribs = [pair(di.fullName, di)]; + + propagate filterIncludeOnly, filterIncludeHiding, renamed, prepended; -- Always imported & not renamed + propagate compareTo, isEqual; +} +{-- + - Used for aspect local variables. The LHS and children have a full name + - like `newname` and in the aspect we can rename it anything we want. + - We should *not* see `newname` in the environment in those cases. + -} +abstract production onlyRenamedEnvItem +attribute compareTo occurs on a, +attribute isEqual {compareTo} occurs on a => +ei::EnvItem ::= newname::String di::a +{ + ei.itemName = newname; + ei.dcl = di; + ei.envContribs = [pair(newname, di)]; + + propagate filterIncludeOnly, filterIncludeHiding, renamed, prepended; -- Should never be imported + propagate compareTo, isEqual; +} + +{-- + - The common case, normal shortnames. + -} +function defaultEnvItem +attribute fullName {} occurs on a, +attribute compareTo occurs on a, +attribute isEqual {compareTo} occurs on a => +EnvItem ::= di::a +{ + return renamedEnvItem(fullNameToShort(di.fullName), di); +} +function fullNameToShort +String ::= s::String +{ + -- Works just fine, even when lastIndexOf returns -1 + return substring(lastIndexOf(":", s) + 1, length(s), s); +} + + +global mapGetDcls :: ([a] ::= [EnvItem]) = map((.dcl), _); +global mapFullnameDcls :: + attribute fullName {} occurs on a, + attribute compareTo occurs on a, + attribute isEqual {compareTo} occurs on a => + ([EnvItem] ::= [a]) = + map(fullNameEnvItem, _); +global mapDefaultWrapDcls :: + attribute fullName {} occurs on a, + attribute compareTo occurs on a, + attribute isEqual {compareTo} occurs on a => + ([EnvItem] ::= [a]) = + map(defaultEnvItem, _); diff --git a/grammars/silver/compiler/definition/env/EnvTree.sv b/grammars/silver/compiler/definition/env/EnvTree.sv new file mode 100644 index 000000000..78b255e02 --- /dev/null +++ b/grammars/silver/compiler/definition/env/EnvTree.sv @@ -0,0 +1,54 @@ +grammar silver:compiler:definition:env; + +import silver:util:treemap as rtm; -- good performance (mostly due to strictness!) + +{-- + - The abstraction for maps throughout the Silver compiler. + -} +type EnvTree = rtm:Map; + +{-- + - Look up function for maps. + -} +function searchEnvTree +[a] ::= search::String et::EnvTree +{ + return rtm:lookup(search, et); +} + +{-- + - Standard environment constructor for a map. + - Obey's EnvItem's rules for what names should appear for each item. + -} +function buildTree +EnvTree ::= eis::[EnvItem] +{ + return directBuildTree(flatMap((.envContribs), eis)); +} + +{-- + - Arbitrary environment constructor for a map. + -} +function directBuildTree +EnvTree ::= eis::[Pair] +{ + return rtm:add(eis, rtm:empty()); +} + +function emptyEnvTree +EnvTree ::= +{ + return directBuildTree([]); +} + +function appendEnvTree +EnvTree ::= e1::EnvTree e2::EnvTree +{ + return rtm:add(rtm:toList(e1), e2); +} + +function consEnvTree +EnvTree ::= eis::[EnvItem] et::EnvTree +{ + return rtm:add(flatMap((.envContribs), eis), et); +} diff --git a/grammars/silver/compiler/definition/env/NamedSignature.sv b/grammars/silver/compiler/definition/env/NamedSignature.sv new file mode 100644 index 000000000..c0a18f9dc --- /dev/null +++ b/grammars/silver/compiler/definition/env/NamedSignature.sv @@ -0,0 +1,200 @@ +grammar silver:compiler:definition:env; + +{-- + - The fully named and fully type signature of a production (or function). + - Includes full name of the production and names for all input and output elements (and annotations). + - + - TODO: we might want to remove the full name of the production from this, and make it just `Signature`? + - It's not clear if this information really belongs here, or not. + -} +nonterminal NamedSignature with fullName, contexts, inputElements, outputElement, namedInputElements, typeScheme, freeVariables, inputNames, inputTypes, typerep; + +flowtype NamedSignature = decorate {}; + +synthesized attribute inputElements :: [NamedSignatureElement]; +synthesized attribute outputElement :: NamedSignatureElement; +synthesized attribute namedInputElements :: [NamedSignatureElement]; +synthesized attribute inputNames :: [String]; +-- inputTypes comes from the types grammar. + +@{- + - Represents the signature of a production (or function). + - @param fn The full name + - @param ctxs The type constraint contexts + - @param ie The input elements + - @param oe The output element + - @param np Named parameters (or annotations) + -} +abstract production namedSignature +top::NamedSignature ::= fn::String ctxs::Contexts ie::NamedSignatureElements oe::NamedSignatureElement np::NamedSignatureElements +{ + top.fullName = fn; + top.contexts = ctxs.contexts; + top.inputElements = ie.elements; + top.outputElement = oe; + top.namedInputElements = np.elements; + top.inputNames = ie.elementNames; + top.inputTypes = ie.elementTypes; -- Does anything actually use this? TODO: eliminate? + local typerep::Type = appTypes(functionType(length(ie.elements), np.elementShortNames), ie.elementTypes ++ np.elementTypes ++ [oe.typerep]); + top.typeScheme = (if null(ctxs.contexts) then polyType else constraintType(_, ctxs.contexts, _))(top.freeVariables, typerep); + top.freeVariables = setUnionTyVars(ctxs.freeVariables, typerep.freeVariables); + top.typerep = typerep; -- TODO: Only used by unifyNamedSignature. Would be nice to eliminate, somehow. + + ctxs.boundVariables = top.freeVariables; + ie.boundVariables = top.freeVariables; + oe.boundVariables = top.freeVariables; + np.boundVariables = top.freeVariables; +} + +@{- + - Represents the signature of a global (or class member). + - @param fn The full name + - @param ctxs The type constraint contexts + - @param ty The type of the global + -} +abstract production globalSignature +top::NamedSignature ::= fn::String ctxs::Contexts ty::Type +{ + top.fullName = fn; + top.contexts = ctxs.contexts; + top.inputElements = error("Not a production or function"); + top.outputElement = error("Not a production or function"); + top.namedInputElements = error("Not a production or function"); + top.inputNames = error("Not a production or function"); + top.inputTypes = ty.inputTypes; -- Does anything actually use this? TODO: eliminate? + top.typeScheme = (if null(ctxs.contexts) then polyType else constraintType(_, ctxs.contexts, _))(top.freeVariables, ty); + top.freeVariables = setUnionTyVars(ctxs.freeVariables, ty.freeVariables); + top.typerep = ty; + + ctxs.boundVariables = top.freeVariables; + ty.boundVariables = top.freeVariables; +} + +{-- + - Used when an error occurs. e.g. aspecting a non-existant production. + - Or, in contexts that have no valid signature, which maybe we should do something about... + -} +abstract production bogusNamedSignature +top::NamedSignature ::= +{ + forwards to namedSignature("_NULL_", nilContext(), nilNamedSignatureElement(), bogusNamedSignatureElement(), nilNamedSignatureElement()); +} + +{-- + - Represents a collection of NamedSignatureElements + -} +nonterminal NamedSignatureElements with elements, elementNames, elementShortNames, elementTypes, freeVariables, boundVariables; + +synthesized attribute elements::[NamedSignatureElement]; +synthesized attribute elementNames::[String]; +synthesized attribute elementShortNames::[String]; +synthesized attribute elementTypes::[Type]; + +abstract production consNamedSignatureElement +top::NamedSignatureElements ::= h::NamedSignatureElement t::NamedSignatureElements +{ + top.elements = h :: t.elements; + top.elementNames = h.elementName :: t.elementNames; + top.elementShortNames = h.elementShortName :: t.elementShortNames; + top.elementTypes = h.typerep :: t.elementTypes; + top.freeVariables = setUnionTyVars(h.freeVariables, t.freeVariables); +} + +abstract production nilNamedSignatureElement +top::NamedSignatureElements ::= +{ + top.elements = []; + top.elementNames = []; + top.elementShortNames = []; + top.elementTypes = []; + top.freeVariables = []; +} + +global foldNamedSignatureElements::(NamedSignatureElements ::= [NamedSignatureElement]) = + foldr(consNamedSignatureElement, nilNamedSignatureElement(), _); + +{-- + - Represents an elements of a signature, whether input, output, or annotation. + -} +nonterminal NamedSignatureElement with elementName, elementShortName, typerep, freeVariables, boundVariables; + +synthesized attribute elementName :: String; +synthesized attribute elementShortName :: String; + +{-- + - Represents an element of the function/production signature. + -} +abstract production namedSignatureElement +top::NamedSignatureElement ::= n::String ty::Type +{ + top.elementName = n; + top.typerep = ty; + top.freeVariables = ty.freeVariables; + + -- When we convert from a SignatureElement to a functionType, we cut down to the short name only: + top.elementShortName = + substring(lastIndexOf(":", n) + 1, length(n), n); +} + +{-- + - A bogus output element, because an error occurred, or because + - There is no output type. + -} +abstract production bogusNamedSignatureElement +top::NamedSignatureElement ::= +{ + forwards to namedSignatureElement("__SV_BOGUS_ELEM", errorType()); +} + +---------------- + +function namedSignatureElementLte +Boolean ::= a::NamedSignatureElement b::NamedSignatureElement +{ + return a.elementName <= b.elementName; +} + +-- This is a big of an awful pile. Related to annotations, for now. +function findNamedSigElem +Integer ::= s::String l::[NamedSignatureElement] z::Integer +{ + return if null(l) then -1 + else if s == head(l).elementName then z + else findNamedSigElem(s, tail(l), z+1); +} + +function findNamedSigElemType +Type ::= n::String l::[NamedSignatureElement] +{ + local elems::NamedSignatureElements = foldNamedSignatureElements(l); + return fromMaybe(errorType(), lookup(n, zipWith(pair, elems.elementNames, elems.elementTypes))); +} + +-------------- + +attribute substitution, flatRenamed occurs on NamedSignature, Contexts, NamedSignatureElements, NamedSignatureElement; +propagate flatRenamed on NamedSignature, Contexts, NamedSignatureElements, NamedSignatureElement; + +-- "Freshens" all the signature's type variables with new skolem constants, +-- to avoid type vars from interface files clashing with new ones from genInt() +function freshenNamedSignature +NamedSignature ::= ns::NamedSignature +{ + ns.substitution = zipVarsAndTypesIntoSubstitution(ns.freeVariables, map(skolemType, ns.typeScheme.boundVars)); + return ns.flatRenamed; +} + +function unifyNamedSignature +Substitution ::= ns1::NamedSignature ns2::NamedSignature +{ + local subst :: Substitution = unifyDirectional(ns1.typerep, ns2.typerep); + return + if !subst.failure then subst + else errorSubstitution(ns1.typerep); +} + +instance Eq NamedSignature { + eq = \ ns1::NamedSignature ns2::NamedSignature -> + ns1.fullName == ns2.fullName && + ns1.typeScheme == ns2.typeScheme; +} diff --git a/grammars/silver/compiler/definition/env/Type.sv b/grammars/silver/compiler/definition/env/Type.sv new file mode 100644 index 000000000..f196d9d7e --- /dev/null +++ b/grammars/silver/compiler/definition/env/Type.sv @@ -0,0 +1,90 @@ +grammar silver:compiler:definition:env; + +-- Just to clarify: +-- call prettyType to pretty print the type. +-- get typeName to find out what nonterminal a NT or DNT is +synthesized attribute typeName :: String; + +attribute typeName occurs on PolyType; + +aspect production monoType +top::PolyType ::= ty::Type +{ + top.typeName = ty.typeName; +} + +aspect production polyType +top::PolyType ::= tvs::[TyVar] ty::Type +{ + top.typeName = ty.typeName; +} + +aspect production constraintType +top::PolyType ::= tvs::[TyVar] contexts::[Context] ty::Type +{ + top.typeName = ty.typeName; +} + +attribute typeName occurs on Type; + +aspect default production +top::Type ::= +{ + top.typeName = ""; -- We actually put a value here, since it's possible for us to request typeName of nonsensical things. +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.typeName = c.typeName; +} + +aspect production nonterminalType +top::Type ::= fn::String _ _ +{ + top.typeName = fn; +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.typeName = fn; +} + +aspect production decoratedType +top::Type ::= te::Type _ +{ + top.typeName = te.typeName; +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type _ +{ + top.typeName = te.typeName; +} + +aspect production varType +top::Type ::= tv::TyVar +{ + top.typeName = tv.typeName; +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.typeName = tv.typeName; +} + +attribute typeName occurs on TyVar; + +aspect production tyVar +top::TyVar ::= k::Kind i::Integer +{ + top.typeName = "_a" ++ toString(i); +} + +aspect production tyVarNamed +top::TyVar ::= k::Kind i::Integer n::String +{ + top.typeName = n; +} diff --git a/grammars/silver/definition/flow/ast/ExprVertexInfo.sv b/grammars/silver/compiler/definition/flow/ast/ExprVertexInfo.sv similarity index 94% rename from grammars/silver/definition/flow/ast/ExprVertexInfo.sv rename to grammars/silver/compiler/definition/flow/ast/ExprVertexInfo.sv index f07f4be55..1fc94070c 100644 --- a/grammars/silver/definition/flow/ast/ExprVertexInfo.sv +++ b/grammars/silver/compiler/definition/flow/ast/ExprVertexInfo.sv @@ -1,4 +1,4 @@ -grammar silver:definition:flow:ast; +grammar silver:compiler:definition:flow:ast; {-- - Information about an expression. Specifically, whether it corresponds to a diff --git a/grammars/silver/compiler/definition/flow/ast/Flow.sv b/grammars/silver/compiler/definition/flow/ast/Flow.sv new file mode 100644 index 000000000..a27b60f9d --- /dev/null +++ b/grammars/silver/compiler/definition/flow/ast/Flow.sv @@ -0,0 +1,408 @@ +grammar silver:compiler:definition:flow:ast; + + +{-- + - Info for constructing production flow graphs. + - Follows Figure 5.12 (p138, pdf p154 of Kaminski's PhD) + - with notable additions of: + - - ntRef, specification (for dealing with 'flowtype' decls and the ref set) + - - implicitFwdAffects (for improving accuracy of inference) + - - extraEq (handling collections '<-') + - which the thesis does not address. + -} +nonterminal FlowDef with synTreeContribs, inhTreeContribs, defTreeContribs, fwdTreeContribs, fwdInhTreeContribs, prodTreeContribs, prodGraphContribs, flowEdges, localInhTreeContribs, suspectFlowEdges, hostSynTreeContribs, nonSuspectContribs, localTreeContribs, partialRefContribs; +nonterminal FlowDefs with synTreeContribs, inhTreeContribs, defTreeContribs, fwdTreeContribs, fwdInhTreeContribs, prodTreeContribs, prodGraphContribs, localInhTreeContribs, hostSynTreeContribs, nonSuspectContribs, localTreeContribs, partialRefContribs; + +{-- lookup (production, attribute) to find synthesized equations + - Used to ensure a necessary lhs.syn equation exists. + - Also decides whether to add a forward or default equation while computing flow types. -} +monoid attribute synTreeContribs :: [Pair]; + +{-- lookup (production, sig, attribute) to find inherited equation + - Used to ensure a necessary rhs.inh equation exists. + - Also decides whether to add a copy equation for autocopy attributes to rhs elements. -} +monoid attribute inhTreeContribs :: [Pair]; + +{-- lookup (nonterminal, attribute) to find default syn equations + - Used to obtain default equation dependencies, when it exists. -} +monoid attribute defTreeContribs :: [Pair]; + +{-- lookup (production) to find forward equations. + - Decides whether default or forward equations should be added. -} +monoid attribute fwdTreeContribs :: [Pair]; + +{-- lookup (production, attr) to find forward INHERITED equations + - Used to ensure equations for inherited attributes exist for all inh of a fwd. -} +monoid attribute fwdInhTreeContribs :: [Pair]; + +{-- lookup (production, local, attr) to find local INHERITED equations. + - ONLY used to check whether an equation exists. -} +monoid attribute localInhTreeContribs :: [Pair]; + +{-- lookup (production, local) to find the local equation -} +monoid attribute localTreeContribs :: [Pair]; + +{-- lookup (nonterminal) to find all non-forwarding production. + - ONLY used to determine all productions that need an equation for a new attribute. -} +monoid attribute prodTreeContribs :: [Pair]; + +{-- find all equations having to do DIRECTLY with a production + (directly meaning e.g. no default equations, even if they might + affect it) These FlowDefs MUST have a flowEdges for this production. -} +monoid attribute prodGraphContribs :: [Pair]; + +{-- Edge lists from equations + - ONLY used to extract edges for a production graph from production-internal flowDefs. -} +synthesized attribute flowEdges :: [Pair]; + +{-- Like flowEdges, but these edges originate from equations that are not + - allowed to affect their OWN flow type. We must still track them because + - they may affect others' flow types. + - (e.g. extsyn = hostsyn; hostsyn = hostinh; need to reflect extsyn's dep on hostinh) -} +synthesized attribute suspectFlowEdges :: [Pair]; + +{-- A list of extension syn attr occurrences, subject to ft lower bounds. -} +monoid attribute hostSynTreeContribs :: [Pair]; + +{-- A list of attributes for a production that are non-suspect -} +monoid attribute nonSuspectContribs :: [Pair]; + +{-- A list of decoration sites where partial references are taken, and the attributes on those partial references -} +monoid attribute partialRefContribs :: [(String, String, Location, [String])]; + +propagate synTreeContribs, inhTreeContribs, defTreeContribs, fwdTreeContribs, fwdInhTreeContribs, localInhTreeContribs, localTreeContribs, prodTreeContribs, prodGraphContribs, hostSynTreeContribs, nonSuspectContribs, partialRefContribs + on FlowDefs; + +abstract production consFlow +top::FlowDefs ::= h::FlowDef t::FlowDefs +{} + +abstract production nilFlow +top::FlowDefs ::= +{} + +-- At the time of writing, this is one giant work in progress. +-- Currently, all we're going to report is whether a synthesized +-- equation EXISTS or whether a production forwards at all. +-- This will be implemented in such a way that it returns the +-- FlowDef, but presently that has no special information. + +aspect default production +top::FlowDef ::= +{ + top.synTreeContribs := []; + top.inhTreeContribs := []; + top.defTreeContribs := []; + top.fwdTreeContribs := []; + top.fwdInhTreeContribs := []; + top.prodTreeContribs := []; + top.localInhTreeContribs := []; + top.localTreeContribs := []; + top.hostSynTreeContribs := []; + top.nonSuspectContribs := []; + top.suspectFlowEdges = []; -- flowEdges is required, but suspect is typically not! + top.partialRefContribs := []; + -- require prodGraphContibs, flowEdges +} + +{-- + - Declaration of a NON-FORWARDING production. Exists to allow lookups of productions + - from nonterminal name. + - + - @param nt The full name of the nonterminal it constructs + - @param prod The full name of the production + -} +abstract production prodFlowDef +top::FlowDef ::= nt::String prod::String +{ + top.prodTreeContribs := [pair(nt, top)]; + top.prodGraphContribs := []; + top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); +} + +{-- + - Declaration that a synthesized attribute occurrence is in the host language + - (exported by the nonterminal) and therefore is *NOT* subject to the restriction + - for extensions synthesized attributes (i.e. must be ft(syn) >= ft(fwd)). + - + - @param nt the full name of the nonterminal + - @param attr the full name of the synthesized attribute + -} +abstract production hostSynFlowDef +top::FlowDef ::= nt::String attr::String +{ + top.hostSynTreeContribs := [pair(nt, top)]; + top.prodGraphContribs := []; + top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); +} + +{-- + - The definition of a default equation for a synthesized attribute on a nonterminal. + - + - @param nt the full name of the *nonterminal* + - @param attr the full name of the attribute + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production defaultSynEq +top::FlowDef ::= nt::String attr::String deps::[FlowVertex] +{ + top.defTreeContribs := [pair(crossnames(nt, attr), top)]; + top.prodGraphContribs := []; -- defaults don't show up in the prod graph!! + top.flowEdges = map(pair(lhsSynVertex(attr), _), deps); -- but their edges WILL end up added to graphs in fixup-phase!! +} + +{-- + - The definition of a synthesized attribute in a production. + - + - @param prod the full name of the production + - @param attr the full name of the attribute + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production synEq +top::FlowDef ::= prod::String attr::String deps::[FlowVertex] mayAffectFlowType::Boolean +{ + top.synTreeContribs := [pair(crossnames(prod, attr), top)]; + top.prodGraphContribs := [pair(prod, top)]; + local edges :: [Pair] = map(pair(lhsSynVertex(attr), _), deps); + top.flowEdges = if mayAffectFlowType then edges else []; + top.suspectFlowEdges = if mayAffectFlowType then [] else edges; +} + +{-- + - The definition of a inherited attribute for a signature element in a production. + - + - @param prod the full name of the production + - @param sigName the name of the RHS element + - @param attr the full name of the attribute + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production inhEq +top::FlowDef ::= prod::String sigName::String attr::String deps::[FlowVertex] +{ + top.inhTreeContribs := [pair(crossnames(prod, crossnames(sigName, attr)), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(rhsVertex(sigName, attr), _), deps); +} + +{-- + - The definition of the forward of a production. + - + - @param prod the full name of the production + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE *NOT* repeat *NOT* POSSIBLE + -} +abstract production fwdEq +top::FlowDef ::= prod::String deps::[FlowVertex] mayAffectFlowType::Boolean +{ + top.fwdTreeContribs := [pair(prod, top)]; + top.prodGraphContribs := [pair(prod, top)]; + local edges :: [Pair] = map(pair(forwardEqVertex(), _), deps); + top.flowEdges = if mayAffectFlowType then edges else []; + top.suspectFlowEdges = if mayAffectFlowType then [] else edges; +} + +{-- + - Attributes that are non-suspect. + - + - These are *allowed* to have their *implicit forward copy equations* affect the flow type. + - + - This is basically a hack to make flow type inference work a little better. + -} +abstract production implicitFwdAffects +top::FlowDef ::= prod::String attrs::[String] +{ + top.nonSuspectContribs := [pair(prod, attrs)]; + top.prodGraphContribs := []; + top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); +} + +{-- + - The definition of an inherited attribute on the forward + - + - @param prod the full name of the production + - @param attrName the full name of the inherited attribute given to the forward + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production fwdInhEq +top::FlowDef ::= prod::String attr::String deps::[FlowVertex] +{ + top.fwdInhTreeContribs := [pair(crossnames(prod, attr), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(forwardVertex(attr), _), deps); +} + +{-- + - The definition of a local or production attribute's equation. + - MAY not be a nonterminal type! + - + - @param prod the full name of the production + - @param fName the name of the local/production attribute + - @param typeName the full name of the type, or empty string if not a decorable type! + - @param isNT true if the type is a nonterminal + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production localEq +top::FlowDef ::= prod::String fName::String typeName::String isNT::Boolean deps::[FlowVertex] +{ + top.localTreeContribs := [pair(crossnames(prod, fName), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(localEqVertex(fName), _), deps); +} + +{-- + - The definition of an inherited attribute for a local attribute. + - + - @param prod the full name of the production + - @param fName the name of the local/production attribute + - @param attr the full name of the attribute + - @param deps the dependencies of this equation on other flow graph elements + - CONTRIBUTIONS ARE POSSIBLE + -} +abstract production localInhEq +top::FlowDef ::= prod::String fName::String attr::String deps::[FlowVertex] +{ + top.localInhTreeContribs := [pair(crossnames(prod, crossnames(fName, attr)), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(localVertex(fName, attr), _), deps); +} + +{-- + - Used for contributions to collections. Allows tacking on dependencies + - to vertices. + - + - @param prod the full name of the production + - @param src the vertex to add dependencies to + - @param deps the dependencies of this vertex + -} +abstract production extraEq +top::FlowDef ::= prod::String src::FlowVertex deps::[FlowVertex] mayAffectFlowType::Boolean +{ + top.prodGraphContribs := [pair(prod, top)]; + local edges :: [Pair] = map(pair(src, _), deps); + top.flowEdges = if mayAffectFlowType then edges else []; + top.suspectFlowEdges = if mayAffectFlowType then [] else edges; +} + +{-- + - The definition of an anonymous decoration site e.g. 'decorate with' + - + - @param prod the full name of the production + - @param fName the generated anonymous name for this decoration site + - @param typeName the full name of the type (usually a nonterminal, but may be a decorable type var) + - @param isNT true if the type is a nonterminal + - @param deps the dependencies of this equation on other flow graph elements + - (no contributions are possible) + -} +abstract production anonEq +top::FlowDef ::= prod::String fName::String typeName::String isNT::Boolean loc::Location deps::[FlowVertex] +{ + top.localTreeContribs := [pair(crossnames(prod, fName), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(anonEqVertex(fName), _), deps); +} + +{-- + - The definition of an inherited attribute for an anonymous decoration site. + - + - @param prod the full name of the production + - @param fName the generated anonymous name for this decoration site + - @param attr the full name of the attribute + - @param deps the dependencies of this equation on other flow graph elements + - (no contributions are possible) + -} +abstract production anonInhEq +top::FlowDef ::= prod::String fName::String attr::String deps::[FlowVertex] +{ + top.localInhTreeContribs := [pair(crossnames(prod, crossnames(fName, attr)), top)]; + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(anonVertex(fName, attr), _), deps); +} + +{-- + - A synthesized occurs-on context for a decoration site of a type variable. + - + - @param prod the full name of the production + - @param vt the decoration site + - @param attr the full name of the synthesized attribute + - @param deps the full names of the inherited attribute dependencies specified in the occurs-on context. + -} +abstract production synOccursContextEq +top::FlowDef ::= prod::String vt::VertexType attr::String deps::[String] +{ + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = map(pair(vt.synVertex(attr), _), map(vt.inhVertex, deps)); +} + +{-- + - A match rule from a pattern matching equation. + - Matching against 'scrutinee' this rule matches 'matchProd'. It extracts the pattern variables in 'vars'. + - This info is used, not to directly create edges, but to inform which stitch points are needed. + -} +abstract production patternRuleEq +top::FlowDef ::= prod::String matchProd::String scrutinee::VertexType vars::[PatternVarProjection] +{ + top.prodGraphContribs := [pair(prod, top)]; + top.flowEdges = []; +} + + +nonterminal PatternVarProjection; + +abstract production patternVarProjection +top::PatternVarProjection ::= child::String typeName::String patternVar::String +{ +} + +{-- + - The taking of a partially decorated reference to a child or local/production attribute. + - Since taking a partially decorated reference means that inherited equations + - on the decoration site for attributes not in the reference set are forbidden, + - this info tracks what decoration sites have partial references taken. + - + - @param prod the full name of the production + - @param fName the full name of the child or local + - @param gram the grammar where the reference was taken + - @param loc the location of where the reference was taken + - @param attrs the attributes in the type of the taken reference + -} +abstract production partialRef +top::FlowDef ::= prod::String fName::String gram::String loc::Location attrs::[String] +{ + top.partialRefContribs := [(crossnames(prod, fName), gram, loc, attrs)]; + top.prodGraphContribs := []; + top.flowEdges = []; +} + +-- + +function crossnames +String ::= a::String b::String +{ + return a ++ " @ " ++ b; +} + +-- + +-- Used to get better error messages +function collectAnonOrigin +[Pair] ::= f::[FlowDef] +{ + return foldr(collectAnonOriginItem, [], f); +} +function collectAnonOriginItem +[Pair] ::= f::FlowDef rest::[Pair] +{ + return case f of + | anonEq(_, fN, _, _, l, _) -> + -- Small hack to improve error messages. Ignore anonEq's that come from patterns + if startsWith("__scrutinee", fN) + then rest + else pair(fN, l) :: rest + | _ -> rest + end; +} diff --git a/grammars/silver/compiler/definition/flow/ast/Vertex.sv b/grammars/silver/compiler/definition/flow/ast/Vertex.sv new file mode 100644 index 000000000..684c1c054 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/ast/Vertex.sv @@ -0,0 +1,103 @@ +grammar silver:compiler:definition:flow:ast; + +{-- + - Data structure representing vertices in the flow graph within a single production. + - + - See VertexType for some extra information organizing these vertexes somewhat. + -} +nonterminal FlowVertex with compareTo, isEqual, compareKey, compare; +propagate compareTo, isEqual, compareKey, compare on FlowVertex; + +{-- + - A vertex representing a synthesized attribute on the nonterminal being constructed by this production. + - + - @param attrName the full name of a synthesized attribute on the lhs. + -} +abstract production lhsSynVertex +top::FlowVertex ::= attrName::String +{} + +{-- + - A vertex representing an inherited attribute on the nonterminal being constructed by this production. + - + - Inherited attributes are separate for 'lhs' and not for 'rhs' because we care rather specially about lhsInh, + - as that's the bit that contributes to computing a flow type. + - + - @param attrName the full name of an inherited attribute on the lhs. + -} +abstract production lhsInhVertex +top::FlowVertex ::= attrName::String +{} + +-- TODO: we should do the above syn/inh separation for everything below too. + +{-- + - A vertex representing an attribute on an element of the signature RHS. + - + - @param sigName the name given to a signature nonterminal. + - @param attrName the full name of an attribute on that signature element. + -} +abstract production rhsVertex +top::FlowVertex ::= sigName::String attrName::String +{} + +{-- + - A vertex representing a local equation. i.e. forward, local attribute, production + - attribute, etc. Note that this may be defined for MORE than just those with + - decorable type!! (e.g. local foo :: String will appear!) + - This is because the dependencies for these local equations still matter, of coursee. + - + - @param fName the full name of the NTA/FWD being defined + -} +abstract production localEqVertex +top::FlowVertex ::= fName::String +{} + +{-- + - A vertex representing an attribute on a local equation. i.e. forward, local + - attribute, production attribute, etc. Note this this implies the equation + - above IS a decorable type! + - + - @param fName the full name of the NTA/FWD + - @param attrName the full name of the attribute on that element + -} +abstract production localVertex +top::FlowVertex ::= fName::String attrName::String +{} + +-- TODO: we should distinguish these! + +-- The forward equation for this production. We do not care to distinguish it. +function forwardEqVertex +FlowVertex ::= +{ + return localEqVertex("forward"); +} + +-- An attribute on the forward node for this production +function forwardVertex +FlowVertex ::= attrName::String +{ + return localVertex("forward", attrName); +} + +{-- + - A vertex representing an anonymous equation. i.e. a 'decorate e with..' + - expression, this production will represent 'e'. + - + - @param fName an anonymous name (typically generated with genInt) + -} +abstract production anonEqVertex +top::FlowVertex ::= fName::String +{} + +{-- + - A vertex representing an attribute on an anonymous equation. + - e.g. 'decorate e with { a = d }' this represents 'e_nt.a's deps 'd'. + - + - @param fName the anonymous name + - @param attrName the full name of the attribute on that element + -} +abstract production anonVertex +top::FlowVertex ::= fName::String attrName::String +{} diff --git a/grammars/silver/definition/flow/ast/VertexType.sv b/grammars/silver/compiler/definition/flow/ast/VertexType.sv similarity index 98% rename from grammars/silver/definition/flow/ast/VertexType.sv rename to grammars/silver/compiler/definition/flow/ast/VertexType.sv index 6ba0f7263..147e3a688 100644 --- a/grammars/silver/definition/flow/ast/VertexType.sv +++ b/grammars/silver/compiler/definition/flow/ast/VertexType.sv @@ -1,4 +1,4 @@ -grammar silver:definition:flow:ast; +grammar silver:compiler:definition:flow:ast; {-- - A "classification" of FlowVertex that has ways to map attributes to vertexes. diff --git a/grammars/silver/definition/flow/driver/DumpGraph.sv b/grammars/silver/compiler/definition/flow/driver/DumpGraph.sv similarity index 79% rename from grammars/silver/definition/flow/driver/DumpGraph.sv rename to grammars/silver/compiler/definition/flow/driver/DumpGraph.sv index d41cde4c6..044f15705 100644 --- a/grammars/silver/definition/flow/driver/DumpGraph.sv +++ b/grammars/silver/compiler/definition/flow/driver/DumpGraph.sv @@ -1,8 +1,8 @@ -grammar silver:definition:flow:driver; +grammar silver:compiler:definition:flow:driver; -import silver:driver; +import silver:compiler:driver; import silver:util:cmdargs; -import silver:util:raw:treemap as rtm; +import silver:util:treemap as rtm; -- This isn't exactly a warning, but it can live here for now... @@ -22,13 +22,19 @@ top::CmdArgs ::= rest::CmdArgs aspect function parseArgs Either ::= args::[String] { - flags <- [pair("--dump-flow-graph", flag(dumpFlowGraphFlag)), - pair("--dump-flow-graphs", flag(dumpFlowGraphFlag))]; -- I mistype this a lot. - -- omitting from descriptions deliberately! + flags <- [ flagSpec(name="--dump-flow-graph", paramString=nothing(), + help="write the flowtypes out to several Graphviz files", + flagParser=flag(dumpFlowGraphFlag)) + -- Ted mistyped this a lot. + , flagSpec(name="--dump-flow-graphs", paramString=nothing(), + help="a typo of --dump-flow-graph", + flagParser=flag(dumpFlowGraphFlag)) + ]; + -- not omitting descriptions deliberately! } aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv +top::Compilation ::= g::Grammars _ _ benv::BuildEnv { top.postOps <- if top.config.dumpFlowGraph @@ -56,13 +62,14 @@ function unList abstract production dumpFlowGraphAction top::DriverAction ::= prodGraph::[ProductionGraph] finalGraph::[ProductionGraph] flowTypes::[Pair] { - top.io = - writeFile("flow-types.dot", "digraph flow {\n" ++ generateFlowDotGraph(flowTypes) ++ "}", - writeFile("flow-deps-direct.dot", "digraph flow {\n" ++ generateDotGraph(prodGraph) ++ "}", - writeFile("flow-deps-transitive.dot", "digraph flow {\n" ++ generateDotGraph(finalGraph) ++ "}", - print("Generating flow graphs\n", top.ioIn)))); + top.run = do { + eprintln("Generating flow graphs"); + writeFile("flow-deps-transitive.dot", "digraph flow {\n" ++ generateDotGraph(finalGraph) ++ "}"); + writeFile("flow-deps-direct.dot", "digraph flow {\n" ++ generateDotGraph(prodGraph) ++ "}"); + writeFile("flow-types.dot", "digraph flow {\n" ++ generateFlowDotGraph(flowTypes) ++ "}"); + return 0; + }; - top.code = 0; top.order = 0; } @@ -75,7 +82,7 @@ String ::= flowTypes::[Pair] return if null(flowTypes) then "" else "subgraph \"cluster:" ++ nt ++ "\" {\nlabel=\"" ++ substring(lastIndexOf(":", nt) + 1, length(nt), nt) ++ "\";\n" ++ - implode("", map(makeLabelDcls(nt, _), nubBy(stringEq, expandLabels(edges)))) ++ + implode("", map(makeLabelDcls(nt, _), nub(expandLabels(edges)))) ++ implode("", map(makeNtFlow(nt, _), edges)) ++ "}\n" ++ generateFlowDotGraph(tail(flowTypes)); diff --git a/grammars/silver/definition/flow/driver/FlowGraph.sv b/grammars/silver/compiler/definition/flow/driver/FlowGraph.sv similarity index 89% rename from grammars/silver/definition/flow/driver/FlowGraph.sv rename to grammars/silver/compiler/definition/flow/driver/FlowGraph.sv index fe078255e..939ee375f 100644 --- a/grammars/silver/definition/flow/driver/FlowGraph.sv +++ b/grammars/silver/compiler/definition/flow/driver/FlowGraph.sv @@ -1,4 +1,4 @@ -grammar silver:definition:flow:driver; +grammar silver:compiler:definition:flow:driver; type FlowType = g:Graph; @@ -7,7 +7,7 @@ FlowType ::= prod::String e::EnvTree { local lookup :: [FlowType] = searchEnvTree(prod, e); - return if null(lookup) then g:empty(compareString) else head(lookup); + return if null(lookup) then g:empty() else head(lookup); } function findProductionGraph ProductionGraph ::= n::String l::EnvTree @@ -24,14 +24,14 @@ function expandGraph { -- look up each vertex, uniq it down. local initial :: set:Set = - set:add(v, foldr(set:union, set:empty(compareFlowVertex), map(e.edgeMap, v))); + set:add(v, foldr(set:union, set:empty(), map(e.edgeMap, v))); return set:toList(expandSuspectEdges(set:toList(initial), initial, e)); } function onlyLhsInh set:Set ::= s::[FlowVertex] { - return set:add(filterLhsInh(s), set:empty(compareString)); + return set:add(filterLhsInh(s), set:empty()); } -- suspect edges are not in the standard graph, so iteratively add them @@ -77,7 +77,7 @@ Boolean ::= v::FlowVertex inhSet::set:Set function createFlowGraph g:Graph ::= l::[Pair] { - return g:add(l, g:empty(compareFlowVertex)); + return g:add(l, g:empty()); } function extendFlowGraph diff --git a/grammars/silver/compiler/definition/flow/driver/FlowTypes.sv b/grammars/silver/compiler/definition/flow/driver/FlowTypes.sv new file mode 100644 index 000000000..8542bcd47 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/driver/FlowTypes.sv @@ -0,0 +1,206 @@ +grammar silver:compiler:definition:flow:driver; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +--import silver:compiler:definition:flow:env; +imports silver:compiler:definition:flow:ast; +imports silver:compiler:analysis:warnings:flow only isOccursSynthesized, isAutocopy; + +imports silver:compiler:modification:autocopyattr; + +imports silver:util:treemap as rtm; +imports silver:util:graph as g; +imports silver:util:treeset as set; + +-- Help some type signatures suck a little less +type ProdName = String; +type NtName = String; + +-- from explicit specifications +function computeInitialFlowTypes +EnvTree ::= specDefs::[(String, String, [String], [String])] +{ + -- We don't care what flow specs reference what + local dropRefs::[(String, String, [String])] = + map(\ d::(String, String, [String], [String]) -> (d.1, d.2, d.3), specDefs); + + local specs :: [(NtName, [(String, [String])])] = + ntListCoalesce(groupBy(ntListEq, sortBy(ntListLte, dropRefs))); + + return rtm:add(map(initialFlowType, specs), rtm:empty()); +} +function initialFlowType +Pair ::= x::(NtName, [(String, [String])]) +{ + return pair(x.fst, g:add(flatMap(toFlatEdges, x.snd), g:empty())); +} +function ntListLte +Boolean ::= a::Pair b::Pair +{ + return a.fst <= b.fst; +} +function ntListEq +Boolean ::= a::Pair b::Pair +{ + return a.fst == b.fst; +} +function ntListCoalesce +[(NtName, [(String, [String])])] ::= l::[[(NtName, String, [String])]] +{ + return if null(l) then [] + else pair(head(head(l)).fst, map(snd, head(l))) :: ntListCoalesce(tail(l)); +} +function toFlatEdges +[Pair] ::= x::Pair +{ + return map(pair(x.fst, _), x.snd); +} + + +{-- + - Produces flow types for every nonterminal. + - Iterates until convergence. + -} +function fullySolveFlowTypes +Pair<[ProductionGraph] EnvTree> ::= + graphs::[ProductionGraph] + ntEnv::EnvTree +{ + -- Each iteration, we rebuild this... :/ + -- TODO: consider a 'mapValuesWithMapState' function :: 'k1, v1, map, map -> v1, k2, v2' + local prodEnv :: EnvTree = + directBuildTree(map(prodGraphToEnv, graphs)); + + local iter :: Pair>> = + solveFlowTypes(graphs, prodEnv, ntEnv); + + -- Just iterate until no new edges are added + return if !iter.fst then iter.snd + else fullySolveFlowTypes(iter.snd.fst, iter.snd.snd); +} + +{-- + - One iteration of solving flow type equations. Goes through each production once. + -} +function solveFlowTypes +Pair>> ::= + graphs::[ProductionGraph] + prodEnv::EnvTree + ntEnv::EnvTree +{ + local updatedGraph :: ProductionGraph = updateGraph(head(graphs), prodEnv, ntEnv); + + local currentFlowType :: FlowType = findFlowType(updatedGraph.lhsNt, ntEnv); + + -- The New Improved Flow Type + local synExpansion :: [Pair] = + map(expandVertexFilterTo(_, updatedGraph), updatedGraph.flowTypeVertexes); + + -- Find what edges are NEW NEW NEW + local brandNewEdges :: [Pair] = + findBrandNewEdges(synExpansion, currentFlowType); + + local newFlowType :: FlowType = + g:add(brandNewEdges, currentFlowType); + -- TODO: we could just always "add everything unconditionally" but we also need to know if there were + -- any new additions... so we'd need something added to graph to support that. + + local recurse :: Pair>> = + solveFlowTypes(tail(graphs), prodEnv, rtm:update(updatedGraph.lhsNt, [newFlowType], ntEnv)); + + return if null(graphs) then pair(false, pair([], ntEnv)) + else pair(!null(brandNewEdges) || recurse.fst, pair(updatedGraph :: recurse.snd.fst, recurse.snd.snd)); +} + + +function findBrandNewEdges +[Pair] ::= candidates::[Pair] currentFlowType::FlowType +{ + local syn :: String = head(candidates).fst; + local inhs :: [String] = head(candidates).snd; + + -- TODO: we might take '[Pair>]' insteadof [String] and gain speed? + local newinhs :: [String] = removeAll(set:toList(g:edgesFrom(syn, currentFlowType)), inhs); + + local newEdges :: [Pair] = map(pair(syn, _), newinhs); + + return if null(candidates) then [] else newEdges ++ findBrandNewEdges(tail(candidates), currentFlowType); +} + +-- Expand 'ver' using 'graph', then filter down to just those in 'inhs' +function expandVertexFilterTo +Pair ::= ver::FlowVertex graph::ProductionGraph +{ + -- TODO: we might return 'Pair>' instead of [String] and gain speed? + -- Have set:filter, don't have "set:map" yet... (FlowVertex->String) + return pair(ver.flowTypeName, filterLhsInh(set:toList(graph.edgeMap(ver)))); +} + +{-- + - Filters vertexes down to just the names of inherited attributes on the LHS + -} +function filterLhsInh +[String] ::= f::[FlowVertex] +{ + return foldr(collectInhs, [], f); +} + +{-- + - Used to filter down to just the inherited attributes (on the LHS) + - + - @param f The flow vertex in question + - @param l The current set of inherited attribute dependencies + - @return {l} with {f} added to it + -} +function collectInhs +[String] ::= f::FlowVertex l::[String] +{ + return case f of + | lhsInhVertex(a) -> a::l + | _ -> l + end; +} + + +{-- + - Flow type lookup names for vertices + -} +synthesized attribute flowTypeName :: String occurs on FlowVertex; + +aspect production lhsSynVertex +top::FlowVertex ::= attrName::String +{ + top.flowTypeName = attrName; +} +aspect production lhsInhVertex +top::FlowVertex ::= attrName::String +{ + top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for inherited attributes?"); +} +aspect production rhsVertex +top::FlowVertex ::= sigName::String attrName::String +{ + top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for child attributes?"); +} +aspect production localEqVertex +top::FlowVertex ::= fName::String +{ + top.flowTypeName = fName; -- secretly only ever "forward" when we actually demand flowTypeName +} +aspect production localVertex +top::FlowVertex ::= fName::String attrName::String +{ + top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for local inherited attributes?"); +} +aspect production anonEqVertex +top::FlowVertex ::= fName::String +{ + top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for anon equations?"); +} +aspect production anonVertex +top::FlowVertex ::= fName::String attrName::String +{ + top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for anon inherited attributes?"); +} diff --git a/grammars/silver/definition/flow/driver/ProductionGraph.sv b/grammars/silver/compiler/definition/flow/driver/ProductionGraph.sv similarity index 91% rename from grammars/silver/definition/flow/driver/ProductionGraph.sv rename to grammars/silver/compiler/definition/flow/driver/ProductionGraph.sv index 18ac5a870..c52b5fb22 100644 --- a/grammars/silver/definition/flow/driver/ProductionGraph.sv +++ b/grammars/silver/compiler/definition/flow/driver/ProductionGraph.sv @@ -1,7 +1,6 @@ -grammar silver:definition:flow:driver; +grammar silver:compiler:definition:flow:driver; -import silver:util only contains, rem; -import silver:definition:type only isDecorable; +import silver:compiler:definition:type only isNonterminal, typerep; nonterminal ProductionGraph with flowTypes, stitchedGraph, prod, lhsNt, transitiveClosure, edgeMap, suspectEdgeMap, cullSuspect, flowTypeVertexes, prodGraphs; @@ -78,7 +77,7 @@ top::ProductionGraph ::= productionGraph(prod, lhsNt, flowTypeVertexes, transitiveClosure, suspectEdges, stitchPoints) end; top.edgeMap = g:edgesFrom(_, graph); - top.suspectEdgeMap = lookupAllBy(equalFlowVertex, _, suspectEdges); + top.suspectEdgeMap = lookupAll(_, suspectEdges); top.cullSuspect = -- this potentially introduces the same edge twice, but that's a nonissue @@ -108,7 +107,7 @@ ProductionGraph ::= -- construct a production graph for each production function computeAllProductionGraphs -[ProductionGraph] ::= prods::[DclInfo] prodTree::EnvTree flowEnv::Decorated FlowEnv realEnv::Decorated Env +[ProductionGraph] ::= prods::[ValueDclInfo] prodTree::EnvTree flowEnv::FlowEnv realEnv::Decorated Env { return if null(prods) then [] else constructProductionGraph(head(prods), searchEnvTree(head(prods).fullName, prodTree), flowEnv, realEnv) :: @@ -155,14 +154,14 @@ function computeAllProductionGraphs - @return A fixed up graph. -} function constructProductionGraph -ProductionGraph ::= dcl::DclInfo defs::[FlowDef] flowEnv::Decorated FlowEnv realEnv::Decorated Env +ProductionGraph ::= dcl::ValueDclInfo defs::[FlowDef] flowEnv::FlowEnv realEnv::Decorated Env { -- The name of this production local prod :: String = dcl.fullName; -- The LHS nonterminal full name local nt :: NtName = dcl.namedSignature.outputElement.typerep.typeName; -- All attributes occurrences - local attrs :: [DclInfo] = getAttrsOn(nt, realEnv); + local attrs :: [OccursDclInfo] = getAttrsOn(nt, realEnv); -- Just synthesized attributes. local syns :: [String] = map((.attrOccurring), filter(isOccursSynthesized(_, realEnv), attrs)); -- Just inherited. @@ -199,7 +198,7 @@ ProductionGraph ::= dcl::DclInfo defs::[FlowDef] flowEnv::Decorated FlowEnv r -- RHS and locals and forward. local stitchPoints :: [StitchPoint] = - rhsStitchPoints(dcl.namedSignature.inputElements) ++ + flatMap(rhsStitchPoints, dcl.namedSignature.inputElements) ++ localStitchPoints(nt, defs) ++ patternStitchPoints(realEnv, defs); @@ -229,7 +228,7 @@ ProductionGraph ::= dcl::DclInfo defs::[FlowDef] flowEnv::Decorated FlowEnv r - @param ntEnv The flow types we've previously computed -} function constructFunctionGraph -ProductionGraph ::= ns::NamedSignature flowEnv::Decorated FlowEnv realEnv::Decorated Env prodEnv::EnvTree ntEnv::EnvTree +ProductionGraph ::= ns::NamedSignature flowEnv::FlowEnv realEnv::Decorated Env prodEnv::EnvTree ntEnv::EnvTree { local prod :: String = ns.fullName; local nt :: NtName = "::nolhs"; -- the same hack we use elsewhere @@ -247,7 +246,7 @@ ProductionGraph ::= ns::NamedSignature flowEnv::Decorated FlowEnv realEnv::Dec -- RHS and locals and forward. local stitchPoints :: [StitchPoint] = - rhsStitchPoints(ns.inputElements) ++ + flatMap(rhsStitchPoints, ns.inputElements) ++ localStitchPoints(error("functions shouldn't have a forwarding equation?"), defs) ++ patternStitchPoints(realEnv, defs); @@ -343,14 +342,14 @@ ProductionGraph ::= ns::NamedSignature defs::[FlowDef] realEnv::Decorated Env - @return A fixed up graph. -} function constructPhantomProductionGraph -ProductionGraph ::= nt::String flowEnv::Decorated FlowEnv realEnv::Decorated Env +ProductionGraph ::= nt::String flowEnv::FlowEnv realEnv::Decorated Env { -- All attributes occurrences - local attrs :: [DclInfo] = getAttrsOn(nt, realEnv); + local attrs :: [OccursDclInfo] = getAttrsOn(nt, realEnv); -- Just synthesized attributes. local syns :: [String] = map((.attrOccurring), filter(isOccursSynthesized(_, realEnv), attrs)); -- Those syns that are not part of the host, and so should have edges to fwdeq - local extSyns :: [String] = rem(syns, getHostSynsFor(nt, flowEnv)); + local extSyns :: [String] = removeAll(getHostSynsFor(nt, flowEnv), syns); -- The phantom edges: ext syn -> fwd.eq local phantomEdges :: [Pair] = @@ -381,7 +380,7 @@ Pair ::= at::String - Called twice: once for safe edges, later for SUSPECT edges! -} function addFwdSynEqs -[Pair] ::= prod::ProdName syns::[String] flowEnv::Decorated FlowEnv +[Pair] ::= prod::ProdName syns::[String] flowEnv::FlowEnv { return if null(syns) then [] else (if null(lookupSyn(prod, head(syns), flowEnv)) @@ -394,7 +393,7 @@ function addFwdSynEqs - Inherited equations are never suspect. -} function addFwdInhEqs -[Pair] ::= prod::ProdName inhs::[String] flowEnv::Decorated FlowEnv +[Pair] ::= prod::ProdName inhs::[String] flowEnv::FlowEnv { return if null(inhs) then [] else (if null(lookupFwdInh(prod, head(inhs), flowEnv)) then [pair(forwardVertex(head(inhs)), lhsInhVertex(head(inhs)))] else []) ++ @@ -404,7 +403,7 @@ function addFwdInhEqs - Introduces default equations deps. Realistically, should be empty, always. -} function addDefEqs -[Pair] ::= prod::ProdName nt::NtName syns::[String] flowEnv :: Decorated FlowEnv +[Pair] ::= prod::ProdName nt::NtName syns::[String] flowEnv :: FlowEnv { return if null(syns) then [] else (if null(lookupSyn(prod, head(syns), flowEnv)) @@ -419,14 +418,14 @@ function addDefEqs - Inherited equations are never suspect. -} function addAllAutoCopyEqs -[Pair] ::= prod::ProdName sigNames::[NamedSignatureElement] inhs::[String] flowEnv::Decorated FlowEnv realEnv::Decorated Env +[Pair] ::= prod::ProdName sigNames::[NamedSignatureElement] inhs::[String] flowEnv::FlowEnv realEnv::Decorated Env { return if null(sigNames) then [] else addAutocopyEqs(prod, head(sigNames), inhs, flowEnv, realEnv) ++ addAllAutoCopyEqs(prod, tail(sigNames), inhs, flowEnv, realEnv); } -- Helper for above. function addAutocopyEqs -[Pair] ::= prod::ProdName sigName::NamedSignatureElement inhs::[String] flowEnv::Decorated FlowEnv realEnv::Decorated Env +[Pair] ::= prod::ProdName sigName::NamedSignatureElement inhs::[String] flowEnv::FlowEnv realEnv::Decorated Env { return if null(inhs) then [] else (if null(lookupInh(prod, sigName.elementName, head(inhs), flowEnv)) -- no equation @@ -447,26 +446,22 @@ function localStitchPoints | [] -> [] -- We add the forward stitch point here, too! | fwdEq(_, _, _) :: rest -> nonterminalStitchPoint(nt, forwardVertexType) :: localStitchPoints(nt, rest) - -- Ignore locals that aren't nonterminal types! - | localEq(_, fN, "", _) :: rest -> localStitchPoints(nt, rest) -- Add locals that are nonterminal types. - | localEq(_, fN, tN, _) :: rest -> nonterminalStitchPoint(tN, localVertexType(fN)) :: localStitchPoints(nt, rest) - -- Add all anon decoration sites - | anonEq(_, fN, tN, _, _) :: rest -> nonterminalStitchPoint(tN, anonVertexType(fN)) :: localStitchPoints(nt, rest) + | localEq(_, fN, tN, true, _) :: rest -> nonterminalStitchPoint(tN, localVertexType(fN)) :: localStitchPoints(nt, rest) + -- Add anon decoration sites that are nonterminal types + | anonEq(_, fN, tN, true, _, _) :: rest -> nonterminalStitchPoint(tN, anonVertexType(fN)) :: localStitchPoints(nt, rest) -- Ignore all other flow def info | _ :: rest -> localStitchPoints(nt, rest) end; } function rhsStitchPoints -[StitchPoint] ::= rhs::[NamedSignatureElement] +[StitchPoint] ::= rhs::NamedSignatureElement { - return if null(rhs) then [] - -- We want only NONTERMINAL stitch points! - else if head(rhs).typerep.isDecorable - then nonterminalStitchPoint( - head(rhs).typerep.typeName, - rhsVertexType(head(rhs).elementName)) :: rhsStitchPoints(tail(rhs)) - else rhsStitchPoints(tail(rhs)); + return + -- We want only NONTERMINAL stitch points! + if rhs.typerep.isNonterminal + then [nonterminalStitchPoint(rhs.typerep.typeName, rhsVertexType(rhs.elementName))] + else []; } function patternStitchPoints [StitchPoint] ::= realEnv::Decorated Env defs::[FlowDef] @@ -506,7 +501,7 @@ Pair ::= p::ProductionGraph return pair(p.prod, p); } function isOccursInherited -Boolean ::= occs::DclInfo e::Decorated Env +Boolean ::= occs::OccursDclInfo e::Decorated Env { return case getAttrDcl(occs.attrOccurring, e) of | at :: _ -> at.isInherited diff --git a/grammars/silver/definition/flow/driver/StitchPoint.sv b/grammars/silver/compiler/definition/flow/driver/StitchPoint.sv similarity index 98% rename from grammars/silver/definition/flow/driver/StitchPoint.sv rename to grammars/silver/compiler/definition/flow/driver/StitchPoint.sv index 7f99964f0..9cacb5f30 100644 --- a/grammars/silver/definition/flow/driver/StitchPoint.sv +++ b/grammars/silver/compiler/definition/flow/driver/StitchPoint.sv @@ -1,4 +1,4 @@ -grammar silver:definition:flow:driver; +grammar silver:compiler:definition:flow:driver; nonterminal StitchPoint with prodGraphs, flowTypes, stitchEdges; diff --git a/grammars/silver/compiler/definition/flow/env/Expr.sv b/grammars/silver/compiler/definition/flow/env/Expr.sv new file mode 100644 index 000000000..b682b81ca --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/Expr.sv @@ -0,0 +1,298 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:type; +import silver:compiler:modification:copper; +import silver:compiler:modification:primitivepattern; +import silver:compiler:extension:patternmatching only Arrow_kwd, Vbar_kwd; -- TODO remove +import silver:compiler:modification:let_fix; + +import silver:compiler:driver:util only isExportedBy; + +{-- + - Direct (potential) dependencies this expression has on nodes in the production flow graph. + -} +monoid attribute flowDeps :: [FlowVertex]; +{-- + - Determines whether this expression corresponds to a node in the flow graph, and how + - to treat it specially if so. + -} +synthesized attribute flowVertexInfo :: ExprVertexInfo; + +-- flowDefs because expressions (decorate, patterns) can now generate stitchpoints +attribute flowDeps, flowDefs, flowEnv occurs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr; +attribute flowVertexInfo occurs on Expr; + +propagate flowDeps on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr + excluding + childReference, lhsReference, localReference, forwardReference, forwardAccess, synDecoratedAccessHandler, inhDecoratedAccessHandler, + decorateExprWith, letp, lexicalLocalReference, matchPrimitiveReal; +propagate flowDefs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr; + +aspect default production +top::Expr ::= +{ + -- We go with using default here because + -- (a) it's safe. vertexInfo is for being less conservative and more precise. + -- (b) only a few productions actually provide it. + top.flowVertexInfo = noVertex(); +} + +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + -- Note that q should find the actual type written in the signature, and so + -- isDecorable on that indeed tells us whether it's something autodecorated. + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + production origRefSet::[String] = getMinRefSet(q.lookupValue.typeScheme.monoType, top.env); + top.flowDeps := + if isDecorable(q.lookupValue.typeScheme.monoType, top.env) && finalTy.isDecorated + then map(rhsVertexType(q.lookupValue.fullName).inhVertex, removeAll(origRefSet, fromMaybe([], refSet))) + else []; + top.flowVertexInfo = + if isDecorable(q.lookupValue.typeScheme.monoType, top.env) && finalTy.isDecorated + then hasVertex(rhsVertexType(q.lookupValue.fullName)) + else noVertex(); + + top.flowDefs <- + case finalTy, refSet of + | partiallyDecoratedType(_, _), just(inhs) + when isExportedBy(top.grammarName, [q.lookupValue.dcl.sourceGrammar], top.compiledGrammars) -> + [partialRef(top.frame.fullName, q.lookupValue.fullName, top.grammarName, q.location, inhs)] + | _, _ -> [] + end; +} +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + -- Always a decorable type, so just check how it's being used: + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + top.flowDeps := + if finalTy.isDecorated + then map(lhsVertexType.inhVertex, fromMaybe([], refSet)) + else []; + top.flowVertexInfo = + if finalTy.isDecorated + then hasVertex(lhsVertexType) + else noVertex(); +} +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + -- Again, q give the actual type written. + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + production origRefSet::[String] = getMinRefSet(q.lookupValue.typeScheme.monoType, top.env); + top.flowDeps := [localEqVertex(q.lookupValue.fullName)] ++ + if isDecorable(q.lookupValue.typeScheme.monoType, top.env) && finalTy.isDecorated + then map(rhsVertexType(q.lookupValue.fullName).inhVertex, removeAll(origRefSet, fromMaybe([], refSet))) + else []; + + top.flowVertexInfo = + if isDecorable(q.lookupValue.typeScheme.monoType, top.env) && finalTy.isDecorated + then hasVertex(localVertexType(q.lookupValue.fullName)) + else noVertex(); + + top.flowDefs <- + case finalTy, refSet of + | partiallyDecoratedType(_, _), just(inhs) + when isExportedBy(top.grammarName, [q.lookupValue.dcl.sourceGrammar], top.compiledGrammars) -> + [partialRef(top.frame.fullName, q.lookupValue.fullName, top.grammarName, q.location, inhs)] + | _, _ -> [] + end; +} +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + -- Again, always a decorable type. + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + top.flowDeps := [forwardEqVertex()]++ + if finalTy.isDecorated + then map(forwardVertexType.inhVertex, fromMaybe([], refSet)) + else []; + + top.flowVertexInfo = + if finalTy.isDecorated + then hasVertex(forwardVertexType) + else noVertex(); +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + top.flowDeps := + case e.flowVertexInfo of + | hasVertex(vertex) -> vertex.fwdVertex :: vertex.eqVertex + | noVertex() -> e.flowDeps + end; +} + +-- Note that below we IGNORE the flow deps of the lhs if we know what it is +-- this is because by default the lhs will have 'taking ref' flow deps (see above) +aspect production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.flowDeps := + case e.flowVertexInfo of + | hasVertex(vertex) -> vertex.synVertex(q.attrDcl.fullName) :: vertex.eqVertex + | noVertex() -> e.flowDeps + end; +} +aspect production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.flowDeps := + case e.flowVertexInfo of + | hasVertex(vertex) -> vertex.inhVertex(q.attrDcl.fullName) :: vertex.eqVertex + | noVertex() -> e.flowDeps + end; +} + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + -- The general theory: + -- ... some expr ... decorate EXP1 with { ... inhs ... } ... + -- is equivalent to: + -- local ANON :: EXP1.typerep = EXP1; + -- ANON.inhN = inhNexp; -- etc... + -- an the expr is now ... ANON ... + + -- We don't actually do this transform, of course, but that's what we're representing + -- this as to the flow analysis, and justifies all the choices below: + + -- First, generate our "anonymous" flow vertex name: + inh.decorationVertex = "__decorate" ++ toString(genInt()) ++ ":line" ++ toString(top.location.line); + + -- Next, emit the "local equation" for this anonymous flow vertex. + -- This means only the deps in 'e', see above conceptual transformation to see why. + -- N.B. 'inh.flowDefs' will emit 'localInhEq's for this anonymous flow vertex. + local eTy::Type = performSubstitution(e.typerep, top.finalSubst); + top.flowDefs <- + [anonEq(top.frame.fullName, inh.decorationVertex, eTy.typeName, eTy.isNonterminal, top.location, e.flowDeps)]; + + -- Now, we represent ourselves to anything that might use us specially + -- as though we were a reference to this anonymous local + top.flowVertexInfo = hasVertex(anonVertexType(inh.decorationVertex)); + + -- Finally, our standard flow deps mimic those of a local: "taking a reference" + -- This are of course ignored when treated specially. + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + top.flowDeps := [anonEqVertex(inh.decorationVertex)] ++ + map(anonVertexType(inh.decorationVertex).inhVertex, fromMaybe([], refSet)); + + -- If we have a type var with occurs-on contexts, add the specified syn -> inh deps for the new vertex + top.flowDefs <- occursContextDeps(top.frame.signature, top.env, finalTy, anonVertexType(inh.decorationVertex)); +} + +autocopy attribute decorationVertex :: String occurs on ExprInhs, ExprInh; + +aspect production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e1::Expr ';' +{ + top.flowDefs <- + if !null(lhs.errors) then [] else + case lhs of + | exprLhsExpr(q) -> [anonInhEq(top.frame.fullName, top.decorationVertex, q.attrDcl.fullName, e1.flowDeps)] + end; + +} + +aspect production exprRef +top::Expr ::= e::PartiallyDecorated Expr +{ + top.flowVertexInfo = e.flowVertexInfo; +} + +-- FROM LET TODO +attribute flowDefs, flowEnv occurs on AssignExpr; +propagate flowDefs on AssignExpr; + +aspect production letp +top::Expr ::= la::AssignExpr e::Expr +{ + top.flowDeps := e.flowDeps; +} + +aspect production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName fi::ExprVertexInfo fd::[FlowVertex] +{ + -- Because of the auto-undecorate behavior, we need to check for the case + -- where `t` should be equivalent to `new(t)` and report accoringly. + + -- If we: + -- 1. Have a flow vertex + -- 2. Are a decorated type + -- 3. Used as undecorated type + -- Then: Suppress `fd` and report just `fi.eq` + + top.flowDeps := + case fi of + | hasVertex(vertex) -> + if performSubstitution(q.lookupValue.typeScheme.monoType, top.finalSubst).isDecorated && + !performSubstitution(top.typerep, top.finalSubst).isDecorated + then vertex.eqVertex -- we're a `t` emulating `new(t)` + else fd -- we're passing along our vertex-ness to the outer expression + | noVertex() -> fd -- we're actually being used as a ref-set-taking decorated var + end; + top.flowVertexInfo = fi; +} + + +-- FROM PATTERN TODO +attribute flowDeps, flowDefs, flowEnv, scrutineeVertexType occurs on PrimPatterns, PrimPattern; +propagate flowDeps, flowDefs on PrimPatterns, PrimPattern; + +autocopy attribute scrutineeVertexType :: VertexType; + +aspect production matchPrimitiveReal +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + -- If we take e.flowDeps ++ f.flowDeps, look them all up in the production + -- graph, and take the union, then filter down to just those on our anon vertex + -- we can discover what's needed, and use that to raise errors. + + -- We do have to do the lookups, though: we can't just use those Deps directly. + -- consider 'case e of prod(x) -> decorate x.syn with ...' + -- that introduces the use of 'x.syn' in a flowDef, and then emits the anonEq in flowDep + -- so we DO need to be transitive. Unfortunately. + + -- hack note: there's a test that depends on this name starting with __scrutinee. grep for it if you have to change this + local anonName :: String = "__scrutinee" ++ toString(genInt()) ++ ":line" ++ toString(e.location.line); + + pr.scrutineeVertexType = + case e.flowVertexInfo of + | hasVertex(vertex) -> vertex + | noVertex() -> anonVertexType(anonName) + end; + + -- Let's make sure for decorated types, we only demand what's necessary for forward + -- evaluation. + top.flowDeps := pr.flowDeps ++ f.flowDeps ++ + (pr.scrutineeVertexType.fwdVertex :: pr.scrutineeVertexType.eqVertex); + + local eTy::Type = performSubstitution(e.typerep, top.finalSubst); + top.flowDefs <- + case e.flowVertexInfo of + | hasVertex(vertex) -> [] + | noVertex() -> [anonEq(top.frame.fullName, anonName, eTy.typeName, eTy.isNonterminal, top.location, e.flowDeps)] + end; + -- We want to use anonEq here because that introduces the nonterminal stitch point for our vertex. +} + +aspect production prodPatternNormal +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.flowDefs <- + [patternRuleEq(top.frame.fullName, qn.lookupValue.fullName, top.scrutineeVertexType, ns.flowProjections)]; +} +aspect production prodPatternGadt +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.flowDefs <- + [patternRuleEq(top.frame.fullName, qn.lookupValue.fullName, top.scrutineeVertexType, ns.flowProjections)]; +} diff --git a/grammars/silver/compiler/definition/flow/env/FlowEnv.sv b/grammars/silver/compiler/definition/flow/env/FlowEnv.sv new file mode 100644 index 000000000..3ba298ea4 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/FlowEnv.sv @@ -0,0 +1,202 @@ +grammar silver:compiler:definition:flow:env; + +imports silver:compiler:definition:flow:ast; +imports silver:compiler:definition:env; +imports silver:compiler:definition:core; + +import silver:compiler:definition:type; + + +autocopy attribute flowEnv :: FlowEnv; +monoid attribute flowDefs :: [FlowDef]; +-- These are factored out of FlowDefs to avoid a circular dependency, +-- since they are needed during type checking +monoid attribute specDefs :: [(String, String, [String], [String])]; -- (nt, attr, [inhs], [referenced flow specs]) +monoid attribute refDefs :: [(String, [String])]; + +nonterminal FlowEnv with synTree, inhTree, defTree, fwdTree, prodTree, fwdInhTree, refTree, partialRefTree, localInhTree, localTree, nonSuspectTree, hostSynTree, specTree, prodGraphTree; + +annotation synTree :: EnvTree; +annotation inhTree :: EnvTree; +annotation defTree :: EnvTree; +annotation fwdTree :: EnvTree; +annotation fwdInhTree :: EnvTree; +annotation prodTree :: EnvTree; +annotation refTree :: EnvTree<[String]>; +annotation partialRefTree :: EnvTree<(String, Location, [String])>; +annotation localInhTree ::EnvTree; +annotation localTree :: EnvTree; +annotation nonSuspectTree :: EnvTree<[String]>; +annotation hostSynTree :: EnvTree; +annotation specTree :: EnvTree<(String, [String], [String])>; +annotation prodGraphTree :: EnvTree; + +abstract production flowEnv +top::FlowEnv ::= +{} + +function fromFlowDefs +FlowEnv ::= + specContribs::[(String, String, [String], [String])] refContribs::[(String, [String])] + d::FlowDefs +{ + return flowEnv( + synTree = directBuildTree(d.synTreeContribs), + inhTree = directBuildTree(d.inhTreeContribs), + defTree = directBuildTree(d.defTreeContribs), + fwdTree = directBuildTree(d.fwdTreeContribs), + fwdInhTree = directBuildTree(d.fwdInhTreeContribs), + prodTree = directBuildTree(d.prodTreeContribs), + refTree = directBuildTree(refContribs), + partialRefTree = directBuildTree(d.partialRefContribs), + localInhTree = directBuildTree(d.localInhTreeContribs), + localTree = directBuildTree(d.localTreeContribs), + nonSuspectTree = directBuildTree(d.nonSuspectContribs), + hostSynTree = directBuildTree(d.hostSynTreeContribs), + specTree = directBuildTree(specContribs), + prodGraphTree = directBuildTree(d.prodGraphContribs) + ); +} + + +-- synthesized equation in a production +function lookupSyn +[FlowDef] ::= prod::String attr::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, attr), e.synTree); +} + +-- inherited equation for a child in a production +function lookupInh +[FlowDef] ::= prod::String sigName::String attr::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, crossnames(sigName, attr)), e.inhTree); +} + +-- default equation for a nonterminal +function lookupDef +[FlowDef] ::= nt::String attr::String e::FlowEnv +{ + return searchEnvTree(crossnames(nt, attr), e.defTree); +} + +-- forward equation for a production +function lookupFwd +[FlowDef] ::= prod::String e::FlowEnv +{ + return searchEnvTree(prod, e.fwdTree); +} + +-- inherited equation for the forward in a production +function lookupFwdInh +[FlowDef] ::= prod::String attr::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, attr), e.fwdInhTree); +} + +-- inherited equation for a local in a production +function lookupLocalInh +[FlowDef] ::= prod::String fName::String attr::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, crossnames(fName, attr)), e.localInhTree); +} + +function lookupLocalEq +[FlowDef] ::= prod::String fName::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, fName), e.localTree); +} + +-- default set of inherited attributes required/assumed to exist for references +function getInhsForNtRef +[[String]] ::= nt::String e::FlowEnv +{ + return searchEnvTree(nt, e.refTree); +} + +-- partially decorated references taken for a child/local/production attribute +function getPartialRefs +[(String, Location, [String])] ::= prod::String fName::String e::FlowEnv +{ + return searchEnvTree(crossnames(prod, fName), e.partialRefTree); +} + +-- implicit forward syn copy equations that are allowed to affect the flow type +function getNonSuspectAttrsForProd +[String] ::= prod::String e::FlowEnv +{ + return concat(searchEnvTree(prod, e.nonSuspectTree)); +} + +-- all (non-forwarding) productions constructing a nonterminal +function getNonforwardingProds +[String] ::= nt::String e::FlowEnv +{ + local extractProdName :: (String ::= FlowDef) = + \p::FlowDef -> case p of prodFlowDef(_, p) -> p | _ -> error("Searches only prod defs") end; + + return map(extractProdName, searchEnvTree(nt, e.prodTree)); +} + +-- Ext Syns subject to ft lower bound +function getHostSynsFor +[String] ::= nt::String e::FlowEnv +{ + local extractHostSynName :: (String ::= FlowDef) = + \f::FlowDef -> case f of hostSynFlowDef(_, at) -> at | _ -> error("Searches only host defs") end; + + return map(extractHostSynName, searchEnvTree(nt, e.hostSynTree)); +} + +-- Get syns (and "forward") that have flow types specified +function getSpecifiedSynsForNt +[String] ::= nt::String e::FlowEnv +{ + return map(fst, searchEnvTree(nt, e.specTree)); +} +function getFlowTypeSpecFor +[(String, [String], [String])] ::= nt::String e::FlowEnv +{ + return searchEnvTree(nt, e.specTree); +} + +function getGraphContribsFor +[FlowDef] ::= prod::String e::FlowEnv +{ + return searchEnvTree(prod, e.prodGraphTree); +} + +monoid attribute occursContextInhDeps::[(String, String, [String])] -- (type name, syn, inhs) + occurs on Contexts, Context; +monoid attribute occursContextInhSetDeps::[(String, String, [TyVar])] -- (type name, syn, InhSet tyvars) + occurs on Contexts, Context; +propagate occursContextInhDeps, occursContextInhSetDeps on Contexts; + +aspect default production +top::Context ::= +{ + top.occursContextInhDeps := []; + top.occursContextInhSetDeps := []; +} +aspect production synOccursContext +top::Context ::= syn::String _ _ inhs::Type ntty::Type +{ + local maxInhSetMembers::(Maybe<[String]>, [TyVar]) = getMaxInhSetMembers([], inhs, top.env); + top.occursContextInhDeps := + case maxInhSetMembers.fst of + | just(inhAttrs) -> [(ntty.typeName, syn, inhAttrs)] + | nothing() -> [] + end; + top.occursContextInhSetDeps := [(ntty.typeName, syn, maxInhSetMembers.snd)]; +} + +-- Defs for the dependencies introduced by syn occurs-on contexts at a decoration site +function occursContextDeps +[FlowDef] ::= ns::NamedSignature env::Decorated Env t::Type vt::VertexType +{ + local contexts::Contexts = foldContexts(ns.contexts); + contexts.env = env; + return map( + \ synDeps::(String, [String]) -> synOccursContextEq(ns.fullName, vt, synDeps.fst, synDeps.snd), + lookupAll(t.typeName, contexts.occursContextInhDeps)); +} diff --git a/grammars/silver/compiler/definition/flow/env/FunctionDcl.sv b/grammars/silver/compiler/definition/flow/env/FunctionDcl.sv new file mode 100644 index 000000000..c66469738 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/FunctionDcl.sv @@ -0,0 +1,36 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type only typerep; +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructFunctionGraph; +import silver:compiler:driver:util only RootSpec; -- actually we just want the occurrences + +attribute flowEnv occurs on FunctionSignature, FunctionLHS; + +aspect production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + {-- Used by core to send down with .frame -} + production myFlowGraph :: ProductionGraph = + constructFunctionGraph(namedSig, top.flowEnv, top.env, myProds, myFlow); + + top.flowDefs <- flatMap( + \ ie::NamedSignatureElement -> occursContextDeps(namedSig, top.env, ie.typerep, rhsVertexType(ie.elementName)), + namedSig.inputElements); +} + +aspect production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + {-- Used by core to send down with .frame -} + production myFlowGraph :: ProductionGraph = + constructFunctionGraph(namedSig, top.flowEnv, top.env, myProds, myFlow); +} + diff --git a/grammars/silver/compiler/definition/flow/env/NonterminalDcl.sv b/grammars/silver/compiler/definition/flow/env/NonterminalDcl.sv new file mode 100644 index 000000000..f656568f2 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/NonterminalDcl.sv @@ -0,0 +1,37 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type:syntax only BracketedOptTypeExprs; +import silver:compiler:driver:util only isStrictlyExportedBy; + +aspect production nonterminalDcl +top::AGDcl ::= quals::NTDeclQualifiers 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' +{ + -- Normally the flow analysis consider options to be the same as exports. + -- Here, to avoid creating a hard dependency on options, we ignore options when + -- deciding the include things in the *inferred* ref set. (Thus, isStrictlyExportedBy.) + local inferredInhs :: [String] = + flatMap( + filterOccursForReferences(_, top.env, isStrictlyExportedBy(_, [top.grammarName], top.compiledGrammars)), + getAttrsOn(fName, top.env)); + + local specInhs :: Maybe<[String]> = + map(fst, lookup("decorate", getFlowTypeSpecFor(fName, top.flowEnv))); + + -- Notice the circularity: refDefs uses flowEnv. Works fine because only + -- the (lazy) parameter of pair isn't computable until later. + + top.refDefs <- [(fName, fromMaybe(inferredInhs, specInhs))]; +} + +-- If it is inherited and exported by this grammar (according to authority) +function filterOccursForReferences +[String] ::= occ::OccursDclInfo e::Decorated Env authority::(Boolean ::= String) +{ + return case getAttrDcl(occ.attrOccurring, e) of + | at :: _ -> + if at.isInherited && authority(occ.sourceGrammar) + then [occ.attrOccurring] + else [] + | _ -> [] + end; +} diff --git a/grammars/silver/compiler/definition/flow/env/Occurs.sv b/grammars/silver/compiler/definition/flow/env/Occurs.sv new file mode 100644 index 000000000..c943321db --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/Occurs.sv @@ -0,0 +1,23 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +import silver:compiler:driver:util only isExportedBy; + +-- Needed for specializing inh deps in syn occurs-on contexts +attribute flowEnv occurs on Contexts, Context; + +aspect production attributionDcl +top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' +{ + local isHost :: Boolean = isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars); + local isSyn :: Boolean = at.lookupAttribute.dcl.isSynthesized; + + -- We must be able to identify host-language synthesized attributes from the flow environment + top.flowDefs <- + if !at.lookupAttribute.found || !nt.lookupType.found || !isHost || !isSyn then + [] + else + [hostSynFlowDef(nt.lookupType.fullName, at.lookupAttribute.fullName)]; +} + diff --git a/grammars/silver/compiler/definition/flow/env/ProductionBody.sv b/grammars/silver/compiler/definition/flow/env/ProductionBody.sv new file mode 100644 index 000000000..4a5faf1d3 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/ProductionBody.sv @@ -0,0 +1,209 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type only isNonterminal, typerep; +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:defaultattr; +import silver:compiler:modification:collection; +import silver:compiler:modification:copper; +import silver:compiler:driver:util only isExportedBy, RootSpec; + +attribute flowDefs, flowEnv occurs on ProductionBody, ProductionStmts, ProductionStmt, ForwardInhs, ForwardInh; +attribute flowEnv occurs on DefLHS; + +propagate flowDefs on ProductionBody, ProductionStmts, ProductionStmt, ForwardInhs, ForwardInh; + +{- A short note on how flowDefs are generated: + + - We ALWAYS produce the flowDef itself. This is necessary to catch missing or duplicate equations. + - We omit the dependencies if it appears in a location not permitted to affect the flow type. + This is to allow us to just compute flow types once, globally. +-} + +{-- + - An occurs dcl info 's flow type can be affected here + -} +function isAffectable +Boolean ::= prodgram::String ntgram::String cg::EnvTree d::OccursDclInfo +{ + return isExportedBy(prodgram, [ntgram, d.sourceGrammar], cg); +} + +aspect production forwardsTo +top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' +{ + local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); + + local mayAffectFlowType :: Boolean = + isExportedBy(top.grammarName, [ntDefGram], top.compiledGrammars); + + top.flowDefs <- [ + fwdEq(top.frame.fullName, e.flowDeps, mayAffectFlowType), + -- These are attributes that we know, here, occurs on this nonterminal. + -- The point is, these are the implicit equations we KNOW get generated, so + -- we regard these as non-suspect. That is, we implicitly insert these copy + -- equations here. + -- Currently, we don't bother to filter this to just synthesized, but we should? + implicitFwdAffects(top.frame.fullName, map((.attrOccurring), + filter(isAffectable(top.grammarName, ntDefGram, top.compiledGrammars, _), + getAttrsOn(top.frame.lhsNtName, top.env))))]; +} +aspect production forwardInh +top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' +{ + -- TODO: we need to figure out how to introduce any new lhsinh deps to the + -- forward flow type automatically. + top.flowDefs <- + case lhs of + | forwardLhsExpr(q) -> [fwdInhEq(top.frame.fullName, q.attrDcl.fullName, e.flowDeps)] + end; +} + +aspect production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); + + local srcGrams :: [String] = [ntDefGram, hackGramFromDcl(attr)]; + + local mayAffectFlowType :: Boolean = + isExportedBy(top.grammarName, srcGrams, top.compiledGrammars); + + top.flowDefs <- + if top.frame.hasPartialSignature then + [synEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps, mayAffectFlowType)] + else + [defaultSynEq(top.frame.lhsNtName, attr.attrDcl.fullName, e.flowDeps)]; +} +aspect production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.flowDefs <- + case dl of + | childDefLHS(q) -> [inhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] + | localDefLHS(q) -> [localInhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] + | forwardDefLHS(q) -> [fwdInhEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps)] + | _ -> [] -- TODO: this isn't quite extensible... more better way eventually, plz + end; +} + +aspect production localValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + -- TODO: So, I'm just going to assume for the moment that we're always allowed to define the eq for a local... + -- technically, it's possible to break this if you declare it in one grammar, but define it in another, but + -- I think we should forbid that syntactically, later on... + top.flowDefs <- + [localEq(top.frame.fullName, val.lookupValue.fullName, val.lookupValue.typeScheme.typeName, val.lookupValue.typeScheme.typerep.isNonterminal, e.flowDeps)]; + + -- If we have a type var with occurs-on contexts, add the specified syn -> inh deps for the new vertex + top.flowDefs <- occursContextDeps(top.frame.signature, top.env, val.lookupValue.typeScheme.typerep, localVertexType(val.lookupValue.fullName)); +} + +-- FROM COLLECTIONS TODO + +aspect production synAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- <- -} e::Expr +{ + local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); + + local mayAffectFlowType :: Boolean = + isExportedBy(top.grammarName, [ntDefGram, hackGramFromDcl(attr)], top.compiledGrammars); + + top.flowDefs <- + [extraEq(top.frame.fullName, lhsSynVertex(attr.attrDcl.fullName), e.flowDeps, mayAffectFlowType)]; +} + +aspect production inhAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- <- -} e::Expr +{ + local vertex :: FlowVertex = + case dl of + | childDefLHS(q) -> rhsVertex(q.lookupValue.fullName, attr.attrDcl.fullName) + | localDefLHS(q) -> localVertex(q.lookupValue.fullName, attr.attrDcl.fullName) + | forwardDefLHS(q) -> forwardVertex(attr.attrDcl.fullName) + | _ -> localEqVertex("bogus:value:from:inhcontrib:flow") + end; + top.flowDefs <- + [extraEq(top.frame.fullName, vertex, e.flowDeps, true)]; +} +aspect production synBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); + + local srcGrams :: [String] = [ntDefGram, hackGramFromDcl(attr)]; + + local mayAffectFlowType :: Boolean = + isExportedBy(top.grammarName, srcGrams, top.compiledGrammars); + + top.flowDefs <- + if top.frame.hasPartialSignature then + [synEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps, mayAffectFlowType)] + else + [defaultSynEq(top.frame.lhsNtName, attr.attrDcl.fullName, e.flowDeps)]; +} +aspect production inhBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.flowDefs <- + case dl of + | childDefLHS(q) -> [inhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] + | localDefLHS(q) -> [localInhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] + | forwardDefLHS(q) -> [fwdInhEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps)] + | _ -> [] -- TODO: this isn't quite extensible... more better way eventually, plz + end; +} + + +aspect production appendCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + local locDefGram :: String = hackGramFromQName(val.lookupValue); + -- TODO: possible bug? this would include ":local" in the gram wouldn't it? + + local mayAffectFlowType :: Boolean = + isExportedBy(top.grammarName, [locDefGram], top.compiledGrammars); + + -- TODO: So, locals that may affect flow types' suspect edges can NEVER have an effect + -- so we don't bother to even emit the extra equations in that case. + -- But, this means we might lose out on knowing there's a contribution here. + -- If we ever start using this information to locate contributions. + -- If we do, we'll have to come back here to add 'location' info anyway, + -- so if we do that, uhhh... fix this! Because you're here! Reading this! + + top.flowDefs <- + if mayAffectFlowType + then [extraEq(top.frame.fullName, localEqVertex(val.lookupValue.fullName), e.flowDeps, true)] + else []; +} + +-- TODO: Copper ProductionStmts + +-- We're in the unfortunate position of HAVING to compute values for 'flowDefs' +-- even if there are errors in the larger grammar, as remote errors in binding +-- cannot be observed to suppress the analysis of flow. + +-- It's not clear how to ideally fix this problem. What we do here is just report +-- harmless junk if we can't determine a good value. + +-- Source grammar of a nonterminal's fullName +function hackGramFromFName +String ::= s::String +{ + -- As a safety feature, rather than crash in this instance, report no known grammar + local i :: Integer = lastIndexOf(":", s); + return if i > 0 then substring(0, i, s) else ""; +} +-- Source grammar of a lookup of an attribute occurrence dcl +function hackGramFromDcl +String ::= qn::Decorated QNameAttrOccur +{ + return if qn.found then qn.dcl.sourceGrammar else ""; +} +-- Source grammar of a lookup of a local dcl +function hackGramFromQName +String ::= qn::Decorated QNameLookup +{ + return if qn.found then qn.dcl.sourceGrammar else ""; +} + diff --git a/grammars/silver/compiler/definition/flow/env/ProductionDcl.sv b/grammars/silver/compiler/definition/flow/env/ProductionDcl.sv new file mode 100644 index 000000000..5f1edaa81 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/ProductionDcl.sv @@ -0,0 +1,48 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:definition:type; +import silver:compiler:modification:defaultattr; +import silver:compiler:definition:flow:driver; +import silver:compiler:driver:util; -- only for productionFlowGraphs occurrence? + +attribute flowEnv occurs on + ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem, + AspectProductionSignature, AspectProductionLHS, AspectFunctionSignature, AspectFunctionLHS, + AspectRHS, AspectRHSElem; + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + -- TODO: bit of a hack, isn't it? + local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + {-- Used by core to send down with .frame -} + production myFlowGraph :: ProductionGraph = + findProductionGraph(fName, myGraphs); + + top.flowDefs <- + if null(body.uniqueSignificantExpression) + then [prodFlowDef(namedSig.outputElement.typerep.typeName, fName)] + else []; + + top.flowDefs <- flatMap( + \ ie::NamedSignatureElement -> occursContextDeps(namedSig, body.env, ie.typerep, rhsVertexType(ie.elementName)), + namedSig.inputElements); +} + +aspect production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + -- TODO: bit of a hack, isn't it? + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + {-- Used by core to send down with .frame -} + production myFlowGraph :: ProductionGraph = + case searchEnvTree(id.lookupValue.fullName, myGraphs) of + | g :: _ -> g + -- In case we didn't find the flow graph (the aspected production doesn't exist?) + -- build an anonymous flow graph to use instead as a "best effort". + | [] -> constructAnonymousGraph(body.flowDefs, top.env, myGraphs, myFlow) + end; +} diff --git a/grammars/silver/compiler/definition/flow/env/Root.sv b/grammars/silver/compiler/definition/flow/env/Root.sv new file mode 100644 index 000000000..0fb0512f1 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/Root.sv @@ -0,0 +1,15 @@ +grammar silver:compiler:definition:flow:env; + +attribute flowDefs, refDefs, specDefs, flowEnv occurs on Root, AGDcls, AGDcl, Grammar; +flowtype flowDefs {decorate} on Root, AGDcls, AGDcl, Grammar; +flowtype refDefs {decorate} on Root, AGDcls, AGDcl, Grammar; +flowtype specDefs {decorate} on Root, AGDcls, AGDcl, Grammar; +propagate flowDefs, refDefs, specDefs on Root, AGDcls, AGDcl, Grammar; + +aspect default production +top::AGDcl ::= +{ + top.flowDefs := []; + top.refDefs := []; + top.specDefs := []; +} diff --git a/grammars/silver/compiler/definition/flow/env/RootSpec.sv b/grammars/silver/compiler/definition/flow/env/RootSpec.sv new file mode 100644 index 000000000..711f05a85 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/env/RootSpec.sv @@ -0,0 +1,65 @@ +grammar silver:compiler:definition:flow:env; + +import silver:compiler:driver:util; + +monoid attribute hasFlowDefs::Boolean with false, ||; +monoid attribute hasRefDefs::Boolean with false, ||; +monoid attribute hasSpecDefs::Boolean with false, ||; + +attribute flowDefs, refDefs, specDefs, hasFlowDefs, hasRefDefs, hasSpecDefs occurs on InterfaceItems, InterfaceItem; +propagate flowDefs, refDefs, specDefs, hasFlowDefs, hasRefDefs, hasSpecDefs on InterfaceItems, InterfaceItem; + +aspect production consInterfaceItem +top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems +{ + top.interfaceErrors <- if !top.hasFlowDefs then ["Missing item flowDefs"] else []; + top.interfaceErrors <- if !top.hasRefDefs then ["Missing item refDefs"] else []; + top.interfaceErrors <- if !top.hasSpecDefs then ["Missing item specDefs"] else []; +} + +aspect default production +top::InterfaceItem ::= +{ + propagate flowDefs, refDefs, specDefs, hasFlowDefs, hasRefDefs, hasSpecDefs; +} + +abstract production flowDefsInterfaceItem +top::InterfaceItem ::= val::[FlowDef] +{ + -- This always changes between builds due to anon vertices named using genInt(). + -- Even if we could assign deterministic anon vertex names, changes to the flow + -- defs don't affect dependent grammars unless the flow analysis is run. + -- So we just ignore changes in flow defs here, and always rebuild dependent + -- grammars when running the flow analysis. + top.isEqual = true; + + top.flowDefs <- val; + top.hasFlowDefs <- true; +} + +abstract production refDefsInterfaceItem +top::InterfaceItem ::= val::[(String, [String])] +{ + propagate isEqual; + top.refDefs <- val; + top.hasRefDefs <- true; +} + +abstract production specDefsInterfaceItem +top::InterfaceItem ::= val::[(String, String, [String], [String])] +{ + propagate isEqual; + top.specDefs <- val; + top.hasSpecDefs <- true; +} + +aspect function packInterfaceItems +InterfaceItems ::= r::Decorated RootSpec +{ + interfaceItems <- [flowDefsInterfaceItem(r.flowDefs)]; + interfaceItems <- [refDefsInterfaceItem(r.refDefs)]; + interfaceItems <- [specDefsInterfaceItem(r.specDefs)]; +} + +attribute flowDefs, refDefs, specDefs occurs on RootSpec; +propagate flowDefs, refDefs, specDefs on RootSpec; diff --git a/grammars/silver/compiler/definition/flow/syntax/FlowSpec.sv b/grammars/silver/compiler/definition/flow/syntax/FlowSpec.sv new file mode 100644 index 000000000..41978f473 --- /dev/null +++ b/grammars/silver/compiler/definition/flow/syntax/FlowSpec.sv @@ -0,0 +1,299 @@ +grammar silver:compiler:definition:flow:syntax; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:flow:ast; +imports silver:compiler:definition:flow:driver only FlowType, inhDepsForSyn; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:driver:util only isExportedBy; +imports silver:util:treeset as set; + +-- unfortunate... +import silver:compiler:analysis:warnings only warnAll; +import silver:compiler:analysis:warnings:flow only warnMissingInh; + +terminal Flowtype 'flowtype' lexer classes {KEYWORD}; + +concrete production flowtypeDcl +top::AGDcl ::= 'flowtype' nt::QName '=' specs::FlowSpecs ';' +{ + top.unparse = "flowtype " ++ nt.unparse ++ " = " ++ specs.unparse ++ ";"; + top.errors := + if nt.lookupType.found + then specs.errors + else nt.lookupType.errors; + top.specDefs := + if nt.lookupType.found + then specs.specDefs + else []; + + specs.onNt = nt.lookupType.typeScheme.typerep; +} + +concrete production flowtypeAttrDcl +top::AGDcl ::= 'flowtype' attr::FlowSpec 'on' nts::NtList ';' +{ + top.unparse = "flowtype " ++ attr.unparse ++ " on " ++ nts.unparse ++ ";"; + top.errors := nts.errors; + top.specDefs := nts.specDefs; + + nts.flowSpecSpec = attr; +} + + +nonterminal FlowSpecs with config, location, grammarName, errors, env, unparse, onNt, specDefs, compiledGrammars, flowEnv; + +propagate errors, specDefs on FlowSpecs; + +concrete production oneFlowSpec +top::FlowSpecs ::= h::FlowSpec +{ + top.unparse = h.unparse; +} +concrete production snocFlowSpec +top::FlowSpecs ::= h::FlowSpecs ',' t::FlowSpec +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + +nonterminal FlowSpec with config, location, grammarName, errors, env, unparse, onNt, specDefs, compiledGrammars, flowEnv; + +autocopy attribute onNt :: Type; + +propagate errors on FlowSpec; + +concrete production flowSpecDcl +top::FlowSpec ::= attr::FlowSpecId '{' inhs::FlowSpecInhs '}' +{ + top.unparse = attr.unparse ++ " {" ++ inhs.unparse ++ "}"; + + top.errors <- + if !attr.found || + isExportedBy(top.grammarName, [attr.authorityGrammar], top.compiledGrammars) + then [] + else [err(attr.location, "flow type for " ++ attr.name ++ " must be exported by " ++ attr.authorityGrammar)]; + + top.errors <- + if attr.found && + length(filter(eq(attr.synName, _), getSpecifiedSynsForNt(top.onNt.typeName, top.flowEnv))) > 1 + then [err(attr.location, "duplicate specification of flow type for " ++ attr.name ++ " on " ++ top.onNt.typeName)] + else []; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local missingFt :: [String] = + set:toList(set:removeAll(inhs.inhList, inhDepsForSyn("forward", top.onNt.typeName, myFlow))); + + top.errors <- + if !attr.found || + !top.config.warnMissingInh || -- we don't want to compute flow graphs unless told to + isExportedBy(attr.authorityGrammar, [hackGramFromFName(top.onNt.typeName)], top.compiledGrammars) || + null(missingFt) + then [] + else [err(attr.location, attr.name ++ " is an extension synthesized attribute, and must contain at least the forward flow type. It is missing " ++ implode(", ", missingFt))]; + + top.errors <- + if attr.found && contains(attr.synName, inhs.refList) + then [err(top.location, s"circularity in flow specification for ${attr.name} on ${top.onNt.typeName}")] + else []; + + -- We want to put the spec in even if there are errors in 'inhs' so that + -- we can look up specs from inhs. + top.specDefs := + if !attr.found then [] + else [(top.onNt.typeName, attr.synName, if contains(attr.synName, inhs.refList) then [] else inhs.inhList, inhs.refList)]; +} + +nonterminal FlowSpecId with config, location, grammarName, errors, env, unparse, onNt, synName, authorityGrammar, found, name; + +synthesized attribute synName :: String; +synthesized attribute authorityGrammar :: String; + +propagate errors on FlowSpecId; + +concrete production qnameSpecId +top::FlowSpecId ::= syn::QNameAttrOccur +{ + top.name = syn.name; + top.unparse = syn.unparse; + top.synName = syn.attrDcl.fullName; + top.authorityGrammar = syn.dcl.sourceGrammar; + top.found = syn.found && syn.attrDcl.isSynthesized; + + syn.attrFor = top.onNt; + + top.errors <- + if !syn.found || syn.attrDcl.isSynthesized then [] + else [err(syn.location, syn.name ++ " is not a synthesized attribute, and so cannot have a flow type")]; +} + +concrete production forwardSpecId +top::FlowSpecId ::= 'forward' +{ + top.name = "forward"; + top.unparse = top.name; + top.synName = "forward"; + top.authorityGrammar = hackGramFromFName(top.onNt.typeName); + top.found = true; +} + +concrete production decorateSpecId +top::FlowSpecId ::= 'decorate' +{ + top.name = "decorate"; + top.unparse = top.name; + top.synName = "decorate"; + top.authorityGrammar = hackGramFromFName(top.onNt.typeName); + top.found = true; +} + + +nonterminal FlowSpecInhs with config, location, grammarName, errors, env, unparse, onNt, inhList, refList, flowEnv; + +monoid attribute inhList :: [String]; -- The attributes in the flow specification +monoid attribute refList :: [String]; -- Flow specifications referenced in this one (currently can only contain "decorate" / "forward") + +propagate errors, inhList, refList on FlowSpecInhs; + +concrete production nilFlowSpecInhs +top::FlowSpecInhs ::= +{ + top.unparse = ""; +} +concrete production oneFlowSpecInhs +top::FlowSpecInhs ::= h::FlowSpecInh +{ + top.unparse = h.unparse; +} +concrete production consFlowSpecInhs +top::FlowSpecInhs ::= h::FlowSpecInh ',' t::FlowSpecInhs +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + +nonterminal FlowSpecInh with config, location, grammarName, errors, env, unparse, onNt, inhList, refList, flowEnv; + +flowtype FlowSpecInh = forward {grammarName, env, flowEnv, onNt}, inhList {forward}; + +propagate errors on FlowSpecInh; + +concrete production flowSpecInh +top::FlowSpecInh ::= inh::QNameAttrOccur +{ + top.unparse = inh.unparse; + top.inhList := if inh.attrFound then [inh.attrDcl.fullName] else []; + top.refList := []; + + inh.attrFor = top.onNt; + + top.errors <- + if !inh.found || inh.attrDcl.isInherited then [] + else [err(inh.location, inh.name ++ " is not an inherited attribute and so cannot be within a flow type")]; +} + +{-- + - Inherit a flow spec from another flow spec. + - + - 1. This is exclusively for other things given explicit specifications: + - (a) by design: we want things documented in the code. + - (b) because it dramatically simplifies the implementation. + - We can do everything here, and not have to worry about having + - to somehow make this work in the inference process. (Which would + - be kinda tricky: probably we'd need to remove this attribute + - from the normal 'infer' process EXCEPT on the phantom production, + - where we'd stash the info given here as edges...) + - 2. We only support 'decorate' and 'forward' here, not syns. + - It's the only version demanded so far, let's wait until there's + - motivation to consider that extension. + -} +concrete production flowSpecDec +top::FlowSpecInh ::= 'decorate' +{ + top.unparse = "decorate"; + + local specs :: [(String, [String], [String])] = getFlowTypeSpecFor(top.onNt.typeName, top.flowEnv); + local decSpec :: Maybe<([String], [String])> = lookup("decorate", specs); + + -- This error message also shows up for Decorated Foo when Foo lacks a spec for 'decorate', + -- so be sufficiently general here. + top.errors <- + case top.onNt, decSpec of + | nonterminalType(_, _, _), just(_) -> [] + | nonterminalType(_, _, _), nothing() -> + [err(top.location, s"to use the default reference set for nonterminal ${top.onNt.typeName}, 'decorate' must also have an explicit flow type")] + | errorType(), _ -> [] + | _, _ -> [err(top.location, s"default reference set can only be used with nonterminal types, not ${prettyType(top.onNt)}")] + end; + + top.inhList := fromMaybe(([], []), decSpec).fst; + top.refList := "decorate" :: fromMaybe(([], []), decSpec).snd; +} + +concrete production flowSpecForward +top::FlowSpecInh ::= 'forward' +{ + top.unparse = "forward"; + + local specs :: [(String, [String], [String])] = getFlowTypeSpecFor(top.onNt.typeName, top.flowEnv); + local forwardSpec :: Maybe<([String], [String])> = lookup("forward", specs); + + top.errors <- + case forwardSpec of + | just(_) -> [] + | nothing() -> + [err(top.location, s"to use the forward set for nonterminal ${top.onNt.typeName} in a flow type, 'forward' must also have an explicit flow type")] + end; + + top.inhList := fromMaybe(([], []), forwardSpec).fst; + top.refList := "forward" :: fromMaybe(([], []), forwardSpec).snd; +} + + +nonterminal NtList with config, location, grammarName, errors, env, unparse, flowSpecSpec, specDefs, compiledGrammars, flowEnv; + +propagate errors, specDefs on NtList; + +concrete production nilNtList +top::NtList ::= +{ + top.unparse = ""; +} +concrete production oneNtList +top::NtList ::= h::NtName +{ + top.unparse = h.unparse; +} +concrete production consNtList +top::NtList ::= h::NtName ',' t::NtList +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; +} + +nonterminal NtName with config, location, grammarName, errors, env, unparse, flowSpecSpec, specDefs, compiledGrammars, flowEnv; + +autocopy attribute flowSpecSpec :: FlowSpec; + +concrete production ntName +top::NtName ::= nt::QName +{ + top.unparse = nt.unparse; + top.errors := + if nt.lookupType.found + then myCopy.errors + else nt.lookupType.errors; + + top.specDefs := + if nt.lookupType.found + then myCopy.specDefs + else []; + + local myCopy :: FlowSpec = top.flowSpecSpec; + myCopy.config = top.config; + myCopy.grammarName = top.grammarName; + myCopy.env = top.env; + myCopy.compiledGrammars = top.compiledGrammars; + myCopy.flowEnv = top.flowEnv; + + myCopy.onNt = nt.lookupType.typeScheme.typerep; +} + diff --git a/grammars/silver/compiler/definition/origins/BlockContext.sv b/grammars/silver/compiler/definition/origins/BlockContext.sv new file mode 100644 index 000000000..948781124 --- /dev/null +++ b/grammars/silver/compiler/definition/origins/BlockContext.sv @@ -0,0 +1,56 @@ +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:flow:driver; + +{-- + - Does code generated in this block need to reference originsCtx, or can it + - get information from the frame's left-hand-side and rules? Effectively, is + - this code in a function or a production? + -} +synthesized attribute originsContextSource :: ContextOriginInfoSource occurs on BlockContext; + +nonterminal ContextOriginInfoSource; + +abstract production useContextLhsAndRules +top::ContextOriginInfoSource ::= +{ + +} + +abstract production useRuntimePassedInfo +top::ContextOriginInfoSource ::= +{ + +} + +abstract production useBogusInfo +top::ContextOriginInfoSource ::= name::String +{ + +} + +aspect production functionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.originsContextSource = useRuntimePassedInfo(); +} + +aspect production inLambdaContext +top::BlockContext ::= containingContext::BlockContext +{ + top.originsContextSource = useRuntimePassedInfo(); + -- Code in lambda blocks needs to respect the OI it is passed, or notes &c won't flow into + -- lambdas in the same way that they flow into functions. +} + +aspect production productionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.originsContextSource = useContextLhsAndRules(); +} + +aspect production globalExprContext +top::BlockContext ::= _ _ _ _ +{ + top.originsContextSource = useBogusInfo("GLOBAL_CONTEXT"); --This is a java implementation detail but... +} \ No newline at end of file diff --git a/grammars/silver/compiler/definition/type/Helpers.sv b/grammars/silver/compiler/definition/type/Helpers.sv new file mode 100644 index 000000000..8f9e3f668 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Helpers.sv @@ -0,0 +1,19 @@ +grammar silver:compiler:definition:type; + +global appTypes::(Type ::= Type [Type]) = foldl(appType, _, _); + +-- HUGE LIE: This is not a set. Well, maybe. We *might* depend on this being ordered. +function setUnionTyVars +[TyVar] ::= a::[TyVar] b::[TyVar] +{ + return a ++ removeAll(a, b); +} +function setUnionTyVarsAll +[TyVar] ::= tvs::[[TyVar]] +{ + return if null(tvs) + then [] + else if null(tail(tvs)) + then head(tvs) + else setUnionTyVars( head(tvs), setUnionTyVarsAll(tail(tvs))); +} diff --git a/grammars/silver/compiler/definition/type/Kind.sv b/grammars/silver/compiler/definition/type/Kind.sv new file mode 100644 index 000000000..33640f389 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Kind.sv @@ -0,0 +1,32 @@ +grammar silver:compiler:definition:type; + +synthesized attribute baseKind::Kind; +synthesized attribute argKinds::[Kind]; + +nonterminal Kind with compareTo, isEqual, baseKind, argKinds; +propagate compareTo, isEqual on Kind; + +aspect default production +top::Kind ::= +{ + top.baseKind = top; + top.argKinds = []; +} + +abstract production starKind +top::Kind ::= +{} + +abstract production inhSetKind +top::Kind ::= +{} + +abstract production arrowKind +top::Kind ::= k1::Kind k2::Kind +{ + top.baseKind = k2.baseKind; + top.argKinds = k1 :: k2.argKinds; +} + +global constructorKind::(Kind ::= Integer) = \ arity::Integer -> + foldr(arrowKind, starKind(), repeat(starKind(), arity)); diff --git a/grammars/silver/compiler/definition/type/PrettyPrinting.sv b/grammars/silver/compiler/definition/type/PrettyPrinting.sv new file mode 100644 index 000000000..3bb80b1ee --- /dev/null +++ b/grammars/silver/compiler/definition/type/PrettyPrinting.sv @@ -0,0 +1,283 @@ +grammar silver:compiler:definition:type; + +import silver:langutil:pp; + +synthesized attribute typepp :: String occurs on PolyType, Context, Type, Kind; +autocopy attribute boundVariables :: [TyVar] occurs on Context, Type; + +function prettyType +String ::= te::Type +{ + te.boundVariables = te.freeVariables; + return te.typepp; +} + +function prettyTypeWith +String ::= te::Type tvs::[TyVar] +{ + te.boundVariables = tvs; + return te.typepp; +} + +function prettyContext +String ::= c::Context +{ + c.boundVariables = c.freeVariables; + return c.typepp; +} + +function prettyContextWith +String ::= c::Context tvs::[TyVar] +{ + c.boundVariables = tvs; + return c.typepp; +} + +function prettyKind +String ::= k::Kind +{ + return k.typepp; +} + +instance Show Type { + pp = compose(text, prettyType); +} + +instance Show Kind { + pp = compose(text, prettyKind); +} + +-------------------------------------------------------------------------------- + +aspect production monoType +top::PolyType ::= ty::Type +{ + top.typepp = ty.typepp; + ty.boundVariables = []; +} + +aspect production polyType +top::PolyType ::= tvs::[TyVar] ty::Type +{ + top.typepp = ty.typepp; + ty.boundVariables = tvs; +} + +aspect production constraintType +top::PolyType ::= tvs::[TyVar] contexts::[Context] ty::Type +{ + top.typepp = implode(", ", map(prettyContextWith(_, tvs), contexts)) ++ " => " ++ ty.typepp; + ty.boundVariables = tvs; +} + +aspect production instContext +top::Context ::= cls::String t::Type +{ + top.typepp = cls ++ " " ++ t.typepp; +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.typepp = s"attribute ${attr}${if null(args) then "" else s"<${implode(" ", map(prettyTypeWith(_, top.boundVariables), args))}>"} occurs on ${ntty.typepp}"; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.typepp = s"attribute ${attr}${if null(args) then "" else s"<${implode(" ", map(prettyTypeWith(_, top.boundVariables), args))}>"} ${inhs.typepp} occurs on ${ntty.typepp}"; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.typepp = s"annotation ${attr}${if null(args) then "" else s"<${implode(" ", map(prettyTypeWith(_, top.boundVariables), args))}>"} occurs on ${ntty.typepp}"; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + top.typepp = "runtimeTypeable " ++ t.typepp; +} + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.typepp = i1.typepp ++ " subset " ++ i2.typepp; +} + +aspect production typeErrorContext +top::Context ::= msg::String +{ + top.typepp = "typeError \"" ++ escapeString(msg) ++ "\""; +} + +aspect production varType +top::Type ::= tv::TyVar +{ + top.typepp = findAbbrevFor(tv, top.boundVariables); +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.typepp = findAbbrevFor(tv, top.boundVariables); +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.typepp = + case c.baseType of + | functionType(params, namedParams) -> "(" ++ + (if length(top.argTypes) > params + length(namedParams) + then prettyTypeWith(head(drop(params + length(namedParams), top.argTypes)), top.boundVariables) + else "_") ++ " ::= " ++ + implode(" ", map(prettyTypeWith(_, top.boundVariables), take(params, top.argTypes))) ++ + (if length(top.argTypes) < params then replicate(params - length(top.argTypes), " _") else "") ++ + concat( + zipWith(\ np::String t::Type -> s"; ${np}::${prettyTypeWith(t, top.boundVariables)}", namedParams, drop(params, top.argTypes)) ++ + map(\ np::String -> s"; ${np}::_", drop(length(top.argTypes) - (params + length(namedParams)), namedParams))) ++ ")" ++ + if length(top.argTypes) <= params + length(namedParams) + 1 then "" + else "<" ++ implode(" ", map(prettyTypeWith(_, top.boundVariables), drop(params + length(namedParams) + 1, top.argTypes))) ++ ">" + | _ -> prettyTypeWith(top.baseType, top.boundVariables) ++ + if null(top.argTypes) then "" + else "<" ++ implode(" ", map(prettyTypeWith(_, top.boundVariables), top.argTypes)) ++ + replicate(max(length(top.baseType.kindrep.argKinds) - length(top.argTypes), 0), " _") ++ ">" + end; +} + +aspect production errorType +top::Type ::= +{ + top.typepp = ""; -- probably shouldn't ever get printed? +} + +aspect production intType +top::Type ::= +{ + top.typepp = "Integer"; +} + +aspect production boolType +top::Type ::= +{ + top.typepp = "Boolean"; +} + +aspect production floatType +top::Type ::= +{ + top.typepp = "Float"; +} + +aspect production stringType +top::Type ::= +{ + top.typepp = "String"; +} + +aspect production terminalIdType +top::Type ::= +{ + top.typepp = "TerminalId"; +} + +aspect production nonterminalType +top::Type ::= fn::String _ _ +{ + top.typepp = fn; +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.typepp = fn; +} + +aspect production inhSetType +top::Type ::= inhs::[String] +{ + -- Elide the grammar name when it is repeated + -- e.g. {silver:compiler:definition:env:env, :config, :isRoot} + top.typepp = + s"{${implode(", ", + flatMap( + \ is::[String] -> head(is) :: map(\ i::String -> ":" ++ last(explode(":", i)), tail(is)), + groupBy(\ i1::String i2::String -> init(explode(":", i1)) == init(explode(":", i2)), inhs)))}}"; +} + +aspect production decoratedType +top::Type ::= t::Type i::Type +{ + top.typepp = s"Decorated ${t.typepp} with ${i.typepp}"; +} + +aspect production partiallyDecoratedType +top::Type ::= t::Type i::Type +{ + top.typepp = s"PartiallyDecorated ${t.typepp} with ${i.typepp}"; +} + +aspect production ntOrDecType +top::Type ::= nt::Type inhs::Type hidden::Type +{ +-- Sometimes useful for debugging. +-- top.typepp = "Undecorable " ++ nt.typepp ++ " with " ++ inhs.typepp ++ " specialized " ++ prettyTypeWith(hidden, []); +} + +aspect production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.typepp = s"(_ ::=${replicate(params, " _") }${if null(namedParams) then "" else "; " ++ implode("::_; ", namedParams) ++ "::_"})"; +} + +aspect production starKind +top::Kind ::= +{ + top.typepp = "*"; +} + +aspect production inhSetKind +top::Kind ::= +{ + top.typepp = "InhSet"; +} + +aspect production arrowKind +top::Kind ::= k1::Kind k2::Kind +{ + top.typepp = + case k1 of + | arrowKind(_, _) -> s"(${k1.typepp}) -> ${k2.typepp}" + | _ -> s"${k1.typepp} -> ${k2.typepp}" + end; +} + +-------------------------------------------------------------------------------- +function findAbbrevFor +String ::= tv::TyVar bv::[TyVar] +{ + local named::[TyVar] = filter(\ tv::TyVar -> case tv of tyVarNamed(_, _, _) -> true | _ -> false end, bv); + local anon::[TyVar] = filter(\ tv::TyVar -> case tv of tyVarNamed(_, _, _) -> false | _ -> true end, bv); + return findAbbrevHelp(tv, named ++ anon, ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"], []); +} + +function findAbbrevHelp +String ::= tv::TyVar bv::[TyVar] vn::[String] assigned::[String] +{ + return + case bv, vn of + | tyVarNamed(_, _, n) :: tbv, _ when !contains(n, assigned) -> + if tv == head(bv) then n else findAbbrevHelp(tv, tbv, vn, n :: assigned) + | hbv :: tbv, hvn :: tvn -> + if contains(hvn, assigned) + then findAbbrevHelp(tv, bv, tvn, assigned) + else if tv == hbv then hvn else findAbbrevHelp(tv, tbv, tvn, hvn :: assigned) + | _, _ -> + case positionOf(tv, bv) of + | i when i > 0 && !contains("a" ++ toString(i), assigned) -> "a" ++ toString(i) + | _ -> "V_" ++ toString(tv.extractTyVarRep) + end + end; +} diff --git a/grammars/silver/compiler/definition/type/Substitutions.sv b/grammars/silver/compiler/definition/type/Substitutions.sv new file mode 100644 index 000000000..de63cf403 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Substitutions.sv @@ -0,0 +1,288 @@ +grammar silver:compiler:definition:type; + +{-- + - A representation of: (1) tyvar->type substitutions. (2) success/failure of unification + - + - History note: This code was written before Silver had polymorphism (it is + - what gave Silver polymorphism!) and so is incredibly archaic. + - + - TODO: Separate success/failure of unification from this type. + - Consider unify returning Maybe or Pair depending. + - TODO: More efficient type representations than a assoc-list, somehow. + -} +nonterminal Substitution with substList, substErrors, failure; + +synthesized attribute substList :: [Pair]; +synthesized attribute substErrors :: [String]; +synthesized attribute failure :: Boolean; -- this is a bad hack to work around unify being unable to return a pair + +-------------------------------------------------------------------------------- + +abstract production goodSubst +top::Substitution ::= sublst::[Pair] +{ + top.substList = sublst; + top.substErrors = []; + top.failure = false; +} + +abstract production badSubst +top::Substitution ::= sublst::[Pair] errs::[String] +{ + top.substList = sublst; + top.substErrors = errs; + top.failure = true; +} + +function emptySubst +Substitution ::= +{ + return goodSubst([]); +} +function errorSubst +Substitution ::= e::String +{ + return badSubst([], [e]); +} +function subst +Substitution ::= tv::TyVar te::Type +{ + return goodSubst([pair(tv,te)]); +} +function composeSubst +Substitution ::= s1::Substitution s2::Substitution +{ + return case s1, s2 of + | goodSubst(s1l), goodSubst(s2l) -> goodSubst(s1l ++ s2l) + | goodSubst(s1l), badSubst(s2l, s2e) -> badSubst(s1l ++ s2l, s2e) + | badSubst(s1l, s1e), goodSubst(s2l) -> badSubst(s1l ++ s2l, s1e) + | badSubst(s1l, s1e), badSubst(s2l, s2e) -> badSubst(s1l ++ s2l, s1e ++ s2e) + end; +} + +function ignoreFailure +Substitution ::= s::Substitution +{ + return case s of + | goodSubst(_) -> s + | badSubst(sl,_) -> goodSubst(sl) + end; +} + +-------------------------------------------------------------------------------- + +function findSubst +Maybe ::= tv::TyVar s::Substitution +{ + return lookup(tv, s.substList); +} + +-------------------------------------------------------------------------------- + +-- These are for ordinary tyvar substitutions. +autocopy attribute substitution :: Substitution occurs on Context, Type; +functor attribute substituted occurs on Context, Type; +-- These are for flat, non-recursive replacement of tyvars with something else directly +functor attribute flatRenamed occurs on Context, Type; + +propagate substituted, flatRenamed on Context, Type + excluding inhOccursContext, synOccursContext, annoOccursContext, varType, skolemType, ntOrDecType; + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.substituted = inhOccursContext(attr, map(performSubstitution(_, top.substitution), args), atty.substituted, ntty.substituted); + top.flatRenamed = inhOccursContext(attr, map(performRenaming(_, top.substitution), args), atty.flatRenamed, ntty.flatRenamed); +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.substituted = synOccursContext(attr, map(performSubstitution(_, top.substitution), args), atty.substituted, inhs.substituted, ntty.substituted); + top.flatRenamed = synOccursContext(attr, map(performRenaming(_, top.substitution), args), atty.flatRenamed, inhs.flatRenamed, ntty.flatRenamed); +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.substituted = annoOccursContext(attr, map(performSubstitution(_, top.substitution), args), atty.substituted, ntty.substituted); + top.flatRenamed = annoOccursContext(attr, map(performRenaming(_, top.substitution), args), atty.flatRenamed, ntty.flatRenamed); +} + +aspect production varType +top::Type ::= tv::TyVar +{ + -- Important: we recursively substitute, until no more substitutions happen! + -- This also means the substitution list must not be circular! + + -- Perform one iteration of substitution + local partialsubst :: Maybe = + case findSubst(tv, top.substitution) of + | just(s) when s.kindrep != tv.kindrep -> error("Kind mismatch in applying substitution!") + | ps -> ps + end; + + -- recursively substitute only if we changed! + top.substituted = + if partialsubst.isJust + then performSubstitution(partialsubst.fromJust, top.substitution) + else top; + top.flatRenamed = + if partialsubst.isJust + then partialsubst.fromJust + else top; +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + -- This may be counter intuitive! I don't know! + + -- I'm allowing Skolem constants to be subtituted for. + -- Now, the "real" behavior of Skolem constants is all in unification: + -- there, they behave as you would expect. However, once we quantify over the + -- "Skolem constant type variables", they should sort of go back to behaving + -- like ordinary type variables. So to get this behavior, we allow them to be + -- substituted. + + -- The only way we can construct a substitution for one though is by non-unification + -- means. (And there's only one way to do that: by quantifying over it.) + + -- (See the only non-unification place where subst(...) is called directly at the bottom of this file.) + + local partialsubst :: Maybe = + case findSubst(tv, top.substitution) of + | just(s) when s.kindrep != tv.kindrep -> nothing() + | ps -> ps + end; + + -- recursively substitute only if we changed! + top.substituted = + if partialsubst.isJust + then performSubstitution(partialsubst.fromJust, top.substitution) + else top; + top.flatRenamed = + if partialsubst.isJust + then partialsubst.fromJust + else top; +} + +aspect production ntOrDecType +top::Type ::= nt::Type inhs::Type hidden::Type +{ + -- We rely very carefully on eliminating ourselves once we've specialized! + -- Note: we're matching on hidden.subsituted, not just hidden. Important! + top.substituted = + case hidden.substituted of + | varType(_) -> ntOrDecType(nt.substituted, inhs.substituted, hidden.substituted) + | _ -> hidden.substituted + end; + -- For a renaming, we don't need to specialize. + propagate flatRenamed; +} + +-------------------------------------------------------------------------------- + +function performContextSubstitution +Context ::= c::Context s::Substitution +{ + c.substitution = s; + return c.substituted; +} + +function performSubstitution +Type ::= te::Type s::Substitution +{ + te.substitution = s; + return te.substituted; +} + +function mapSubst +[Type] ::= tes::[Type] s::Substitution +{ + return map(performSubstitution(_, s), tes); +} + +---- + +function performContextRenaming +Context ::= c::Context s::Substitution +{ + c.substitution = s; + return c.flatRenamed; +} + +function performRenaming +Type ::= te::Type s::Substitution +{ + te.substitution = s; + return te.flatRenamed; +} + +function mapRenameSubst +[Type] ::= tes::[Type] s::Substitution +{ + return map(performRenaming(_, s), tes); +} + +-------------------------------------------------------------------------------- + +-- Generate fresh type vars with the same kinds as tvs +function freshTyVars +[TyVar] ::= tvs::[TyVar] +{ + return map(freshTyVar, map((.kindrep), tvs)); +} + +function zipVarsIntoSubstitution +Substitution ::= original::[TyVar] sub::[TyVar] +{ + -- once we have "productions are subtypes of functions" then make this just map 'varType' and call the other one below + return if null(original) || null(sub) then emptySubst() + else composeSubst(subst(head(original), varType(head(sub))), + zipVarsIntoSubstitution(tail(original), tail(sub))); +} + +function zipVarsIntoSkolemizedSubstitution +Substitution ::= original::[TyVar] sub::[TyVar] +{ + -- once we have "productions are subtypes of functions" then make this just map 'varType' and call the other one below + return if null(original) || null(sub) then emptySubst() + else composeSubst(subst(head(original), skolemType(head(sub))), + zipVarsIntoSkolemizedSubstitution(tail(original), tail(sub))); +} + + +function zipVarsAndTypesIntoSubstitution +Substitution ::= original::[TyVar] sub::[Type] +{ + return if null(original) || null(sub) then emptySubst() + else composeSubst(subst(head(original), head(sub)), + zipVarsAndTypesIntoSubstitution(tail(original), tail(sub))); +} + +function freshenType +Type ::= te::Type tvs::[TyVar] +{ + return freshenTypeWith(te, tvs, freshTyVars(tvs)); +} + +function freshenContextWith +Context ::= c::Context tvs::[TyVar] ntvs::[TyVar] +{ + -- Freshening just straight replaces variables, not deeply substituting them. + return performContextRenaming(c, zipVarsIntoSubstitution(tvs, ntvs)); +} + +function freshenTypeWith +Type ::= te::Type tvs::[TyVar] ntvs::[TyVar] +{ + -- Freshening just straight replaces variables, not deeply substituting them. + return performRenaming(te, zipVarsIntoSubstitution(tvs, ntvs)); +} + +function errorSubstitution +Substitution ::= t::Type +{ + return zipVarsAndTypesIntoSubstitution(t.freeVariables, repeat(errorType(), length(t.freeVariables))); +} diff --git a/grammars/silver/compiler/definition/type/Type.sv b/grammars/silver/compiler/definition/type/Type.sv new file mode 100644 index 000000000..4b8b50580 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Type.sv @@ -0,0 +1,395 @@ +grammar silver:compiler:definition:type; + +option silver:compiler:modification:ffi; -- foreign types +option silver:compiler:modification:list; -- list type + +synthesized attribute kindrep :: Kind; +synthesized attribute freeVariables :: [TyVar]; +synthesized attribute boundVars :: [TyVar]; +synthesized attribute contexts :: [Context]; +synthesized attribute typerep :: Type; +synthesized attribute monoType :: Type; -- Raises on error when we encounter a polyType and didn't expect one + +{-- + - Represents a type, quantified over some type variables. + -} +nonterminal PolyType with boundVars, contexts, typerep, monoType; + +flowtype PolyType = decorate {}, forward {}; + +abstract production monoType +top::PolyType ::= ty::Type +{ + top.boundVars = []; + top.contexts = []; + top.typerep = ty; + top.monoType = ty; +} + +abstract production polyType +top::PolyType ::= bound::[TyVar] ty::Type +{ + top.boundVars = freshTyVars(bound); + top.contexts = []; + top.typerep = freshenTypeWith(ty, bound, top.boundVars); + top.monoType = error("Expected a mono type but found a poly type!"); +} + +abstract production constraintType +top::PolyType ::= bound::[TyVar] contexts::[Context] ty::Type +{ + top.boundVars = freshTyVars(bound); + top.contexts = map(freshenContextWith(_, bound, top.boundVars), contexts); + top.typerep = freshenTypeWith(ty, bound, top.boundVars); + top.monoType = error("Expected a mono type but found a (constraint) poly type!"); +} + +{-- + - Represents a constraint on a type, e.g. a type class instance + -} + +nonterminal Context with freeVariables; + +abstract production instContext +top::Context ::= cls::String t::Type +{ + top.freeVariables = t.freeVariables; +} + +abstract production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.freeVariables = setUnionTyVarsAll(map((.freeVariables), args ++ [ntty])); +} + +abstract production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.freeVariables = setUnionTyVarsAll(map((.freeVariables), args ++ [inhs, ntty])); +} + +abstract production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.freeVariables = setUnionTyVarsAll(map((.freeVariables), args ++ [ntty])); +} + +abstract production typeableContext +top::Context ::= t::Type +{ + top.freeVariables = t.freeVariables; +} + +abstract production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.freeVariables = setUnionTyVars(i1.freeVariables, i2.freeVariables); +} + +abstract production typeErrorContext +top::Context ::= msg::String +{ + top.freeVariables = []; +} + +{-- + - Silver Type Representations. + -} +nonterminal Type with kindrep, freeVariables, tracked; +synthesized attribute tracked :: Boolean; + +flowtype Type = decorate {}, forward {}; + +aspect default production +top::Type ::= +{ + top.tracked = false; +} + +{-- + - This is a (universally quantified) type variable. + -} +abstract production varType +top::Type ::= tv::TyVar +{ + top.kindrep = tv.kindrep; + top.freeVariables = [tv]; +} + +{-- + - This is an (existentially quantified) type variable, i.e. skolem constant. + - Type are pretty much (exists sks. forall tys. type) + -} +abstract production skolemType +top::Type ::= tv::TyVar +{ + top.kindrep = tv.kindrep; + top.freeVariables = [tv]; +} + +{-- + - Represents the application of a constructor type. + -} +abstract production appType +top::Type ::= c::Type a::Type +{ + top.kindrep = + case c.kindrep of + | arrowKind(_, k) -> k + | _ -> starKind() + end; + top.freeVariables = setUnionTyVars(c.freeVariables, a.freeVariables); + top.tracked = c.tracked; +} + +{-- + - When an error message has **already** been reported, and we must supply a type, + - and we wish to suppress further error messages, use errorType. + -} +abstract production errorType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - Integer type. + -} +abstract production intType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - Boolean type. + -} +abstract production boolType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - Float type. + -} +abstract production floatType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - String type. + -} +abstract production stringType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - Terminal identifier type. + - This isn't a foreign type, since we want equality checking. + - TODO: Revisit this once we have type classes. + -} +abstract production terminalIdType +top::Type ::= +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + +{-- + - An (undecorated) nonterminal type. + - Note that this is the *unapplied* type constructor for a nonterminal type; + - e.g. `Pair` would be represented as + - `apType(apType(nonterminalType("silver:core:Pair", [starKind(), starKind()], false), stringType()), integerType())`. + - + - @param fn The fully qualified name of the nonterminal. + - @param k The number type parameters for that nonterminal. + - @param tracked Might this NT be tracked. + -} +abstract production nonterminalType +top::Type ::= fn::String ks::[Kind] tracked::Boolean +{ + top.kindrep = foldr(arrowKind, starKind(), ks); + top.freeVariables = []; + top.tracked = tracked; +} + +{-- + - A terminal type. + - @param fn The fully qualified name of the terminal. + -} +abstract production terminalType +top::Type ::= fn::String +{ + top.kindrep = starKind(); + top.freeVariables = []; +} + + +{-- + - A type-level inherited attribute set. + - @param inhs The (sorted) list of fully-qualified inherited attribute names. + -} +abstract production inhSetType +top::Type ::= inhs::[String] +{ + top.kindrep = inhSetKind(); + top.freeVariables = []; +} + +{-- + - A *decorated* nonterminal type. + - Represents a reference with at least some set of provided inherited attributes, + - cannot be decorated with additional attributes. + - @param te MUST be a 'nonterminalType' or 'varType'/'skolemType' + - @param i MUST have kind InhSet + -} +abstract production decoratedType +top::Type ::= te::Type i::Type +{ + top.kindrep = starKind(); + top.freeVariables = setUnionTyVars(te.freeVariables, i.freeVariables); +} + +{-- + - A *partially decorated* nonterminal type. + - Represents a reference with some exact set of provided inherited attributes, + - may be decorated with additional attributes. + - @param te MUST be a 'nonterminalType' or 'varType'/'skolemType' + - @param i MUST have kind InhSet + -} +abstract production partiallyDecoratedType +top::Type ::= te::Type i::Type +{ + top.kindrep = starKind(); + top.freeVariables = setUnionTyVars(te.freeVariables, i.freeVariables); +} + +{-- + - An intermediate type. This *should* never appear as the type of a symbol, + - etc. Rather, this is a helper type only used within expressions. + - + - It represents a nonterminal that is *either* decorated or undecorated + - (e.g. when referencing a child) but has not yet been specialized. + - + - This is annoyingly complicated because there are some cases in which it is + - fine for the type to be decorated with any set of inherited attributes + - (e.g. taking references to children, locals) and some where we only want to + - permit a specific set of attributes if the type does get specialized to decorated + - (references to variables bound in let expressions/pattern matching.) + - This is what 'inhs' tracks. + - + - Seperately, we also want to control the default behavior for when we never + - specialize - whether we are partially or totally decorated reference + - (determined by 'defaultPartialDec') and what set of attributes we should have + - (determined by 'defaultInhs'.) These are not affected by unification, but we + - must not specialize to 'defaultInhs' if 'inhs' ultimately unifies with + - something incompatible. + - + - @param nt MUST be a 'nonterminalType' + - @param inhs The inh set that we're decorated with, or a free var if we don't care - MUST have kind InhSet + - @param hidden One of: (a) a type variable (b) 'nt' (c) 'decoratedType(nt, inhs)' (d) 'partiallyDecoratedType(nt, inhs)' + - representing state: unspecialized, undecorated, or decorated. + - @param defaultPartialDec The default for what we are if we never specialize. + - @param inhs The default for what we're decorated with if we never specialize - MUST have kind InhSet + -} + +-- This will ONLY appear in the types of expressions, nowhere else! +abstract production ntOrDecType +top::Type ::= nt::Type inhs::Type hidden::Type +{ + -- Note that we are excluding hidden here if it is unspecialized + top.freeVariables = + case hidden of + | varType(_) -> setUnionTyVars(nt.freeVariables, inhs.freeVariables) + | _ -> hidden.freeVariables + end; + + -- If we never specialize what we're decorated with, we're decorated with nothing. + production actualInhs::Type = + case inhs of + | varType(_) -> inhSetType([]) + | _ -> inhs + end; + + -- If we never specialize, we're decorated. + forwards to decoratedType(nt, actualInhs); +} + +{-- + - Function type. (Whether production or function.) + - Note that this is the *unapplied* type constructor for a nonterminal type, + - and argument types are provided before the result type; + - e.g. `(Integer ::= String Boolean)` would be represented as + - `apType(apType(apType(functionType(3, []), stringType()), booleanType()), integerType())`. + - + - @param params The number input types of the function + - @param namedParams The names of named parameters for this function. + - NOTE: These must always be *IN SORTED ORDER* + -} +abstract production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.kindrep = constructorKind(params + length(namedParams) + 1); + top.freeVariables = []; +} + +-------------------------------------------------------------------------------- + +nonterminal TyVar with kindrep, compareTo, isEqual; +propagate compareTo, isEqual on TyVar; + +-- In essence, this should be 'private' to this file. +synthesized attribute extractTyVarRep :: Integer occurs on TyVar; + +abstract production tyVar +top::TyVar ::= k::Kind i::Integer +{ + top.kindrep = k; + top.extractTyVarRep = i; +} + +abstract production tyVarNamed +top::TyVar ::= k::Kind i::Integer n::String +{ + forwards to tyVar(k, i); +} + +function freshTyVar +TyVar ::= k::Kind +{ + return tyVar(k, genInt()); +} + +function freshTyVarNamed +TyVar ::= k::Kind n::String +{ + return tyVarNamed(k, genInt(), n); +} + +function freshType +Type ::= +{ + return varType(freshTyVar(starKind())); +} + +function newSkolemConstant +Type ::= +{ + return skolemType(freshTyVar(starKind())); +} + +function freshInhSet +Type ::= +{ + return varType(freshTyVar(inhSetKind())); +} diff --git a/grammars/silver/compiler/definition/type/Unification.sv b/grammars/silver/compiler/definition/type/Unification.sv new file mode 100644 index 000000000..e4bc645b4 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Unification.sv @@ -0,0 +1,274 @@ +grammar silver:compiler:definition:type; + +inherited attribute unifyWith :: Type occurs on Type; +synthesized attribute unify :: Substitution occurs on Type; + +-------------------------------------------------------------------------------- +aspect production varType +top::Type ::= tv::TyVar +{ + top.unify = + case top.unifyWith of + | varType(j) when j.kindrep == tv.kindrep -> + if tv == j + then emptySubst() + else subst(tv, top.unifyWith) + | t when t.kindrep == tv.kindrep -> + if contains(tv, top.unifyWith.freeVariables) + then errorSubst("Infinite type! Tried to unify with " ++ prettyType(top.unifyWith)) + else subst(tv, top.unifyWith) + | t -> errorSubst("Kind mismatch! Tried to unify with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.unify = + case top.unifyWith of + | skolemType(otv) when tv.kindrep == otv.kindrep -> + if tv == otv + then emptySubst() + else errorSubst("Tried to unify skolem constant with incompatible skolem constant") + | _ -> errorSubst("Tried to unify skolem constant with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.unify = + case top.unifyWith of + | appType(c1, a1) -> + let unifyC :: Substitution = unify(c, c1) + in composeSubst(unifyC, unify(performSubstitution(a, unifyC), performSubstitution(a1, unifyC))) + end + | _ -> errorSubst("Tried to unify application of " ++ prettyType(c) ++ " with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production errorType +top::Type ::= +{ + top.unify = emptySubst(); -- report no additional errors +} + +aspect production intType +top::Type ::= +{ + top.unify = + case top.unifyWith of + | intType() -> emptySubst() + | _ -> errorSubst("Tried to unify Integer with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production boolType +top::Type ::= +{ + top.unify = + case top.unifyWith of + | boolType() -> emptySubst() + | _ -> errorSubst("Tried to unify Boolean with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production floatType +top::Type ::= +{ + top.unify = + case top.unifyWith of + | floatType() -> emptySubst() + | _ -> errorSubst("Tried to unify Float with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production stringType +top::Type ::= +{ + top.unify = + case top.unifyWith of + | stringType() -> emptySubst() + | _ -> errorSubst("Tried to unify Boolean with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production terminalIdType +top::Type ::= +{ + top.unify = + case top.unifyWith of + | terminalIdType() -> emptySubst() + | _ -> errorSubst("Tried to unify TerminalId with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production nonterminalType +top::Type ::= fn::String ks::[Kind] tracked::Boolean +{ + top.unify = + case top.unifyWith of + | nonterminalType(ofn, oks, otracked) -> + if fn == ofn && tracked == otracked -- Mismatched trackedness can happen when comparing interface files + then if ks == oks + then emptySubst() + else error("kind mismatch during unification for " ++ prettyType(top) ++ " and " ++ prettyType(top.unifyWith)) -- Should be impossible + else errorSubst("Tried to unify conflicting nonterminal types " ++ fn ++ " and " ++ ofn) + | ntOrDecType(_, _, _) -> errorSubst("nte-nodte: try again") + | _ -> errorSubst("Tried to unify nonterminal type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.unify = + case top.unifyWith of + | terminalType(ofn) -> + if fn == ofn + then emptySubst() + else errorSubst("Tried to unify conflicting terminal types " ++ fn ++ " and " ++ ofn) + | _ -> errorSubst("Tried to unify terminal type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production inhSetType +top::Type ::= inhs::[String] +{ + top.unify = + case top.unifyWith of + | inhSetType(oinhs) when inhs == oinhs -> emptySubst() + | _ -> errorSubst("Tried to unify inh set type " ++ prettyType(top) ++ " with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production decoratedType +top::Type ::= te::Type i::Type +{ + top.unify = + case top.unifyWith of + | decoratedType(ote, oi) -> composeSubst(unify(te, ote), unify(i, oi)) + | ntOrDecType(_,_,_) -> errorSubst("dte-nodte: try again") + | _ -> errorSubst("Tried to unify decorated type with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type i::Type +{ + top.unify = + case top.unifyWith of + | partiallyDecoratedType(ote, oi) -> composeSubst(unify(te, ote), unify(i, oi)) + | ntOrDecType(_,_,_) -> errorSubst("dte-nodte: try again") + | _ -> errorSubst("Tried to unify partially decorated type with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production ntOrDecType +top::Type ::= nt::Type inhs::Type hidden::Type +{ + -- If we're being asked to unify, then we know hidden is still a type variable, + -- since we shouldn't be unifying with anything but fully-substituted types. + -- And we kill off this type once hidden is specialized. + top.unify = + case top.unifyWith.baseType of + | ntOrDecType(ont1, oi, ohidden1) -> + -- Ensure compatibility between nonterminal types and inh sets, then merge our specializations + unifyAllShortCircuit([ont1, oi, ohidden1], + [nt, inhs, hidden]) + | decoratedType(ote, oi) -> + -- Ensure compatibility between Decorated nonterminal types, then specialize ourselves + unifyAllShortCircuit([ote, oi, top.unifyWith], + [nt, inhs, hidden]) + | partiallyDecoratedType(ote, oi) -> + -- Ensure compatibility between Decorated nonterminal types, then specialize ourselves + unifyAllShortCircuit([ote, oi, top.unifyWith], + [nt, inhs, hidden]) + | nonterminalType(_, _, _) -> + -- Ensure compatibility between nonterminal types, then specialize ourselves + unifyAllShortCircuit([top.unifyWith, top.unifyWith], + [nt, hidden]) + | skolemType(_) -> + -- Ensure compatibility between skolem types (referring to an unknown nonterminal), then specialize ourselves + unifyAllShortCircuit([top.unifyWith, top.unifyWith], + [nt, hidden]) + | _ -> errorSubst("Tried to unify decorated type with " ++ prettyType(top.unifyWith)) + end; +} + +aspect production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.unify = + case top.unifyWith of + | functionType(op, onp) when params == op && namedParams == onp -> emptySubst() + | _ -> errorSubst("Tried to unify conflicting function types " ++ prettyType(top) ++ " and " ++ prettyType(top.unifyWith)) + end; +} + +-------------------------------------------------------------------------------- + +function unify +Substitution ::= te1::Type te2::Type +{ + local leftward :: Substitution = te1.unify; + te1.unifyWith = te2; + + local rightward :: Substitution = te2.unify; + te2.unifyWith = te1; + + return if null(leftward.substErrors) + then leftward -- arbitrary choice if both work, but if they are confluent, it's okay + else rightward; -- arbitrary choice of errors. Non-confluent!! +} + +function unifyCheck +Substitution ::= te1::Type te2::Type s::Substitution +{ + return composeSubst(ignoreFailure(s), unify(performSubstitution(te1, s), performSubstitution(te2, s))); +} + +-- This function is meant to produce a simple rewriting FROM `fromte` to `tote` +-- suitable for use with `performRenaming` (vs `performSubstitution`). +-- Basically, it's supposed to structurally rewrite type variables from +-- stale variables in the environment, to contextually valid variables/types. +-- e.g. (v1 ::= v1 v2) U (int ::= int v1) +-- should yield: v1 -> int, v2 -> v1. +-- Rewriting should apply this without `v2` becoming `int`. (As normal subst would do.) +-- TODO this code is obviously implemented in a fragile way. +function unifyDirectional +Substitution ::= fromte::Type tote::Type +{ + -- Currently, this is built on the assumption that the unification will not fail. + -- Therefore, for now we will FRAGILEY just call unify + -- This is a possible source of bugs/unexpected behavior? + return unify(fromte, tote); +} + +function unifyAll +Substitution ::= te1::[Type] te2::[Type] +{ + local first :: Substitution = unify(head(te1), head(te2)); + + return if null(te1) && null(te2) + then emptySubst() + else if null(te1) || null(te2) + then errorSubst("Internal error: unifying mismatching numbers") + else composeSubst(first, unifyAll( mapSubst(tail(te1), first), + mapSubst(tail(te2), first) )); +} + +function unifyAllShortCircuit +Substitution ::= te1::[Type] te2::[Type] +{ + local first :: Substitution = unify(head(te1), head(te2)); + + return if null(te1) && null(te2) + then emptySubst() + else if null(te1) || null(te2) + then errorSubst("Internal error: unifying mismatching numbers") + else if first.failure + then first -- terminate recursion! + else composeSubst(first, unifyAllShortCircuit( mapSubst(tail(te1), first), + mapSubst(tail(te2), first) )); +} diff --git a/grammars/silver/compiler/definition/type/Util.sv b/grammars/silver/compiler/definition/type/Util.sv new file mode 100644 index 000000000..e6af4f8d7 --- /dev/null +++ b/grammars/silver/compiler/definition/type/Util.sv @@ -0,0 +1,270 @@ +grammar silver:compiler:definition:type; + +-- Quick check to see if an error message should be suppressed +synthesized attribute isError :: Boolean; + +-- Check for whether the type can be applied +synthesized attribute isApplicable :: Boolean; + +synthesized attribute inputTypes :: [Type]; +synthesized attribute outputType :: Type; +synthesized attribute namedTypes :: [Pair]; +synthesized attribute arity :: Integer; +synthesized attribute baseType :: Type; +synthesized attribute argTypes :: [Type]; +synthesized attribute inhSetMembers :: [String]; +monoid attribute freeSkolemVars :: [TyVar] with [], setUnionTyVars; +monoid attribute freeFlexibleVars :: [TyVar] with [], setUnionTyVars; + +-- Used by Expr, could possibly be replaced by pattern matching for decoratedType +-- Also used by 'new()' +synthesized attribute isDecorated :: Boolean; + +-- Determines whether a type is a partially decorated nonterminal type +-- Used in determining whether a type may be supplied with inherited attributes. +synthesized attribute isPartiallyDecorated :: Boolean; + +-- Determines whether a type is an (undecorated) nonterminal type +-- Used in determining whether a type may be supplied with inherited attributes. +synthesized attribute isNonterminal :: Boolean; + +-- Used for type checking by 'terminal()' +synthesized attribute isTerminal :: Boolean; + +-- Used by 'new' and type-determination for attributes (NOT on regular nonterminals) +synthesized attribute decoratedType :: Type; + +-- Freshens a nonterminal PolyType into a possibly-decorated nonterminal Type +synthesized attribute asNtOrDecType :: Type; + +-- The decorated type that this type forwards to, if it is an ntOrDecType +synthesized attribute defaultSpecialization :: Type; + +-- Used instead of unify() when we want to just know its decorated or undecorated +synthesized attribute unifyInstanceNonterminal :: Substitution; +synthesized attribute unifyInstanceDecorated :: Substitution; +synthesized attribute unifyInstanceDecorable :: Substitution; -- NT or partially decorated + +attribute arity, isError, isDecorated, isPartiallyDecorated, isNonterminal, isTerminal, asNtOrDecType, compareTo, isEqual occurs on PolyType; + +aspect production monoType +top::PolyType ::= ty::Type +{ + top.arity = ty.arity; + top.isError = ty.isError; + top.isDecorated = ty.isDecorated; + top.isPartiallyDecorated = ty.isPartiallyDecorated; + top.isNonterminal = ty.isNonterminal; + top.isTerminal = ty.isTerminal; + top.asNtOrDecType = ty.asNtOrDecType; + + top.isEqual = + top.compareTo.boundVars == [] && + top.compareTo.contexts == [] && + top.compareTo.typerep == ty; +} + +aspect production polyType +top::PolyType ::= bound::[TyVar] ty::Type +{ + top.arity = ty.arity; + top.isError = ty.isError; + top.isDecorated = ty.isDecorated; + top.isPartiallyDecorated = ty.isPartiallyDecorated; + top.isNonterminal = ty.isNonterminal; + top.isTerminal = ty.isTerminal; + top.asNtOrDecType = error("Only mono types should be possibly-decorated"); + + local eqSub::Substitution = + zipVarsIntoSubstitution(bound, top.compareTo.boundVars); + top.isEqual = + top.compareTo.contexts == [] && + top.compareTo.typerep == performRenaming(ty, eqSub); +} + +aspect production constraintType +top::PolyType ::= bound::[TyVar] contexts::[Context] ty::Type +{ + top.arity = ty.arity; + top.isError = ty.isError; + top.isDecorated = ty.isDecorated; + top.isPartiallyDecorated = ty.isPartiallyDecorated; + top.isNonterminal = ty.isNonterminal; + top.isTerminal = ty.isTerminal; + top.asNtOrDecType = error("Only mono types should be possibly-decorated"); + + local eqSub::Substitution = + zipVarsIntoSubstitution(bound, top.compareTo.boundVars); + top.isEqual = + top.compareTo.contexts == map(performContextRenaming(_, eqSub), contexts) && + top.compareTo.typerep == performRenaming(ty, eqSub); +} + +attribute isError, inputTypes, outputType, namedTypes, arity, baseType, argTypes, isDecorated, isPartiallyDecorated, isNonterminal, isTerminal, isApplicable, decoratedType, asNtOrDecType, defaultSpecialization, inhSetMembers, freeSkolemVars, freeFlexibleVars, unifyInstanceNonterminal, unifyInstanceDecorated, unifyInstanceDecorable occurs on Type; + +propagate freeSkolemVars, freeFlexibleVars on Type; + +aspect default production +top::Type ::= +{ + top.inputTypes = []; + top.outputType = errorType(); + top.namedTypes = []; + top.arity = 0; + top.baseType = top; + top.argTypes = []; + top.inhSetMembers = []; + + top.isDecorated = false; + top.isPartiallyDecorated = false; + top.isNonterminal = false; + top.isTerminal = false; + top.isError = false; + top.isApplicable = false; + + top.decoratedType = errorType(); + top.asNtOrDecType = errorType(); + top.defaultSpecialization = top; + + top.unifyInstanceNonterminal = errorSubst("not nt"); + top.unifyInstanceDecorated = errorSubst("not dec"); + top.unifyInstanceDecorable = errorSubst("not dec"); +} + +aspect production varType +top::Type ::= tv::TyVar +{ + top.freeFlexibleVars <- [tv]; +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.freeSkolemVars <- [tv]; + + -- Skolems with occurs-on contexts act like nonterminals, so use that behavior in unification + top.asNtOrDecType = ntOrDecType(top, freshInhSet(), freshType()); + top.unifyInstanceNonterminal = emptySubst(); + top.unifyInstanceDecorable = emptySubst(); +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.baseType = c.baseType; + top.argTypes = c.argTypes ++ [a]; + top.isNonterminal = c.isNonterminal; + top.asNtOrDecType = ntOrDecType(top, freshInhSet(), freshType()); -- c.baseType should be a nonterminal or skolem + top.unifyInstanceNonterminal = c.unifyInstanceNonterminal; + top.unifyInstanceDecorable = c.unifyInstanceDecorable; + top.arity = c.arity; + top.isApplicable = c.isApplicable; + + top.inputTypes = take(top.arity, top.argTypes); + top.outputType = + case top.baseType of + | functionType(_, _) -> last(top.argTypes) + | _ -> errorType() + end; + top.namedTypes = + case top.baseType of + | functionType(_, nps) -> zipWith(pair, nps, drop(top.arity, top.argTypes)) + | _ -> [] + end; +} + + +aspect production errorType +top::Type ::= +{ + top.isError = true; +} + +aspect production intType +top::Type ::= +{ +} + +aspect production boolType +top::Type ::= +{ +} + +aspect production floatType +top::Type ::= +{ +} + +aspect production stringType +top::Type ::= +{ +} + +aspect production nonterminalType +top::Type ::= fn::String _ _ +{ + top.isNonterminal = true; + top.asNtOrDecType = ntOrDecType(top, freshInhSet(), freshType()); + top.unifyInstanceNonterminal = emptySubst(); + top.unifyInstanceDecorable = emptySubst(); +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.isTerminal = true; +} + +aspect production inhSetType +top::Type ::= inhs::[String] +{ + top.inhSetMembers = inhs; +} + +aspect production decoratedType +top::Type ::= te::Type i::Type +{ + top.isDecorated = true; + top.decoratedType = te; + top.inhSetMembers = i.inhSetMembers; + top.unifyInstanceDecorated = emptySubst(); +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type i::Type +{ + top.isDecorated = true; + top.isPartiallyDecorated = true; + top.decoratedType = te; + top.asNtOrDecType = ntOrDecType(te, freshInhSet(), freshType()); + top.inhSetMembers = i.inhSetMembers; + top.unifyInstanceDecorated = emptySubst(); + top.unifyInstanceDecorable = emptySubst(); +} + +aspect production ntOrDecType +top::Type ::= nt::Type inhs::Type hidden::Type +{ + top.baseType = top; + top.argTypes = []; + + top.asNtOrDecType = top; + top.unifyInstanceNonterminal = unify(hidden, nt); + top.unifyInstanceDecorated = unify(hidden, decoratedType(nt, inhs)); + top.unifyInstanceDecorable = unify(hidden, nt); +} + +aspect production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.arity = params; + top.isApplicable = true; +} + +-- Strict type equality, assuming all type vars are skolemized +instance Eq Type { + eq = \ t1::Type t2::Type -> !unifyDirectional(t1, t2).failure; +} + +attribute compareTo, isEqual occurs on Context; +propagate compareTo, isEqual on Context; diff --git a/grammars/silver/compiler/definition/type/syntax/AspectDcl.sv b/grammars/silver/compiler/definition/type/syntax/AspectDcl.sv new file mode 100644 index 000000000..af1285637 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/AspectDcl.sv @@ -0,0 +1,67 @@ +grammar silver:compiler:definition:type:syntax; + +attribute lexicalTypeVariables, lexicalTyVarKinds occurs on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectRHSElem, AspectFunctionSignature, AspectFunctionLHS; + +flowtype lexicalTypeVariables {realSignature, env, flowEnv, grammarName} on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectFunctionSignature, AspectFunctionLHS; +flowtype lexicalTypeVariables {realSignature, env, flowEnv, grammarName, deterministicCount} on AspectRHSElem; + +function addNewLexicalTyVars_ActuallyVariables +[Def] ::= gn::String sl::Location lk::[Pair] l::[String] +{ + return map(\ n::String -> aspectLexTyVarDef(gn, sl, n, freshTyVarNamed(fromMaybe(starKind(), lookup(n, lk)), n)), l); +} + +-- This binds variables that appear in the signature to type variables, rather than skolem constants +-- as in productions declarations. They will be unified with the "real" type, and therefore +-- will become those skolem constants. + +aspect production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(ns.lexicalTypeVariables); + + sigDefs <- addNewLexicalTyVars_ActuallyVariables(top.grammarName, top.location, ns.lexicalTyVarKinds, allLexicalTyVars); +} + +aspect production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(ns.lexicalTypeVariables); + + sigDefs <- addNewLexicalTyVars_ActuallyVariables(top.grammarName, top.location, ns.lexicalTyVarKinds, allLexicalTyVars); +} + +propagate lexicalTypeVariables on AspectProductionLHS, AspectFunctionLHS, AspectRHS, AspectRHSElem excluding aspectRHSElemCons; +propagate lexicalTyVarKinds on AspectProductionSignature, AspectFunctionSignature, AspectProductionLHS, AspectFunctionLHS, AspectRHS, AspectRHSElem; + +aspect production aspectProductionSignature +top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS +{ + top.lexicalTypeVariables := nub(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); +} + +aspect production aspectRHSElemCons +top::AspectRHS ::= h::AspectRHSElem t::AspectRHS +{ + top.lexicalTypeVariables := nub(h.lexicalTypeVariables ++ t.lexicalTypeVariables); +} + +aspect production aspectProductionLHSTyped +top::AspectProductionLHS ::= id::Name '::' t::TypeExpr +{ + propagate lexicalTypeVariables, lexicalTyVarKinds; +} + +aspect production aspectRHSElemTyped +top::AspectRHSElem ::= id::Name '::' t::TypeExpr +{ + propagate lexicalTypeVariables, lexicalTyVarKinds; +} + +aspect production aspectFunctionSignature +top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS +{ + top.lexicalTypeVariables := nub(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); +} diff --git a/grammars/silver/compiler/definition/type/syntax/ClassDcl.sv b/grammars/silver/compiler/definition/type/syntax/ClassDcl.sv new file mode 100644 index 000000000..503ef3a67 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/ClassDcl.sv @@ -0,0 +1,10 @@ +grammar silver:compiler:definition:type:syntax; + +aspect production typeClassDcl +top::AGDcl ::= 'class' cl::ConstraintList '=>' id::QNameType var::TypeExpr '{' body::ClassBody '}' +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(cl.lexicalTypeVariables ++ var.lexicalTypeVariables ++ body.lexicalTypeVariables); + + headPreDefs <- addNewLexicalTyVars(top.grammarName, top.location, cl.lexicalTyVarKinds ++ var.lexicalTyVarKinds ++ body.lexicalTyVarKinds, allLexicalTyVars); +} diff --git a/grammars/silver/compiler/definition/type/syntax/Constraint.sv b/grammars/silver/compiler/definition/type/syntax/Constraint.sv new file mode 100644 index 000000000..31b3f633c --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/Constraint.sv @@ -0,0 +1,435 @@ +grammar silver:compiler:definition:type:syntax; + +autocopy attribute constraintPos::ConstraintPosition; + +nonterminal ConstraintList + -- This grammar doesn't export silver:compiler:definition:core, so the type concrete + -- syntax doesn't "know about" the core layout terminals. + -- Thus we have to set the layout explicitly for the "root" nonterminal here. + layout {BlockComments, Comments, WhiteSpace} + with config, grammarName, env, flowEnv, location, unparse, errors, defs, occursDefs, contexts, lexicalTypeVariables, lexicalTyVarKinds, constraintPos; +nonterminal Constraint with config, grammarName, env, flowEnv, location, unparse, errors, defs, occursDefs, contexts, lexicalTypeVariables, lexicalTyVarKinds, constraintPos; + +flowtype Constraint = decorate {grammarName, env, flowEnv, constraintPos}; + +propagate errors, defs, occursDefs, lexicalTypeVariables, lexicalTyVarKinds on ConstraintList, Constraint; + +concrete production consConstraint +top::ConstraintList ::= h::Constraint ',' t::ConstraintList +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; + top.contexts = h.contexts ++ t.contexts; +} +concrete production oneConstraint +top::ConstraintList ::= h::Constraint +{ + top.unparse = h.unparse; + top.contexts = h.contexts; +} +abstract production nilConstraint +top::ConstraintList ::= +{ + top.unparse = ""; + top.contexts = []; +} + +concrete production classConstraint +top::Constraint ::= c::QNameType t::TypeExpr +{ + top.unparse = c.unparse ++ " " ++ t.unparse; + top.contexts = + if !null(undecidableInstanceErrors) then [] -- Avoid a cycle in instance resolution checking + else [instContext(fName, t.typerep)]; + + production dcl::TypeDclInfo = c.lookupType.dcl; + production fName::String = c.lookupType.fullName; + + top.errors <- c.lookupType.errors; + top.errors <- + if c.lookupType.found && dcl.isClass then [] + else [err(c.location, c.name ++ " is not a type class.")]; + top.errors <- t.errorsTyVars; + + -- We essentially permit FlexibleInstances but not UndecidableInstnaces, + -- check that there are no class constraints if instance head is a type variable. + -- This is required to ensure that instance resolution will terminate, otherwise + -- one could write e.g. instance Eq a => Eq a. + -- HOWEVER, this is sometimes really handy in some places (that we know are safe) + -- within the standard library; turn off this check for those instances + -- (equivalent to writing {-# LANGUAGE UndecidableInstances #-} in Haskell): + production attribute undecidableInstanceClasses::[String] with ++; + undecidableInstanceClasses := [ + -- Safe because instance for Show a has no further class constraints + "silver:langutil:pp:Show" + ]; + + production undecidableInstanceErrors::[Message] = + case top.constraintPos.instanceHead of + | just(h) when (h, contains(fName, undecidableInstanceClasses)) matches (instContext(_, skolemType(_)), false) -> + [err(top.location, s"The constraint ${top.unparse} is no smaller than the instance head ${prettyContext(h)}")] + | _ -> [] + end; + top.errors <- undecidableInstanceErrors; + + local instDcl::InstDclInfo = top.constraintPos.classInstDcl(fName, t.typerep, top.grammarName, top.location); + top.defs <- [tcInstDef(instDcl)]; + top.defs <- transitiveSuperDefs(top.env, t.typerep, [], instDcl); + top.occursDefs <- transitiveSuperOccursDefs(top.env, t.typerep, [], instDcl); + + top.lexicalTyVarKinds <- + case t of + | typeVariableTypeExpr(tv) + -- Avoid circular inference if someone uses a class constraint within its own definition + when top.constraintPos.classDefName != just(fName) -> + [pair(tv.lexeme, c.lookupType.typeScheme.monoType.kindrep)] + | _ -> [] + end; +} action { + insert semantic token IdTypeClass_t at c.baseNameLoc; +} + +concrete production inhOccursConstraint +top::Constraint ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' t::TypeExpr +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ t.unparse; + top.contexts = [inhOccursContext(fName, attl.types, attrTy, t.typerep)]; + + production dcl::AttributeDclInfo = at.lookupAttribute.dcl; + production fName::String = at.lookupAttribute.fullName; + + top.errors <- at.lookupAttribute.errors; + top.errors <- + if at.lookupAttribute.found && !dcl.isInherited + then [err(at.location, fName ++ " is not an inherited attribute")] + else []; + + top.errors <- + if attl.missingCount > 0 + then [err(attl.location, "Attribute type arguments cannot contain _")] + else []; + + -- Make sure we get the number and kind of type variables correct for the ATTR + top.errors <- + if length(atTypeScheme.boundVars) != length(attl.types) + then [err(at.location, + at.name ++ " expects " ++ toString(length(atTypeScheme.boundVars)) ++ + " type variables, but " ++ toString(length(attl.types)) ++ " were provided.")] + else if map((.kindrep), atTypeScheme.boundVars) != map((.kindrep), attl.types) + then [err(at.location, + at.name ++ " has kind " ++ prettyKind(foldr(arrowKind, starKind(), map((.kindrep), atTypeScheme.boundVars))) ++ + "but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), attl.types)) ++ ".")] + else []; + + top.errors <- t.errorsKindStar; + + local atTypeScheme::PolyType = at.lookupAttribute.typeScheme; + local rewrite :: Substitution = zipVarsAndTypesIntoSubstitution(atTypeScheme.boundVars, attl.types); + production attrTy::Type = performRenaming(atTypeScheme.typerep, rewrite); + + local instDcl::OccursDclInfo = top.constraintPos.occursInstDcl(fName, t.typerep, attrTy, top.grammarName, top.location); + top.occursDefs <- [instDcl]; +} + +concrete production synOccursConstraint +top::Constraint ::= 'attribute' at::QName attl::BracketedOptTypeExprs i::TypeExpr 'occurs' 'on' t::TypeExpr +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " " ++ i.unparse ++ " occurs on " ++ t.unparse; + top.contexts = [synOccursContext(fName, attl.types, attrTy, i.typerep, t.typerep)]; + + production dcl::AttributeDclInfo = at.lookupAttribute.dcl; + production fName::String = at.lookupAttribute.fullName; + + top.errors <- at.lookupAttribute.errors; + top.errors <- + if at.lookupAttribute.found && !dcl.isSynthesized + then [err(at.location, fName ++ " is not a synthesized attribute")] + else []; + + top.errors <- + if attl.missingCount > 0 + then [err(attl.location, "Attribute type arguments cannot contain _")] + else []; + + -- Make sure we get the number and kind of type variables correct for the ATTR + top.errors <- + if length(atTypeScheme.boundVars) != length(attl.types) + then [err(at.location, + at.name ++ " expects " ++ toString(length(atTypeScheme.boundVars)) ++ + " type variables, but " ++ toString(length(attl.types)) ++ " were provided.")] + else if map((.kindrep), atTypeScheme.boundVars) != map((.kindrep), attl.types) + then [err(at.location, + at.name ++ " has kind " ++ prettyKind(foldr(arrowKind, starKind(), map((.kindrep), atTypeScheme.boundVars))) ++ + "but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), attl.types)) ++ ".")] + else []; + + top.errors <- + if i.typerep.kindrep != inhSetKind() + then [err(i.location, s"${i.unparse} has kind ${prettyKind(i.typerep.kindrep)}, but kind InhSet is expected here")] + else []; + + top.errors <- t.errorsKindStar; + + local atTypeScheme::PolyType = at.lookupAttribute.typeScheme; + local rewrite :: Substitution = zipVarsAndTypesIntoSubstitution(atTypeScheme.boundVars, attl.types); + production attrTy::Type = performRenaming(atTypeScheme.typerep, rewrite); + + local instDcl::OccursDclInfo = top.constraintPos.occursInstDcl(fName, t.typerep, attrTy, top.grammarName, top.location); + top.occursDefs <- [instDcl]; + + top.lexicalTyVarKinds <- + case i of + | typeVariableTypeExpr(tv) -> [pair(tv.lexeme, inhSetKind())] + | _ -> [] + end; +} + +concrete production annoOccursConstraint +top::Constraint ::= 'annotation' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' t::TypeExpr +{ + top.unparse = "annotation " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ t.unparse; + top.contexts = [annoOccursContext(fName, attl.types, attrTy, t.typerep)]; + + production dcl::AttributeDclInfo = at.lookupAttribute.dcl; + production fName::String = at.lookupAttribute.fullName; + + top.errors <- at.lookupAttribute.errors; + top.errors <- + if at.lookupAttribute.found && !dcl.isAnnotation + then [err(at.location, fName ++ " is not an annotation")] + else []; + + top.errors <- + if attl.missingCount > 0 + then [err(attl.location, "Annotation type arguments cannot contain _")] + else []; + + -- Make sure we get the number and kind of type variables correct for the ATTR + top.errors <- + if length(atTypeScheme.boundVars) != length(attl.types) + then [err(at.location, + at.name ++ " expects " ++ toString(length(atTypeScheme.boundVars)) ++ + " type variables, but " ++ toString(length(attl.types)) ++ " were provided.")] + else if map((.kindrep), atTypeScheme.boundVars) != map((.kindrep), attl.types) + then [err(at.location, + at.name ++ " has kind " ++ prettyKind(foldr(arrowKind, starKind(), map((.kindrep), atTypeScheme.boundVars))) ++ + "but type variable(s) have kind(s) " ++ implode(", ", map(compose(prettyKind, (.kindrep)), attl.types)) ++ ".")] + else []; + + top.errors <- t.errorsKindStar; + + local atTypeScheme::PolyType = at.lookupAttribute.typeScheme; + local rewrite :: Substitution = zipVarsAndTypesIntoSubstitution(atTypeScheme.boundVars, attl.types); + production attrTy::Type = performRenaming(atTypeScheme.typerep, rewrite); + + local instDcl::OccursDclInfo = top.constraintPos.occursInstDcl(fName, t.typerep, attrTy, top.grammarName, top.location); + top.occursDefs <- [instDcl]; +} + +concrete production typeableConstraint +top::Constraint ::= 'runtimeTypeable' t::TypeExpr +{ + top.unparse = "runtimeTypeable " ++ t.unparse; + top.contexts = [typeableContext(t.typerep)]; + + top.errors <- t.errorsTyVars; + top.errors <- t.errorsKindStar; + + local instDcl::InstDclInfo = top.constraintPos.typeableInstDcl(t.typerep, top.grammarName, top.location); + top.defs <- [tcInstDef(instDcl)]; +} + +concrete production inhSubsetConstraint +top::Constraint ::= i1::TypeExpr 'subset' i2::TypeExpr +{ + top.unparse = i1.unparse ++ " subset " ++ i2.unparse; + top.contexts = [inhSubsetContext(i1.typerep, i2.typerep)]; + + top.errors <- + if i1.typerep.kindrep != inhSetKind() + then [err(top.location, s"${top.unparse} has kind ${prettyKind(i1.typerep.kindrep)}, but kind InhSet is expected here")] + else []; + top.errors <- + if i2.typerep.kindrep != inhSetKind() + then [err(top.location, s"${top.unparse} has kind ${prettyKind(i2.typerep.kindrep)}, but kind InhSet is expected here")] + else []; + + local instDcl::InstDclInfo = top.constraintPos.inhSubsetInstDcl(i1.typerep, i2.typerep, top.grammarName, top.location); + top.defs <- + case top.constraintPos of + | classPos(_, _) -> [] + | _ -> [tcInstDef(instDcl)] + end; + top.errors <- + case top.constraintPos of + | classPos(_, _) -> [err(top.location, "subset constraint not permitted as superclass")] + | _ -> [] + end; + + top.lexicalTyVarKinds <- + case i1 of + | typeVariableTypeExpr(tv) -> [pair(tv.lexeme, inhSetKind())] + | _ -> [] + end; + top.lexicalTyVarKinds <- + case i2 of + | typeVariableTypeExpr(tv) -> [pair(tv.lexeme, inhSetKind())] + | _ -> [] + end; +} + +concrete production typeErrorConstraint +top::Constraint ::= 'typeError' msg::String_t +{ + top.unparse = "typeError " ++ msg.lexeme; + top.contexts = [typeErrorContext(unescapeString(substring(1, length(msg.lexeme) - 1, msg.lexeme)))]; + + top.errors <- + case top.constraintPos of + | instancePos(_, _) -> [] + | _ -> [err(top.location, "typeError constraint is only permitted on instances")] + end; +} + +synthesized attribute classInstDcl::(InstDclInfo ::= String Type String Location); +synthesized attribute occursInstDcl::(OccursDclInfo ::= String Type Type String Location); +synthesized attribute typeableInstDcl::(InstDclInfo ::= Type String Location); +synthesized attribute inhSubsetInstDcl::(InstDclInfo ::= Type Type String Location); +synthesized attribute classDefName::Maybe; +synthesized attribute instanceHead::Maybe; +nonterminal ConstraintPosition with classInstDcl, occursInstDcl, typeableInstDcl, inhSubsetInstDcl, classDefName, instanceHead; + +aspect default production +top::ConstraintPosition ::= +{ + top.classDefName = nothing(); + top.instanceHead = nothing(); +} +abstract production instancePos +top::ConstraintPosition ::= instHead::Context tvs::[TyVar] +{ + top.classInstDcl = instConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); + top.occursInstDcl = occursInstConstraintDcl(_, _, _, tvs, sourceGrammar=_, sourceLocation=_); + top.typeableInstDcl = typeableInstConstraintDcl(_, tvs, sourceGrammar=_, sourceLocation=_); + top.inhSubsetInstDcl = inhSubsetInstConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); + top.instanceHead = just(instHead); +} +abstract production classPos +top::ConstraintPosition ::= className::String tvs::[TyVar] +{ + top.classInstDcl = \ fName::String t::Type g::String l::Location -> + instSuperDcl(fName, + currentInstDcl(className, t, sourceGrammar=g, sourceLocation=l), + sourceGrammar=g, sourceLocation=l); + top.occursInstDcl = \ fName::String ntty::Type atty::Type g::String l::Location -> + occursSuperDcl(fName, atty, + currentInstDcl(className, ntty, sourceGrammar=g, sourceLocation=l), + sourceGrammar=g, sourceLocation=l); + top.typeableInstDcl = \ t::Type g::String l::Location -> + typeableSuperDcl( + currentInstDcl(className, t, sourceGrammar=g, sourceLocation=l), + sourceGrammar=g, sourceLocation=l); + top.inhSubsetInstDcl = error("subset constraint not permitted as superclass"); + top.classDefName = just(className); +} +abstract production classMemberPos +top::ConstraintPosition ::= className::String tvs::[TyVar] +{ + top.classInstDcl = instConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); + top.occursInstDcl = occursInstConstraintDcl(_, _, _, tvs, sourceGrammar=_, sourceLocation=_); + top.typeableInstDcl = typeableInstConstraintDcl(_, tvs, sourceGrammar=_, sourceLocation=_); + top.inhSubsetInstDcl = inhSubsetInstConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); + top.classDefName = just(className); + -- A bit strange, but class member constraints are sort of like instance constraints. + -- However we don't know what the instance type actually is, and want to skip the + -- decidability check, so just put errorType here for now. + top.instanceHead = just(instContext(className, errorType())); +} +abstract production signaturePos +top::ConstraintPosition ::= sig::NamedSignature +{ + top.classInstDcl = sigConstraintDcl(_, _, sig, sourceGrammar=_, sourceLocation=_); + top.occursInstDcl = occursSigConstraintDcl(_, _, _, sig, sourceGrammar=_, sourceLocation=_); + top.typeableInstDcl = typeableSigConstraintDcl(_, sig, sourceGrammar=_, sourceLocation=_); + top.inhSubsetInstDcl = inhSubsetSigConstraintDcl(_, _, sig, sourceGrammar=_, sourceLocation=_); +} +abstract production globalPos +top::ConstraintPosition ::= tvs::[TyVar] +{ + -- These are translated the same as instance constraints. + top.classInstDcl = instConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); + top.occursInstDcl = occursInstConstraintDcl(_, _, _, tvs, sourceGrammar=_, sourceLocation=_); + top.typeableInstDcl = typeableInstConstraintDcl(_, tvs, sourceGrammar=_, sourceLocation=_); + top.inhSubsetInstDcl = inhSubsetInstConstraintDcl(_, _, tvs, sourceGrammar=_, sourceLocation=_); +} + +function transitiveSuperContexts +[Context] ::= env::Decorated Env ty::Type seenClasses::[String] className::String +{ + local dcls::[TypeDclInfo] = getTypeDcl(className, env); + local dcl::TypeDclInfo = head(dcls); + dcl.givenInstanceType = ty; + local superClassNames::[String] = catMaybes(map((.contextClassName), dcl.superContexts)); + return + if null(dcls) || contains(dcl.fullName, seenClasses) + then [] + else unionsBy( + sameSuperContext, + dcl.superContexts :: + map(transitiveSuperContexts(env, ty, dcl.fullName :: seenClasses, _), superClassNames)); +} + +-- TODO: Should be an equality attribute, maybe? +function sameSuperContext +Boolean ::= c1::Context c2::Context +{ + return + case c1, c2 of + | instContext(c1, _), instContext(c2, _) -> c1 == c2 + | inhOccursContext(a1, _, _, _), inhOccursContext(a2, _, _, _) -> a1 == a2 + | synOccursContext(a1, _, _, _, _), synOccursContext(a2, _, _, _, _) -> a1 == a2 + | typeableContext(_), typeableContext(_) -> true + | _, _ -> false + end; +} + +function transitiveSuperDefs +[Def] ::= env::Decorated Env ty::Type seenClasses::[String] instDcl::InstDclInfo +{ + local dcls::[TypeDclInfo] = getTypeDcl(instDcl.fullName, env); + local dcl::TypeDclInfo = head(dcls); + dcl.givenInstanceType = ty; + local superClassNames::[String] = catMaybes(map((.contextClassName), dcl.superContexts)); + local superInstDcls::[InstDclInfo] = + map( + instSuperDcl(_, instDcl, sourceGrammar=instDcl.sourceGrammar, sourceLocation=instDcl.sourceLocation), + superClassNames); + return + if null(dcls) || contains(dcl.fullName, seenClasses) + then [] + else + -- This might introduce duplicate defs in "diamond subclassing" cases, + -- but that shouldn't actually be an issue besides the (minor) added lookup overhead. + flatMap(\ c::Context -> c.contextSuperDefs(instDcl, dcl.sourceGrammar, dcl.sourceLocation), dcl.superContexts) ++ + flatMap(transitiveSuperDefs(env, ty, dcl.fullName :: seenClasses, _), superInstDcls); +} + +function transitiveSuperOccursDefs +[OccursDclInfo] ::= env::Decorated Env ty::Type seenClasses::[String] instDcl::InstDclInfo +{ + local dcls::[TypeDclInfo] = getTypeDcl(instDcl.fullName, env); + local dcl::TypeDclInfo = head(dcls); + dcl.givenInstanceType = ty; + local superClassNames::[String] = catMaybes(map((.contextClassName), dcl.superContexts)); + local superInstDcls::[InstDclInfo] = + map( + instSuperDcl(_, instDcl, sourceGrammar=instDcl.sourceGrammar, sourceLocation=instDcl.sourceLocation), + superClassNames); + return + if null(dcls) || contains(dcl.fullName, seenClasses) + then [] + else + -- This might introduce duplicate defs in "diamond subclassing" cases, + -- but that shouldn't actually be an issue besides the (minor) added lookup overhead. + flatMap(\ c::Context -> c.contextSuperOccursDefs(instDcl, dcl.sourceGrammar, dcl.sourceLocation), dcl.superContexts) ++ + flatMap(transitiveSuperOccursDefs(env, ty, dcl.fullName :: seenClasses, _), superInstDcls); +} diff --git a/grammars/silver/compiler/definition/type/syntax/FunctionDcl.sv b/grammars/silver/compiler/definition/type/syntax/FunctionDcl.sv new file mode 100644 index 000000000..3d870ab20 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/FunctionDcl.sv @@ -0,0 +1,22 @@ +grammar silver:compiler:definition:type:syntax; + +attribute lexicalTypeVariables, lexicalTyVarKinds occurs on FunctionSignature, FunctionLHS; + +aspect production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(ns.lexicalTypeVariables); + + sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, ns.lexicalTyVarKinds, allLexicalTyVars); +} + +aspect production functionSignature +top::FunctionSignature ::= cl::ConstraintList '=>' lhs::FunctionLHS '::=' rhs::ProductionRHS +{ + top.lexicalTypeVariables := nub(cl.lexicalTypeVariables ++ lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); +} + +propagate lexicalTyVarKinds on FunctionSignature; +propagate lexicalTypeVariables, lexicalTyVarKinds on FunctionLHS; + diff --git a/grammars/silver/compiler/definition/type/syntax/InstanceDcl.sv b/grammars/silver/compiler/definition/type/syntax/InstanceDcl.sv new file mode 100644 index 000000000..814c3d505 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/InstanceDcl.sv @@ -0,0 +1,10 @@ +grammar silver:compiler:definition:type:syntax; + +aspect production instanceDcl +top::AGDcl ::= 'instance' cl::ConstraintList '=>' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(cl.lexicalTypeVariables ++ ty.lexicalTypeVariables); + + headPreDefs <- addNewLexicalTyVars(top.grammarName, top.location, cl.lexicalTyVarKinds ++ ty.lexicalTyVarKinds, allLexicalTyVars); +} diff --git a/grammars/silver/compiler/definition/type/syntax/KindExpr.sv b/grammars/silver/compiler/definition/type/syntax/KindExpr.sv new file mode 100644 index 000000000..48518b3b0 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/KindExpr.sv @@ -0,0 +1,33 @@ +grammar silver:compiler:definition:type:syntax; + +nonterminal KindExpr with config, location, grammarName, errors, env, unparse, kindrep; + +propagate errors on KindExpr; -- TODO: Are errors even possible here? + +concrete production starKindExpr +top::KindExpr ::= '*' +{ + top.unparse = "*"; + top.kindrep = starKind(); +} + +concrete production inhSetKindExpr +top::KindExpr ::= 'InhSet' +{ + top.unparse = "InhSet"; + top.kindrep = inhSetKind(); +} + +concrete production arrowKindExpr +top::KindExpr ::= k1::KindExpr '->' k2::KindExpr +{ + top.unparse = s"${k1.unparse} -> ${k2.unparse}"; + top.kindrep = arrowKind(k1.kindrep, k2.kindrep); +} + +concrete production parenKindExpr +top::KindExpr ::= '(' k::KindExpr ')' +{ + top.unparse = s"(${k.unparse})"; + forwards to k; +} diff --git a/grammars/silver/compiler/definition/type/syntax/ProductionDcl.sv b/grammars/silver/compiler/definition/type/syntax/ProductionDcl.sv new file mode 100644 index 000000000..0f0acac35 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/ProductionDcl.sv @@ -0,0 +1,30 @@ +grammar silver:compiler:definition:type:syntax; + +attribute lexicalTypeVariables, lexicalTyVarKinds occurs on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; + +flowtype lexicalTypeVariables {decorate} on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(ns.lexicalTypeVariables); + + sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, ns.lexicalTyVarKinds, allLexicalTyVars); +} + +propagate lexicalTyVarKinds on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; + +aspect production productionSignature +top::ProductionSignature ::= cl::ConstraintList '=>' lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + top.lexicalTypeVariables := nub(cl.lexicalTypeVariables ++ lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); +} + +propagate lexicalTypeVariables on ProductionLHS, ProductionRHS, ProductionRHSElem excluding productionRHSCons; + +aspect production productionRHSCons +top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS +{ + top.lexicalTypeVariables := nub(h.lexicalTypeVariables ++ t.lexicalTypeVariables); +} diff --git a/grammars/silver/compiler/definition/type/syntax/Terminals.sv b/grammars/silver/compiler/definition/type/syntax/Terminals.sv new file mode 100644 index 000000000..eeb32a074 --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/Terminals.sv @@ -0,0 +1,32 @@ +grammar silver:compiler:definition:type:syntax; + +imports silver:langutil:lsp as lsp; + +-- '<' has precedence 9, assoc = left + +terminal Arrow_t '->' association = right, lexer classes {SPECOP}; + +-- Ambiguity at '{' in production signature between an inh set type and the production body. +-- Since just `{inh}` doesn't make any sense in a production signature, +-- prefer '{' as the start of a production body. +terminal InhSetLCurly_t /{/; +disambiguate LCurly_t, InhSetLCurly_t { pluck LCurly_t; } + +terminal Boolean_tkwd 'Boolean' lexer classes {TYPE,RESERVED}; +terminal Decorated_tkwd 'Decorated' lexer classes {TYPE,RESERVED}, precedence=1; +terminal PartiallyDecorated_tkwd 'PartiallyDecorated' lexer classes {TYPE,RESERVED}, precedence=1; +terminal Float_tkwd 'Float' lexer classes {TYPE,RESERVED}; +terminal Integer_tkwd 'Integer' lexer classes {TYPE,RESERVED}; +terminal String_tkwd 'String' lexer classes {TYPE,RESERVED}; +terminal TerminalId_tkwd 'TerminalId' lexer classes {TYPE,RESERVED}; +terminal InhSet_tkwd 'InhSet' lexer classes {TYPE}; -- Well, actually a kind + +terminal RuntimeTypeable_kwd 'runtimeTypeable' lexer classes {KEYWORD,RESERVED}; +terminal Subset_kwd 'subset' lexer classes {KEYWORD}; +terminal TypeError_kwd 'typeError' lexer classes {KEYWORD}; + +terminal IdTypeVar_t /[a-z][A-Za-z0-9\_]*/ lexer classes {lsp:TypeParameter}; + +-- Avoid making these reserved, for now +disambiguate Subset_kwd, IdLower_t { pluck Subset_kwd; } +disambiguate TypeError_kwd, IdLower_t { pluck TypeError_kwd; } diff --git a/grammars/silver/compiler/definition/type/syntax/TypeExpr.sv b/grammars/silver/compiler/definition/type/syntax/TypeExpr.sv new file mode 100644 index 000000000..9f207998f --- /dev/null +++ b/grammars/silver/compiler/definition/type/syntax/TypeExpr.sv @@ -0,0 +1,551 @@ +grammar silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type; +imports silver:compiler:definition:env; +imports silver:compiler:definition:flow:syntax; + +nonterminal TypeExpr with config, location, grammarName, errors, env, flowEnv, unparse, typerep, lexicalTypeVariables, lexicalTyVarKinds, errorsTyVars, errorsKindStar, freeVariables, mentionedAliases, onNt, errorsInhSet, typerepInhSet; +nonterminal Signature with config, location, grammarName, errors, env, flowEnv, unparse, typerep, lexicalTypeVariables, lexicalTyVarKinds, mentionedAliases; +nonterminal SignatureLHS with config, location, grammarName, errors, env, flowEnv, unparse, maybeType, lexicalTypeVariables, lexicalTyVarKinds, mentionedAliases; +nonterminal TypeExprs with config, location, grammarName, errors, env, unparse, flowEnv, types, missingCount, lexicalTypeVariables, lexicalTyVarKinds, appArgKinds, appLexicalTyVarKinds, errorsTyVars, errorsKindStar, freeVariables, mentionedAliases; +nonterminal BracketedTypeExprs with config, location, grammarName, errors, env, flowEnv, unparse, types, missingCount, lexicalTypeVariables, lexicalTyVarKinds, appArgKinds, appLexicalTyVarKinds, errorsTyVars, freeVariables, mentionedAliases, envBindingTyVars, initialEnv; +nonterminal BracketedOptTypeExprs with config, location, grammarName, errors, env, flowEnv, unparse, types, missingCount, lexicalTypeVariables, lexicalTyVarKinds, appArgKinds, appLexicalTyVarKinds, errorsTyVars, freeVariables, mentionedAliases, envBindingTyVars, initialEnv; + +synthesized attribute maybeType :: Maybe; +synthesized attribute types :: [Type]; +synthesized attribute missingCount::Integer; + +-- Important: These should be IN-ORDER and include ALL type variables that appear, including duplicates! +monoid attribute lexicalTypeVariables :: [String]; +-- freeVariables also occurs on TypeExprs, and should be IN ORDER + +monoid attribute lexicalTyVarKinds :: [Pair]; + +inherited attribute appArgKinds :: [Kind]; +monoid attribute appLexicalTyVarKinds :: [Pair]; + +-- These attributes are used if we're using the TypeExprs as type variables-only. +monoid attribute errorsTyVars :: [Message]; +-- A new environment, with the type variables in this list appearing bound +inherited attribute initialEnv :: Decorated Env; +synthesized attribute envBindingTyVars :: Decorated Env; + +monoid attribute errorsKindStar::[Message]; + +-- The set of full names of type aliases that are mentioned by/transitively depended on by this type expression. +monoid attribute mentionedAliases :: [String]; + + +-- Better error checking and type information if this type expression is an inherited attribute set +-- for a particular nonterminal (e.g. the {env} in Decorated Expr with {env}). +-- We can check that the attributes actually occur on the nonterminal, +-- and also can interpret {decorate} or {forward} since we can look up those flow types. +-- The nonterminal type is specified by the attribute onNt, which should only be used by these attributes. +synthesized attribute errorsInhSet::[Message]; +synthesized attribute typerepInhSet::Type; +flowtype errorsInhSet {decorate, onNt} on TypeExpr; +flowtype typerepInhSet {decorate, onNt} on TypeExpr; + +flowtype TypeExpr = + decorate {grammarName, env, flowEnv}, forward {decorate}, + freeVariables {decorate}, lexicalTypeVariables {decorate}, lexicalTyVarKinds {decorate}, + errorsTyVars {decorate}, errorsKindStar {decorate}; + +-- typerep requires flowEnv to look up default ref sets +flowtype typerep {grammarName, env, flowEnv} on TypeExpr, Signature; +flowtype maybeType {grammarName, env, flowEnv} on SignatureLHS; +flowtype types {grammarName, env, flowEnv} on TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs; + +propagate errors on TypeExpr, Signature, SignatureLHS, TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs excluding refTypeExpr, partialRefTypeExpr; +propagate lexicalTypeVariables, lexicalTyVarKinds on TypeExpr, Signature, SignatureLHS, TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs; +propagate appLexicalTyVarKinds on TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs; +propagate errorsTyVars on TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs; +propagate errorsKindStar on TypeExprs; +propagate mentionedAliases on TypeExpr, Signature, SignatureLHS, TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs; + +function addNewLexicalTyVars +[Def] ::= gn::String sl::Location lk::[Pair] l::[String] +{ + return map(\ n::String -> lexTyVarDef(gn, sl, n, freshTyVarNamed(fromMaybe(starKind(), lookup(n, lk)), n)), l); +} + +aspect default production +top::TypeExpr ::= +{ + -- This has to do with type lists that are type variables only. + -- We don't have a separate nonterminal for this, because we'd like to produce + -- "semantic" errors, rather than parse errors for this. + top.errorsTyVars := [err(top.location, top.unparse ++ " is not permitted here, only type variables are")]; + top.freeVariables = top.typerep.freeVariables; + top.errorsKindStar := + if top.typerep.kindrep != starKind() + then [err(top.location, s"${top.unparse} has kind ${prettyKind(top.typerep.kindrep)}, but kind * is expected here")] + else []; + top.errorsInhSet = top.errors; + top.typerepInhSet = top.typerep; +} + +abstract production errorTypeExpr +top::TypeExpr ::= e::[Message] +{ + top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; + + top.typerep = errorType(); + + top.errors <- e; +} + +abstract production typerepTypeExpr +top::TypeExpr ::= t::Type +{ + top.unparse = prettyType(t); + + top.typerep = t; +} + +concrete production integerTypeExpr +top::TypeExpr ::= 'Integer' +{ + top.unparse = "Integer"; + + top.typerep = intType(); +} + +concrete production floatTypeExpr +top::TypeExpr ::= 'Float' +{ + top.unparse = "Float"; + + top.typerep = floatType(); +} + +concrete production stringTypeExpr +top::TypeExpr ::= 'String' +{ + top.unparse = "String"; + + top.typerep = stringType(); +} + +concrete production booleanTypeExpr +top::TypeExpr ::= 'Boolean' +{ + top.unparse = "Boolean"; + + top.typerep = boolType(); +} + +concrete production terminalIdTypeExpr +top::TypeExpr ::= 'TerminalId' +{ + top.unparse = "TerminalId"; + + top.typerep = terminalIdType(); +} + +concrete production inhSetTypeExpr +top::TypeExpr ::= InhSetLCurly_t inhs::FlowSpecInhs '}' +{ + top.unparse = s"{${inhs.unparse}}"; + + top.typerep = inhSetType(sort(nub(inhs.inhList))); + inhs.onNt = errorType(); + + -- When we are in a refTypeExpr where we know the nonterminal type, + -- decorate the inhSetTypeExpr with onNt for better errors and lookup disambiguation. + production ntInhs::FlowSpecInhs = inhs; + ntInhs.config = top.config; + ntInhs.grammarName = top.grammarName; + ntInhs.env = top.env; + ntInhs.flowEnv = top.flowEnv; + ntInhs.onNt = top.onNt; + + top.errorsInhSet = ntInhs.errors; + top.typerepInhSet = inhSetType(sort(ntInhs.inhList)); +} + +concrete production nominalTypeExpr +top::TypeExpr ::= q::QNameType +{ + top.unparse = q.unparse; + + top.mentionedAliases <- + if q.lookupType.found && q.lookupType.dcl.isTypeAlias + then q.lookupType.fullName :: q.lookupType.dcl.mentionedAliases + else []; + + top.errors <- q.lookupType.errors; + top.errors <- + if !q.lookupType.found || q.lookupType.dcl.isType then [] + else if q.lookupType.dcl.isTypeAlias -- Raise a less confusing error if we see an unapplied type alias + then [err(top.location, q.name ++ " is a type alias, expecting " ++ toString(length(q.lookupType.dcl.typeScheme.boundVars)) ++ " type arguments.")] + else [err(top.location, q.name ++ " is not a type.")]; + + top.typerep = q.lookupType.typeScheme.typerep; -- NOT .monoType since this can be a polyType when an error is raised +} action { + insert semantic token IdType_t at q.baseNameLoc; +} + +concrete production typeVariableTypeExpr +top::TypeExpr ::= tv::IdLower_t +{ + top.unparse = tv.lexeme; + + local attribute hack::QNameLookup; + hack = customLookup("type", getTypeDcl(tv.lexeme, top.env), tv.lexeme, top.location); + + top.typerep = hack.typeScheme.monoType; + top.errors <- hack.errors; + top.errorsTyVars := []; + + top.lexicalTypeVariables <- [tv.lexeme]; +} action { + insert semantic token IdTypeVar_t at tv.location; +} + +concrete production kindSigTypeVariableTypeExpr +top::TypeExpr ::= '(' tv::IdLower_t '::' k::KindExpr ')' +{ + top.unparse = s"(${tv.lexeme} :: ${k.unparse})"; + + local attribute hack::QNameLookup; + hack = customLookup("type", getTypeDcl(tv.lexeme, top.env), tv.lexeme, top.location); + + top.typerep = hack.typeScheme.monoType; + top.errors <- hack.errors; + top.errorsTyVars := []; + + top.lexicalTypeVariables <- [tv.lexeme]; + top.lexicalTyVarKinds <- [pair(tv.lexeme, k.kindrep)]; +} action { + insert semantic token IdTypeVar_t at tv.location; +} + +concrete production appTypeExpr +top::TypeExpr ::= ty::TypeExpr tl::BracketedTypeExprs +{ + top.unparse = ty.unparse ++ tl.unparse; + + propagate lexicalTypeVariables; -- Needed to avoid circularity + + forwards to + case ty of + | nominalTypeExpr(q) when + q.lookupType.found && q.lookupType.dcl.isTypeAlias && + length(q.lookupType.typeScheme.boundVars) > 0 -> + aliasAppTypeExpr(q, tl, location=top.location) + | _ -> typeAppTypeExpr(ty, tl, location=top.location) + end; +} + +abstract production aliasAppTypeExpr +top::TypeExpr ::= q::Decorated QNameType with {env} tl::BracketedTypeExprs +{ + top.unparse = q.unparse ++ tl.unparse; + + production ts::PolyType = q.lookupType.typeScheme; + top.typerep = performRenaming(ts.typerep, zipVarsAndTypesIntoSubstitution(ts.boundVars, tl.types)); + + top.mentionedAliases <- q.lookupType.fullName :: q.lookupType.dcl.mentionedAliases; + + local tlCount::Integer = length(tl.types) + tl.missingCount; + top.errors <- + if tlCount != length(ts.boundVars) + then [err(top.location, q.lookupType.fullName ++ " expects " ++ toString(length(ts.boundVars)) ++ " type arguments, but there are " ++ toString(tlCount) ++ " supplied here.")] + else []; + top.errors <- + if tl.missingCount > 0 + then [err(tl.location, q.lookupType.fullName ++ " is a type alias and cannot be partially applied.")] + else []; +} + +abstract production typeAppTypeExpr +top::TypeExpr ::= ty::Decorated TypeExpr tl::BracketedTypeExprs +{ + top.unparse = ty.unparse ++ tl.unparse; + + top.typerep = appTypes(ty.typerep, tl.types); + + top.mentionedAliases <- ty.mentionedAliases; + + top.errors <- ty.errors; + + local tlCount::Integer = length(tl.types) + tl.missingCount; + local tlKinds::[Kind] = map((.kindrep), tl.types); + top.errors <- + if tlCount != length(ty.typerep.kindrep.argKinds) + then [err(top.location, ty.unparse ++ " has kind " ++ prettyKind(ty.typerep.kindrep) ++ ", but there are " ++ toString(tlCount) ++ " type arguments supplied here.")] + else if take(length(tlKinds), ty.typerep.kindrep.argKinds) != tlKinds + then [err(top.location, ty.unparse ++ " has kind " ++ prettyKind(ty.typerep.kindrep) ++ ", but argument(s) have kind(s) " ++ implode(", ", map(prettyKind, tlKinds)))] + else []; + + tl.appArgKinds = + case ty of + | nominalTypeExpr(q) when q.lookupType.found -> q.lookupType.dcl.kindrep.argKinds + | _ -> [] + end; + top.lexicalTyVarKinds <- + case ty of + | typeVariableTypeExpr(tv) -> + -- This assumes that all type args have kind *. + -- If that is not the case, then an explcit kind signature is needed on ty, + -- which will shadow this entry in the lexicalTyVarKinds list. + [pair(tv.lexeme, constructorKind(tlCount))] + + | nominalTypeExpr(q) when q.lookupType.found -> tl.appLexicalTyVarKinds + | _ -> [] + end; +} + +concrete production refTypeExpr +top::TypeExpr ::= 'Decorated' t::TypeExpr 'with' i::TypeExpr +{ + top.unparse = "Decorated " ++ t.unparse ++ " with " ++ i.unparse; + + i.onNt = t.typerep; + + top.typerep = decoratedType(t.typerep, i.typerepInhSet); + + top.errors := i.errorsInhSet ++ t.errors; + top.errors <- + case t.typerep.baseType of + | nonterminalType(_,_,_) -> [] + | skolemType(_) -> [] + | _ -> [err(t.location, t.unparse ++ " is not a nonterminal, and cannot be Decorated.")] + end; + top.errors <- + if i.typerep.kindrep != inhSetKind() + then [err(i.location, s"${i.unparse} has kind ${prettyKind(i.typerep.kindrep)}, but kind InhSet is expected here")] + else []; + top.errors <- t.errorsKindStar; + + top.lexicalTyVarKinds <- + case i of + | typeVariableTypeExpr(tv) -> [pair(tv.lexeme, inhSetKind())] + | _ -> [] + end; +} + +concrete production refDefaultTypeExpr +top::TypeExpr ::= 'Decorated' t::TypeExpr +{ + top.unparse = "Decorated " ++ t.unparse; + + top.typerep = + decoratedType(t.typerep, + inhSetType(sort(concat(getInhsForNtRef(t.typerep.typeName, top.flowEnv))))); + + top.errors <- + case t.typerep.baseType of + | nonterminalType(_,_,_) -> [] + | skolemType(_) -> [err(t.location, "polymorphic Decorated types must specify an explicit reference set")] + | _ -> [err(t.location, t.unparse ++ " is not a nonterminal, and cannot be Decorated.")] + end; +} + +concrete production partialRefTypeExpr +top::TypeExpr ::= 'PartiallyDecorated' t::TypeExpr 'with' i::TypeExpr +{ + top.unparse = "PartiallyDecorated " ++ t.unparse ++ " with " ++ i.unparse; + + i.onNt = t.typerep; + + top.typerep = partiallyDecoratedType(t.typerep, i.typerepInhSet); + + top.errors := i.errorsInhSet ++ t.errors; + top.errors <- + case t.typerep.baseType of + | nonterminalType(_,_,_) -> [] + | skolemType(_) -> [] + | _ -> [err(t.location, t.unparse ++ " is not a nonterminal, and cannot be PartiallyDecorated.")] + end; + top.errors <- + if i.typerep.kindrep != inhSetKind() + then [err(i.location, s"${i.unparse} has kind ${prettyKind(i.typerep.kindrep)}, but kind InhSet is expected here")] + else []; + top.errors <- t.errorsKindStar; + + top.lexicalTyVarKinds <- + case i of + | typeVariableTypeExpr(tv) -> [pair(tv.lexeme, inhSetKind())] + | _ -> [] + end; +} + +concrete production partialRefDefaultTypeExpr +top::TypeExpr ::= 'PartiallyDecorated' t::TypeExpr +{ + top.unparse = "PartiallyDecorated " ++ t.unparse; + + top.typerep = + partiallyDecoratedType(t.typerep, + inhSetType(sort(concat(getInhsForNtRef(t.typerep.typeName, top.flowEnv))))); + + top.errors <- + case t.typerep.baseType of + | nonterminalType(_,_,_) -> [] + | skolemType(_) -> [err(t.location, "polymorphic PartiallyDecorated types must specify an explicit reference set")] + | _ -> [err(t.location, t.unparse ++ " is not a nonterminal, and cannot be PartiallyDecorated.")] + end; +} + +concrete production funTypeExpr +top::TypeExpr ::= '(' sig::Signature ')' +{ + top.unparse = "(" ++ sig.unparse ++ ")"; + top.typerep = sig.typerep; +} + +concrete production signatureEmptyRhs +top::Signature ::= l::SignatureLHS '::=' +{ + top.unparse = l.unparse ++ " ::="; + top.typerep = appTypes(functionType(0, []), case l.maybeType of just(t) -> [t] | nothing() -> [] end); +} + +concrete production psignature +top::Signature ::= l::SignatureLHS '::=' list::TypeExprs +{ + top.unparse = l.unparse ++ " ::= " ++ list.unparse; + top.typerep = + appTypes( + functionType(length(list.types) + list.missingCount, []), + list.types ++ case l.maybeType of just(t) -> [t] | nothing() -> [] end); + + top.errors <- list.errorsKindStar; + top.errors <- + if l.maybeType.isJust && list.missingCount > 0 + then [err($1.location, "Return type cannot be present when argument types are missing")] + else []; +} + +concrete production presentSignatureLhs +top::SignatureLHS ::= t::TypeExpr +{ + top.unparse = t.unparse; + top.maybeType = just(t.typerep); + + top.errors <- t.errorsKindStar; +} + +concrete production missingSignatureLhs +top::SignatureLHS ::= '_' +{ + top.unparse = "_"; + top.maybeType = nothing(); +} + +-- Bracketed Optional Type Lists ----------------------------------------------- + +concrete production botlNone +top::BracketedOptTypeExprs ::= +{ + top.unparse = ""; + forwards to botlSome(bTypeList('<', typeListNone(location=top.location), '>', location=top.location), location=top.location); +} + +concrete production botlSome +top::BracketedOptTypeExprs ::= btl::BracketedTypeExprs +{ + top.unparse = btl.unparse; + top.types = btl.types; + top.missingCount = btl.missingCount; + top.freeVariables = btl.freeVariables; + top.envBindingTyVars = btl.envBindingTyVars; + + btl.initialEnv = top.initialEnv; + btl.appArgKinds = top.appArgKinds; +} + +concrete production bTypeList +top::BracketedTypeExprs ::= '<' tl::TypeExprs '>' +{ + top.unparse = "<" ++ tl.unparse ++ ">"; + + top.types = tl.types; + top.missingCount = tl.missingCount; + + top.freeVariables = tl.freeVariables; + + top.errorsTyVars <- + if length(tl.lexicalTypeVariables) != length(nub(tl.lexicalTypeVariables)) + then [err(top.location, "Type parameter list repeats type variable names")] + else []; + top.errorsTyVars <- + if tl.missingCount > 0 + then [err(top.location, "Type parameter list cannot contain _")] + else []; + + top.envBindingTyVars = + newScopeEnv( + addNewLexicalTyVars(top.grammarName, top.location, tl.lexicalTyVarKinds, tl.lexicalTypeVariables), + top.initialEnv); + + tl.appArgKinds = top.appArgKinds; +} + +-- TypeExprs ------------------------------------------------------------------- + +abstract production typeListNone +top::TypeExprs ::= +{ + top.unparse = ""; + top.types = []; + top.missingCount = 0; + top.freeVariables = []; +} + +concrete production typeListSingle +top::TypeExprs ::= t::TypeExpr +{ + top.unparse = t.unparse; + forwards to typeListCons(t, typeListNone(location=top.location), location=top.location); +} + +concrete production typeListSingleMissing +top::TypeExprs ::= '_' +{ + top.unparse = "_"; + forwards to typeListConsMissing($1, typeListNone(location=top.location), location=top.location); +} + +concrete production typeListCons +top::TypeExprs ::= t::TypeExpr list::TypeExprs +{ + top.unparse = t.unparse ++ " " ++ list.unparse; + top.types = t.typerep :: list.types; + top.missingCount = list.missingCount; + top.freeVariables = t.freeVariables ++ list.freeVariables; + + list.appArgKinds = + case top.appArgKinds of + | [] -> [] + | _ :: t -> t + end; + top.appLexicalTyVarKinds <- + case t, top.appArgKinds of + | typeVariableTypeExpr(tv), k :: _ -> [pair(tv.lexeme, k)] + | _, _ -> [] + end; +} + +concrete production typeListConsMissing +top::TypeExprs ::= '_' list::TypeExprs +{ + top.unparse = "_ " ++ list.unparse; + top.types = list.types; + top.missingCount = list.missingCount + 1; + top.freeVariables = list.freeVariables; + + list.appArgKinds = + case top.appArgKinds of + | [] -> [] + | _ :: t -> t + end; + + top.errors <- + if length(list.types) > 0 + then [err($1.location, "Missing type argument cannot be followed by a provided argument")] + else []; +} diff --git a/grammars/silver/compiler/driver/BuildProcess.sv b/grammars/silver/compiler/driver/BuildProcess.sv new file mode 100644 index 000000000..d19182b0b --- /dev/null +++ b/grammars/silver/compiler/driver/BuildProcess.sv @@ -0,0 +1,216 @@ +grammar silver:compiler:driver; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; + +imports silver:util:cmdargs; + +exports silver:compiler:driver:util; + +type SVParser = (ParseResult ::= String String); + +{-- + - Run the silver compiler, as if invoked from the command line. + -} +function cmdLineRun +IO ::= args::[String] svParser::SVParser +{ + local unit :: IOErrorable = + cmdLineRunInitial(args, svParser); + + return performActions(unit); +} + +-- Compute the environment, and then setup and do a build run. No postOps executed, though. +function cmdLineRunInitial +IOErrorable ::= args::[String] svParser::SVParser +{ + return do { + env::(Decorated CmdArgs, BuildEnv) <- computeEnv(args); + setupBuildRun(svParser, env.1, env.2); + }; +} + +-- Perform the postOps from a cmdLineRunInitial. +function performActions +IO ::= unit::IOErrorable +{ + return do { + res::Either <- unit.run; + case res of + | left(re) -> do { + eprintln(re.message); + return re.code; + } + | right(comp) -> runAll(comp.postOps) + end; + }; +} + +-- Parser args and environment +function computeEnv +IOErrorable<(Decorated CmdArgs, BuildEnv)> ::= args::[String] +{ + return + -- Figure out arguments + case parseArgs(args) of + | left(argErrors) -> throwRunError(1, argErrors) + | right(a) -> do { + -- Figure out build env from environment and args + benv::BuildEnv <- determineBuildEnv(a); + -- Because we want printing the version to work even if the environment is messed up + -- we premptively handle that here. This is slightly unfortunate. + -- Ideally, version printing would be just another thing we could have the command + -- line decide to go do, but currently it's hard to re-use code if we do that. + if a.displayVersion then + throwRunError(127, -- error code so 'ant' isnt run + "Silver Version 0.4.5-dev\n" ++ + "SILVER_HOME = " ++ benv.silverHome ++ "\n" ++ + "SILVER_GEN = " ++ benv.silverGen ++ "\n" ++ + "GRAMMAR_PATH:\n" ++ implode("\n", benv.grammarPath)) + else pure((a, benv)); + } + end; +} + +-- Upon deciding that we're to build one or more grammars into a jar, we do this +function setupBuildRun +IOErrorable ::= + svParser::SVParser + a::Decorated CmdArgs + benv::BuildEnv +{ + return do { + -- Check environment stuff specific to building a grammar + checkbuild::[String] <- lift(checkPreBuild(benv, a.buildGrammars)); + when_(!null(checkbuild), throwRunError(1, implode("\n", checkbuild))); + + -- Build! + buildrun :: Decorated Compilation <- lift(buildRun(svParser, a, benv, a.buildGrammars)); + let missingGrammars::[String] = + removeAll(map((.declaredName), buildrun.grammarList), a.buildGrammars); + when_(!null(missingGrammars), + throwRunError(1, "The specified grammar(s) " ++ implode(", ", missingGrammars) ++ " could not be found.\n")); + + return buildrun; + }; +} + +{-- + - Given an environment and a grammar to build, returns a Compilation. + - Note that it's the caller's responsibility to actually evaluate that + - compilation's actions. + -} +function buildRun +IO ::= + svParser::SVParser + a::Decorated CmdArgs + benv::BuildEnv + buildGrammars::[String] +{ + return mdo { + -- Compile grammars. There's some tricky circular program data flow here. + -- This does an "initial grammar stream" composed of + -- grammars and interface files that *locally* seem good. + rootStream :: [Maybe] <- + unsafeInterleaveIO(compileGrammars(svParser, benv, grammarStream, a.doClean)); + + -- The list of grammars to build. This is circular with the above, producing + -- a list that's terminated when the response count is equal to the number of emitted + -- grammar names. + let grammarStream :: [String] = + buildGrammars ++ + eatGrammars(mentionedGrammars, length(buildGrammars), buildGrammars, rootStream, unit.grammarList); + + -- This is, essentially, a data structure representing a compilation. + -- Note that it is pure: it doesn't take any actions. + let unit :: Decorated Compilation = + decorate + compilation( + foldr(consGrammars, nilGrammars(), catMaybes(rootStream)), + foldr(consGrammars, nilGrammars(), catMaybes(reRootStream)), + buildGrammars, benv) + with { + -- This is something we should probably get rid of, someday. Somehow. It's hard. + config = a; + }; + + -- There is a second circularity here where we use unit.recompiledGrammars + -- to supply the second parameter to unit. + reRootStream :: [Maybe] <- + unsafeInterleaveIO(compileGrammars(svParser, benv, reGrammarStream, true)); + + let reGrammarStream :: [String] = + eatGrammars( + (.dirtyGrammars), length(unit.initRecompiledGrammars), map((.declaredName), unit.initRecompiledGrammars), + map(compose(just, new), unit.initRecompiledGrammars) ++ reRootStream, unit.recompiledGrammars); + + return unit; + }; +} + +{-- + - Eat the stream `need` and produce the output stream of (maybe, if found) `RootSpec`s. + - + - @param benv The compiler configuration, including search paths + - @param need A **stream** of grammars to compile. + - @param clean If true, ignore interface files entirely. + -} +function compileGrammars +IO<[Maybe]> ::= + svParser::SVParser + benv::BuildEnv + need::[String] + clean::Boolean +{ + return traverseA(\ g::String -> compileGrammar(svParser, benv, g, clean).run, need); +} + +{-- + - Consumes a stream of parses, outputs a stream of new dependencies. + - Typically used as a circular program with 'compileGrammars' + - + - @param triggered A function returning a list of grammars that sould be triggered by a grammar + - @param n Expected number of new inputs from rootStream + - @param sofar Set of grammars already seen, and should not be requested again + - @param rootStream Stream of found/not found info. Should not be used except to test presence + - @param grammars List of grammars *in the same order as 'just' appears in rootStream* + - @return A stream of new dependencies + -} +function eatGrammars +[String] ::= triggered::([String] ::= Decorated RootSpec) n::Integer sofar::[String] rootStream::[Maybe] grammars::[Decorated RootSpec] +{ + local it :: Decorated RootSpec = head(grammars); + local directDeps :: [String] = triggered(it); + + local newDeps :: [String] = removeAll(sofar, directDeps); + + return + if n == 0 then + [] + else if !head(rootStream).isJust then + eatGrammars(triggered, n-1, sofar, tail(rootStream), grammars) + else + newDeps ++ eatGrammars(triggered, n-1+length(newDeps), newDeps ++ sofar, tail(rootStream), tail(grammars)); +} + +synthesized attribute code::Integer; + +nonterminal RunError with code, message; +-- from silver:langutil, and silver:compiler:driver:util; + +abstract production runError +top::RunError ::= c::Integer m::String +{ + top.code = c; + top.message = m; +} + +-- A common return type for IO functions. Does IO and returns error or whatever. +type IOErrorable = EitherT; + +function throwRunError +IOErrorable ::= c::Integer m::String +{ + return throwError(runError(c, m)); +} diff --git a/grammars/silver/compiler/driver/Command.sv b/grammars/silver/compiler/driver/Command.sv new file mode 100644 index 000000000..367c8bce8 --- /dev/null +++ b/grammars/silver/compiler/driver/Command.sv @@ -0,0 +1,278 @@ +grammar silver:compiler:driver; + +attribute genLocation, doClean, displayVersion, warnError, forceOrigins, noOrigins, noRedex, tracingOrigins, searchPath, outName, buildGrammars, silverHomeOption, noBindingChecking occurs on CmdArgs; + +synthesized attribute searchPath :: [String]; +synthesized attribute outName :: [String]; +synthesized attribute genLocation :: [String]; +synthesized attribute silverHomeOption :: [String]; + +synthesized attribute displayVersion :: Boolean; +synthesized attribute doClean :: Boolean; +synthesized attribute warnError :: Boolean; +synthesized attribute forceOrigins :: Boolean; +synthesized attribute noOrigins :: Boolean; +synthesized attribute noRedex :: Boolean; +synthesized attribute tracingOrigins :: Boolean; + +synthesized attribute buildGrammars :: [String]; + +synthesized attribute noBindingChecking :: Boolean; + +aspect production endCmdArgs +top::CmdArgs ::= l::[String] +{ + top.doClean = false; + top.displayVersion = false; + top.warnError = false; + top.outName = []; + top.searchPath = []; + top.genLocation = []; + top.silverHomeOption = []; + top.buildGrammars = l; + top.noBindingChecking = false; + top.forceOrigins = false; + top.noOrigins = false; + top.noRedex = false; + top.tracingOrigins = false; +} +abstract production versionFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.displayVersion = true; + forwards to rest; +} +abstract production cleanFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.doClean = true; + forwards to rest; +} +abstract production warnErrorFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.warnError = true; + forwards to rest; +} +abstract production forceOriginsFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.forceOrigins = true; + forwards to rest; +} +abstract production noOriginsFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.noOrigins = true; + forwards to rest; +} +abstract production tracingOriginsFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.tracingOrigins = true; + forwards to rest; +} +abstract production noRedexFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.noRedex = true; + forwards to rest; +} +abstract production outFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.outName = s :: forward.outName; + forwards to rest; +} +abstract production includeFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.searchPath = s :: forward.searchPath; + forwards to rest; +} +abstract production genFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.genLocation = s :: forward.genLocation; + forwards to rest; +} +abstract production homeFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.silverHomeOption = s :: forward.silverHomeOption; + forwards to rest; +} +abstract production nobindingFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.noBindingChecking = true; + forwards to rest; +} + +function parseArgs +Either ::= args::[String] +{ + production attribute flags::[FlagSpec] with ++; + flags := []; + + -- General rules of thumb: + -- Use -- as your prefix + -- Unless it's an OPTION, and it's commonly used, and it's obvious from context what it means + -- e.g. -I my/grammars is obvious because it refers to a location to include. + + flags <- + [ flagSpec(name="-I", paramString=just(""), + help="path to grammars (GRAMMAR_PATH)", + flagParser=option(includeFlag)) + , flagSpec(name="-o", paramString=just(""), + help="name of binary file", + flagParser=option(outFlag)) + , flagSpec(name="-G", paramString=just(""), + help="location to store generate files (SILVER_GEN)", + flagParser=option(genFlag)) + , flagSpec(name="--silver-home", paramString=nothing(), + help="set the location of the silver repo (SILVER_HOME)", + flagParser=option(homeFlag)) + , flagSpec(name="--version", paramString=nothing(), + help="display version", + flagParser=flag(versionFlag)) + , flagSpec(name="--clean", paramString=nothing(), + help="overwrite interface files", + flagParser=flag(cleanFlag)) + , flagSpec(name="--dont-analyze", paramString=nothing(), + help="", -- TODO + flagParser=flag(nobindingFlag)) + , flagSpec(name="--warn-error", paramString=nothing(), + help="treat warnings as errors", + flagParser=flag(warnErrorFlag)) + , flagSpec(name="--no-origins", paramString=nothing(), + help="treat all nonterminals as un`tracked`", + flagParser=flag(noOriginsFlag)) + , flagSpec(name="--force-origins", paramString=nothing(), + help="treat all nonterminals as tracked", + flagParser=flag(forceOriginsFlag)) + , flagSpec(name="--no-redex", paramString=nothing(), + help="do not collect redex information", + flagParser=flag(noRedexFlag)) + , flagSpec(name="--tracing-origins", paramString=nothing(), + help="attach source locations as origin notes to trace control flow", + flagParser=flag(tracingOriginsFlag)) + ]; + + local usage :: String = + s"Usage: silver [options] [grammar:to:build ...]\n\nFlag options:\n" ++ + flagSpecsToHelpText(flags); + + -- Parse the command line + local cmdArgs :: CmdArgs = interpretCmdArgs(flags, args); + + production attribute errors :: [String] with ++; + errors := if cmdArgs.cmdError.isJust then [cmdArgs.cmdError.fromJust] else []; + + errors <- + if length(cmdArgs.outName) > 1 then ["Multiple options given for -o flag: " ++ implode(" ", cmdArgs.outName)] + else if length(cmdArgs.genLocation) > 1 then ["Multiple options given for -G flag: " ++ implode(" ", cmdArgs.genLocation)] + else if length(cmdArgs.silverHomeOption) > 1 then ["Multiple options given for --silver-home flag: " ++ implode(" ", cmdArgs.silverHomeOption)] + else if cmdArgs.noOrigins && cmdArgs.forceOrigins then ["Can't specify --no-origins and --force-origins"] + else []; + + return if !null(errors) + then left(implode("\n", errors) ++ "\n\n" ++ usage) + else right(cmdArgs); +} + +function parseArgsOrError +Decorated CmdArgs ::= args::[String] +{ + return + case parseArgs(args) of + | left(msg) -> error("Failed to parse args: " ++ msg) + | right(a) -> a + end; +} + +function determineBuildEnv +IOErrorable ::= a::Decorated CmdArgs +{ + return do { + benv :: BuildEnv <- lift(do { + -- Let's locally set up and verify the environment + envSH :: String <- envVar("SILVER_HOME"); + envGP :: String <- envVar("GRAMMAR_PATH"); + envSHG :: String <- envVar("SILVER_HOST_GEN"); + envSG :: String <- envVar("SILVER_GEN"); + + -- If SILVER_HOME isn't set, determine it from where this jar is + derivedSH :: String <- + if envSH == "" then + stateIO(determineDefaultSilverHome) + else pure(envSH); + + return + fromArgsAndEnv( + -- TODO: maybe we should use the java platform separator here? + derivedSH, envSG, explode(":", envGP), explode(":", envSHG), + a.silverHomeOption, a.genLocation, a.searchPath); + }); + + -- Let's do some checks on the environment + checkenv :: [String] <- lift(checkEnvironment(benv)); + unless(null(checkenv), throwRunError(1, implode("\n", checkenv))); + + return benv; + }; +} + +function checkEnvironment +IO<[String]> ::= benv::BuildEnv +{ + return do { + isGenDir :: Boolean <- isDirectory(benv.silverGen); + isGramDir :: Boolean <- isDirectory(benv.defaultGrammarPath); + + return + if benv.silverHome == "/" -- because we called 'endWithSlash' on empty string + then ["Missing SILVER_HOME or --silver-home .\nThis should have been set up by the 'silver' script.\n"] + else if !isGenDir + then if benv.silverGen == benv.defaultSilverGen + then ["Missing SILVER_GEN or -G .\nThis should have been inferable, but " ++ benv.silverGen ++ " is not a directory.\n"] + else ["Supplied SILVER_GEN location " ++ benv.silverGen ++ " is not a directory.\n"] + else if !isGramDir + then ["Missing standard library grammars: tried " ++ benv.defaultGrammarPath ++ " but this did not exist.\n"] + else []; + -- TODO: We should probably check everything in grammarPath? + -- TODO: Maybe look for 'core' specifically? + }; +} + +function checkPreBuild +IO<[String]> ::= + benv::BuildEnv + buildGrammars::[String] +{ + return pure( + if null(buildGrammars) then ["No grammar(s) to build were specified.\n"] + else flatMap(\ buildGrammar::String -> + if indexOf("/", buildGrammar) != -1 -- basic sanity check + then ["Build grammar appears to contain slashes: " ++ buildGrammar ++ "\n"] + else if indexOf(".", buildGrammar) != -1 -- also, now + then ["Build grammar appears to contain dots: " ++ buildGrammar ++ "\n"] + else [], + buildGrammars)); + -- TODO: presently, we check whether we find this grammar elsewhere. Maybe it should be here? not sure. +} + +-- This code has to live in the generated jar for the program, as putting it in the +-- standard library may someday return the location of the standard library jar instead +-- of us +function determineDefaultSilverHome +IOVal ::= i::IOToken +{ + return error("NYI"); +} foreign { + -- This grabs the path to this jar (using Init.class as the thing to find the path to) + -- Then goes up two levels (HOME/jars/file.jar to HOME) and returns that. + -- If anything goes wrong, we crash. + "java" : return "new silver.core.Pioval(%i%, common.Util.determineSilverHomePath(Init.class))"; +} + diff --git a/grammars/silver/compiler/driver/CompileFiles.sv b/grammars/silver/compiler/driver/CompileFiles.sv new file mode 100644 index 000000000..c09e48635 --- /dev/null +++ b/grammars/silver/compiler/driver/CompileFiles.sv @@ -0,0 +1,32 @@ +grammar silver:compiler:driver; + +{-- + - Parses a list of files. + - @param svParser The parser to use to contruct Roots + - @param gpath The path where we found the grammar. Ends in a slash/ + - @param files The list of .sv files to read. + - @return An IO action constructing the list of parse results and parse errors. + -} +function compileFiles +IO<([Root], [ParseError])> ::= svParser::SVParser gpath::String files::[String] +{ + return + case files of + | file :: rest -> do { + rawText :: String <- readFile(gpath ++ file); + let text :: String = transformFile(file, rawText); + + -- This is where a .sv file actually gets parsed: + let r :: ParseResult = svParser(text, file); + + -- Continue parsing the rest of the files. + recurse :: ([Root], [ParseError]) <- compileFiles(svParser, gpath, rest); + return + case r of + | parseSucceeded(rtree, _) -> (rtree :: recurse.1, recurse.2) + | parseFailed(errval, _) -> (recurse.1, errval :: recurse.2) + end; + } + | [] -> pure(([], [])) + end; +} diff --git a/grammars/silver/compiler/driver/CompileGrammar.sv b/grammars/silver/compiler/driver/CompileGrammar.sv new file mode 100644 index 000000000..473abf65c --- /dev/null +++ b/grammars/silver/compiler/driver/CompileGrammar.sv @@ -0,0 +1,152 @@ +grammar silver:compiler:driver; + +import silver:reflect:nativeserialize; + +{-- + - Hunts down a grammar and obtains its symbols, either by building or from an interface file. + -} +function compileGrammar +MaybeT ::= + svParser::SVParser + benv::BuildEnv + grammarName::String + clean::Boolean +{ + local gramPath :: String = grammarToPath(grammarName); + + return do { + findGrammar::Maybe<(Integer, String, [String])> <- lift(do { + -- IO Step 1: Look for the grammar's source files + grammarLocation :: String <- findGrammarLocation(gramPath, benv.grammarPath); + + -- IO Step 2: List those files, and obtain their newest modification time + files :: [String] <- lift(listSilverFiles(grammarLocation)); + when_(null(files), empty); -- Grammar had no files! + grammarTime :: Integer <- lift(fileTimes(grammarLocation, files)); + + return (grammarTime, grammarLocation, files); + }.run); + alt( + -- IO Step 3: Let's look for a valid interface file + if clean + then empty -- We just skip this search if it's a clean build + else compileInterface(grammarName, benv.silverHostGen, map(fst, findGrammar)), + do { + -- We didn't find a valid interface file + foundGrammar::(Integer, String, [String]) <- maybeT(pure(findGrammar)); + let grammarTime::Integer = foundGrammar.1; + let grammarLocation::String = foundGrammar.2; + let files::[String] = foundGrammar.3; + + -- IO Step 4: Build the grammar, and say so + lift(eprintln("Compiling " ++ grammarName ++ "\n\t[" ++ grammarLocation ++ "]\n\t[" ++ renderFileNames(files, 0) ++ "]")); + gramCompile::([Root], [ParseError]) <- lift(compileFiles(svParser, grammarLocation, files)); + + -- IO Step 5: Check for an old interface file, to tell if we need to transitively re-translate + oldInterface::Maybe <- lift(do { + gen :: String <- findInterfaceLocation(gramPath, benv.silverHostGen); + let file :: String = gen ++ "src/" ++ gramPath ++ "Silver.svi"; + --lift(eprintln(s"Found old interface ${file}")); + content::ByteArray <- lift(readBinaryFile(file)); + case nativeDeserialize(content) of + | left(msg) -> empty + | right(ii) -> pure(ii) + end; + }.run); + + return if null(gramCompile.2) + then grammarRootSpec(foldRoot(gramCompile.1), oldInterface, grammarName, grammarLocation, grammarTime, benv.silverGen) + else errorRootSpec(gramCompile.2, grammarName, grammarLocation, grammarTime, benv.silverGen); + }); + }; +} + +function foldRoot +Grammar ::= l::[Root] +{ + return foldr(consGrammar, nilGrammar(), l); +} + +{-- + - Determined whether a file name should be considered a Silver source file. + -} +function isValidSilverFile +Boolean ::= f::String +{ + return any(map(endsWith(_, f), allowedSilverFileExtensions)) && !startsWith(".", f); +} +function listSilverFiles +IO<[String]> ::= dir::String +{ + return do { + files :: [String] <- listContents(dir); + return filter(isValidSilverFile, files); + }; +} + +{-- + - Determines the maximum modification time of all files in a directory. + - Including the directory itself, to detect file deletions. + -} +function fileTimes +IO ::= dir::String is::[String] +{ + return + case is of + | [] -> fileTime(dir) -- check the directory itself. Catches deleted files. + | h :: t -> do { + ft :: Integer <- fileTime(dir ++ h); + rest :: Integer <- fileTimes(dir, t); + return max(ft, rest); + } + end; +} + +-- A crude approximation of line wrapping +function renderFileNames +String ::= files::[String] depth::Integer +{ + return + if null(files) then "" else + if depth >= 7 then "\n\t " ++ renderFileNames(files, 0) else + head(files) ++ + if null(tail(files)) then "" else " " ++ renderFileNames(tail(files), depth + 1); +} + +{-- + - Takes a grammar name (already converted to a path) and searches the grammar + - path for the first directory that matches. + -} +function findGrammarLocation +MaybeT ::= path::String searchPaths::[String] +{ + return + case searchPaths of + | h :: t -> alt(findGrammarInLocation(path, h), findGrammarLocation(path, t)) + | [] -> empty + end; +} + +{-- + - Looks to see if the grammar can be found in 'inPath' + - Tries (in order) for edu:umn:cs + - edu/umn/cs/ + - edu.umn/cs/ + - edu.umn.cs/ + -} +function findGrammarInLocation +MaybeT ::= gram::String inPath::String +{ + -- Find the first / in the grammar name (turned path) we're looking for. + local idx :: Integer = indexOf("/", gram); + + -- Replace the first / with a . + local nextGram :: String = substring(0, idx, gram) ++ "." ++ substring(idx + 1, length(gram), gram); + + return do { + exists :: Boolean <- lift(isDirectory(inPath ++ gram)); + if exists then pure(inPath ++ gram) + else if idx == -1 then empty + else findGrammarInLocation(nextGram, inPath); + }; +} diff --git a/grammars/silver/compiler/driver/CompileInterface.sv b/grammars/silver/compiler/driver/CompileInterface.sv new file mode 100644 index 000000000..4c477303b --- /dev/null +++ b/grammars/silver/compiler/driver/CompileInterface.sv @@ -0,0 +1,68 @@ +grammar silver:compiler:driver; + +import silver:reflect; +import silver:reflect:nativeserialize; + +{-- + - Find an interface file, if it exists, and it's valid (parsable and modification time is newer). + - + - @param grammarName The grammar we're looking for an interface file for + - @param silverHostGen The search path to look for interface files within + - @param grammarTime The newest modification time of the source files, to compare against + -} +function compileInterface +MaybeT ::= grammarName::String silverHostGen::[String] grammarTime::Maybe +{ + local gramPath :: String = grammarToPath(grammarName); + + return do { + -- IO Step 1: Find the interface file, if any + gen :: String <- findInterfaceLocation(gramPath, silverHostGen); + let file :: String = gen ++ "src/" ++ gramPath ++ "Silver.svi"; + + -- IO Step 2: Let's say so, and parse it + lift(eprintln("Found " ++ grammarName ++ "\n\t[" ++ file ++ "]")); + text :: ByteArray <- lift(readBinaryFile(file)); + let ir :: Either = nativeDeserialize(text); + + -- IO Step 3: Perhaps complain it failed to deserialize + case ir of + | left(msg) -> + do { + lift(eprintln( + "\n\tFailed to deserialize interface file!\n" ++ msg ++ + "\n\tRecovering by parsing grammar....")); + empty; + } + | right(i) when !null(i.interfaceErrors) -> + do { + lift(eprintln( + "\n\tErrors unpacking interface file:\n " ++ implode("\n ", i.interfaceErrors) ++ + "\n\tRecovering by parsing grammar....")); + empty; + } + | right(i) -> + case grammarTime of + -- Fail if the grammar sources are newer than the ones used to build the interface file + | just(t) when t > i.maybeGrammarTime.fromJust -> empty + | _ -> pure(interfaceRootSpec(i, gen)) + end + end; + }; +} + +{-- + - Takes a grammar name (already converted to a path) and searches for Silver.svi + -} +function findInterfaceLocation +MaybeT ::= gramPath::String searchPaths::[String] +{ + return + case searchPaths of + | [] -> empty + | h :: t -> do { + exists :: Boolean <- lift(isFile(h ++ "src/" ++ gramPath ++ "Silver.svi")); + if exists then pure(h) else findInterfaceLocation(gramPath, t); + } + end; +} diff --git a/grammars/silver/compiler/driver/CompileLiterateFile.sv.md b/grammars/silver/compiler/driver/CompileLiterateFile.sv.md new file mode 100644 index 000000000..24ef3c69b --- /dev/null +++ b/grammars/silver/compiler/driver/CompileLiterateFile.sv.md @@ -0,0 +1,95 @@ +This file implements the helpers for literate programming in Silver, and serves as a test-case for it as well. + +## Pre-processing + +The transformFile function here implements whatever preprocessing needs to be done on a certain file. + +```silver +function transformFile +String ::= path::String contents::String +{ + return +``` + +If the file is a normal Silver file, we return the contents unchanged. + +```silver + if endsWith(".sv", path) || endsWith(".ag", path) + then contents +``` + +If it's a Literate Silver file instead, we extract the relevant code blocks, then join them with newlines. + +```silver + else if endsWith(".sv.md", path) || endsWith(".ag.md", path) + then implode("\n", extractSilverCodeBlocks(path, contents)) +``` + +Since these are the only extensions allowed by `isValidSilverFile` in `CompileGrammar.sv`, they're the only ones we need to handle. + +```silver + else error("Unknown filetype for " ++ path); +} +``` + +## Literate Silver + +The preprocessing for Literate Silver files is just a matter of extracting and concatenating blocks of Silver code. +The CommonMark spec notes that commonly only the first word is used as the language; all the ones we recognize start with `silver` as a result. + +In the future, we may want to support info strings like `silver test`, `silver wrongCode`, etc. +See [rustdoc](https://doc.rust-lang.org/rustdoc/documentation-tests.html#attributes) for prior art. + + +```silver +function extractSilverCodeBlocks +[String] ::= path::String markdown::String +{ + local allCodeBlocks::[(String, Location, String)] = extractCodeBlocks(path, markdown); +``` + +The simple case is blocks whose info string is *exactly* `silver`. +These are replicated verbatim, after adjusting the line number so locations match. + +```silver + local goodCode::[String] = + map(\block::(String, Location, String) -> "#line " ++ toString(block.2.line + 1) ++ "\n" ++ block.3, + filter(\block::(String, Location, String) -> block.1 == "silver", allCodeBlocks)); +``` + +An info string of `silver imports` causes the code block to be hoisted to the top; this is useful because imports need to be at the top of the file, but we might want to only show an import later on in a document, to avoid scaring the reader off with a large block of imports at the top. + +```silver + local importsCode::[String] = + map(\block::(String, Location, String) -> "#line " ++ toString(block.2.line + 1) ++ "\n" ++ block.3, + filter(\block::(String, Location, String) -> block.1 == "silver imports", allCodeBlocks)); +``` + +We also emit warnings for code blocks with no info string. + +```silver + local warnCode::[String] = + map(\block::(String, Location, String) -> "#line " ++ toString(block.2.line + 1) ++ "\n" + ++ "#warn Code block with no info string; this won't be compiled", + filter(\block::(String, Location, String) -> block.1 == "", allCodeBlocks)); +``` + +Since we use `#line` to set the line numbers on the parsed trees, we don't need to do any fancy interleaving or anything before returning all the blocks. + +```silver + return importsCode ++ goodCode ++ warnCode; +} +``` + +We do the actual extraction with the [commonmark-java](https://github.com/commonmark/commonmark-java) library. +The actual conversion is in `Markdown.java` in the runtime; see there for the fine details. + +```silver +function extractCodeBlocks +[(String, Location, String)] ::= path::String markdown::String +{ + return []; +} foreign { + "java" : return "common.Markdown.extractCodeBlocks(%path%.toString(), %markdown%.toString())"; +} +``` diff --git a/grammars/silver/compiler/driver/DriverAction.sv b/grammars/silver/compiler/driver/DriverAction.sv new file mode 100644 index 000000000..43cbd666d --- /dev/null +++ b/grammars/silver/compiler/driver/DriverAction.sv @@ -0,0 +1,102 @@ +grammar silver:compiler:driver; + +{- + - A short guide to error codes: + - Negative = configuration/installation error + - 1-19 = command line/start up error + - 20+ = Normal use error (errors in spec) + - 127 = "abnormal" success (e.g. printed version string, quit now) + - 0 = success of course + -} + +{- Orders: + - 0: parsing errors (also flow graph emitting) + - 1: binding errors + - 3: interfaces and classes + - 4: doc + - 5: copper_mda + - 6: buildxml + - 7: impide + -} + +aspect production compilation +top::Compilation ::= g::Grammars r::Grammars buildGrammars::[String] benv::BuildEnv +{ + top.postOps <- [printAllParsingErrors(top.allGrammars)]; + top.postOps <- if top.config.noBindingChecking then [] else + [printAllBindingErrors(top.allGrammars)]; + top.postOps <- [touchIfaces(r.grammarList, benv.silverGen)]; +} + +abstract production touchIfaces +top::DriverAction ::= r::[Decorated RootSpec] genPath::String +{ + top.run = do { touchFiles(map(sviPath(_, genPath), r)); return 0; }; + top.order = 3; +} +function sviPath +String ::= r::Decorated RootSpec genPath::String +{ + return genPath ++ "src/" ++ grammarToPath(r.declaredName) ++ "Silver.svi"; +} + +abstract production printAllBindingErrors +top::DriverAction ::= specs::[Decorated RootSpec] +{ + -- Force printing of status before doing error checks + top.run = do { + eprintln("Checking For Errors."); + forward.run; + }; + + forwards to printAllBindingErrorsHelp(specs); +} + +abstract production printAllBindingErrorsHelp +top::DriverAction ::= specs::[Decorated RootSpec] +{ + top.run = + case specs of + | [] -> pure(0) + | spec :: rest -> do { + unless(null(spec.grammarErrors), + eprintln("Errors for " ++ spec.declaredName ++ "\n" ++ flatMap(renderMessages(spec.grammarSource, _), spec.grammarErrors))); + let containsErrors :: Boolean = grammarContainsErrors(spec.grammarErrors, spec.config.warnError); + recurse :: Integer <- printAllBindingErrorsHelp(rest).run; + return if containsErrors || recurse != 0 then 20 else 0; + } + end; + + top.order = 1; +} + +abstract production printAllParsingErrors +top::DriverAction ::= specs::[Decorated RootSpec] +{ + top.run = + case specs of + | [] -> pure(0) + | spec :: rest -> do { + unless(null(spec.parsingErrors), + eprintln("Errors for " ++ spec.declaredName ++ "\n" ++ flatMap(renderMessages(spec.grammarSource, _), spec.parsingErrors))); + recurse :: Integer <- printAllParsingErrors(rest).run; + return if !null(spec.parsingErrors) || recurse != 0 then 21 else 0; + } + end; + + top.order = 0; +} + +function renderMessages +String ::= grammarSource::String msg::Pair +{ + return " [" ++ grammarSource ++ msg.fst ++ "]\n" ++ messagesToString(msg.snd) ++ "\n"; +} + +function grammarContainsErrors +Boolean ::= es::[Pair] werr::Boolean +{ + return if null(es) then false + else containsErrors(head(es).snd, werr) || grammarContainsErrors(tail(es), werr); +} + diff --git a/grammars/silver/driver/util/BuildEnv.sv b/grammars/silver/compiler/driver/util/BuildEnv.sv similarity index 98% rename from grammars/silver/driver/util/BuildEnv.sv rename to grammars/silver/compiler/driver/util/BuildEnv.sv index 1c7c6d63b..396f1272b 100644 --- a/grammars/silver/driver/util/BuildEnv.sv +++ b/grammars/silver/compiler/driver/util/BuildEnv.sv @@ -1,4 +1,4 @@ -grammar silver:driver:util; +grammar silver:compiler:driver:util; nonterminal BuildEnv with silverHome, silverGen, grammarPath, silverHostGen, defaultSilverGen, defaultGrammarPath; diff --git a/grammars/silver/compiler/driver/util/Compilation.sv b/grammars/silver/compiler/driver/util/Compilation.sv new file mode 100644 index 000000000..d56baf58b --- /dev/null +++ b/grammars/silver/compiler/driver/util/Compilation.sv @@ -0,0 +1,110 @@ +grammar silver:compiler:driver:util; + +import silver:compiler:definition:core only jarName, grammarErrors; +import silver:util:treemap as map; + +synthesized attribute initRecompiledGrammars::[Decorated RootSpec]; + +nonterminal Compilation with config, postOps, grammarList, allGrammars, initRecompiledGrammars, recompiledGrammars; + +flowtype postOps {config} on Compilation; + +synthesized attribute postOps :: [DriverAction] with ++; +synthesized attribute grammarList :: [Decorated RootSpec]; +synthesized attribute allGrammars :: [Decorated RootSpec]; + +{-- + - This abstractly represents a compilation. + - Note, in particular, that this does not *DO* any IO at all itself. + - + - However, it does expect some "flow": + - * 'g' should be examined, and then 'top.dirtyGrammars' used to provide 'r' + - + - @param g A list of grammar initially read in + - @param r A list of grammars that we re-compiled, due to dirtiness in 'g' + - @param buildGrammars The initial grammars requested built + - @param benv The build configuration + -} +abstract production compilation +top::Compilation ::= g::Grammars r::Grammars buildGrammars::[String] benv::BuildEnv +{ + -- the list of rootspecs coming out of g + top.grammarList = g.grammarList; + -- all compiled rootspecs from g and r + top.allGrammars = g.grammarList ++ r.grammarList; + -- the initial list of rootspecs from g that were re-compiled + top.initRecompiledGrammars = keepGrammars(grammarsDependedUpon, g.recompiledGrammars); + -- the list of re-compiled rootspecs from g and r + top.recompiledGrammars := top.initRecompiledGrammars ++ r.grammarList; + + -- All grammars that were compiled due to being dirty or dependencies of dirty grammars + -- see all the other initially compiled grammars. + g.compiledGrammars = directBuildTree(map(\ r::Decorated RootSpec -> (r.declaredName, r), g.grammarList)); + -- However, since we don't initially know all the grammars we are going to recheck, + -- we are forced to start with the interface files that we are going to + -- recheck in the .compiledGrammars for the recheck. + r.compiledGrammars = g.compiledGrammars; + -- Since we never compile a grammar more than once, this means in case of mutual dependencies + -- between grammars, the initially-compiled grammar may see an outdated interface file. + -- See https://github.com/melt-umn/silver/issues/673 + + g.dependentGrammars = flatMap( + \ r::Decorated RootSpec -> map(\ g::String -> (g, r.declaredName), r.allGrammarDependencies), + grammarsRelevant); + -- See above comments. + -- Assumption: if a grammar has an up-to-date interface file, then its dependencies are unchanged. + r.dependentGrammars = g.dependentGrammars; + + -- This determines what is actually needed in this build. + -- For example, it excludes "options" and conditional builds that aren't + -- actually used / triggered. + production grammarsDependedUpon :: [String] = + expandAllDeps(buildGrammars, [], g.compiledGrammars); + + -- Ditto the above, but rootspecs + production grammarsRelevant :: [Decorated RootSpec] = + keepGrammars(grammarsDependedUpon, g.grammarList); + + -- The grammars that we have recompiled, that need to be translated + production grammarsToTranslate :: [Decorated RootSpec] = top.recompiledGrammars; + + local rGrammarNames :: [String] = map((.declaredName), r.grammarList); + -- All grammars from g and r, excluding interface files from r that were later recompiled + production allLatestGrammars :: [Decorated RootSpec] = + r.grammarList ++ + filter(\ rs::Decorated RootSpec -> !contains(rs.declaredName, rGrammarNames), g.grammarList); + + top.postOps := []; +} + +nonterminal Grammars with config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, dependentGrammars, grammarList, dirtyGrammars, recompiledGrammars, jarName; + +propagate dirtyGrammars, recompiledGrammars, jarName, dependentGrammars on Grammars; + +abstract production consGrammars +top::Grammars ::= h::RootSpec t::Grammars +{ + top.grammarList = h :: t.grammarList; + + -- Once we have compiled a grammar, replace the interface file rootspec when compiling dependent grammars + h.compiledGrammars = map:update(h.declaredName, [h], top.compiledGrammars); + t.compiledGrammars = h.compiledGrammars; +} + +abstract production nilGrammars +top::Grammars ::= +{ + top.grammarList = []; +} + +{-- + - Keep only a selected set of grammars. + - @param keep The set of grammars to keep + - @param d The list of grammars to filter + -} +function keepGrammars +[Decorated RootSpec] ::= keep::[String] d::[Decorated RootSpec] +{ + return filter(\ r::Decorated RootSpec -> contains(r.declaredName, keep), d); +} + diff --git a/grammars/silver/compiler/driver/util/DriverAction.sv b/grammars/silver/compiler/driver/util/DriverAction.sv new file mode 100644 index 000000000..2ff794b0c --- /dev/null +++ b/grammars/silver/compiler/driver/util/DriverAction.sv @@ -0,0 +1,18 @@ +grammar silver:compiler:driver:util; + +closed nonterminal DriverAction with config, order, run>; + +synthesized attribute order :: Integer; + +{-- + - Run actions in order until a non-zero error code is encountered. + -} +function runAll +IO ::= l::[DriverAction] +{ + return foldl( + \ prev::IO a::DriverAction -> + bind(prev, \ code::Integer -> if code != 0 then pure(code) else a.run), + pure(0), + sortBy(\ a::DriverAction b::DriverAction -> a.order <= b.order, l)); +} diff --git a/grammars/silver/compiler/driver/util/FlowTypes.sv b/grammars/silver/compiler/driver/util/FlowTypes.sv new file mode 100644 index 000000000..dce3b61e1 --- /dev/null +++ b/grammars/silver/compiler/driver/util/FlowTypes.sv @@ -0,0 +1,61 @@ +grammar silver:compiler:driver:util; + +import silver:compiler:definition:flow:driver; +import silver:compiler:definition:flow:ast; +import silver:compiler:definition:flow:env; +import silver:util:treemap as rtm; +import silver:util:graph as g; + +-- Hide all the flow type computation over here + +aspect production compilation +top::Compilation ::= g::Grammars r::Grammars buildGrammars::[String] benv::BuildEnv +{ + -- aggregate all flow def information + local allFlowDefs :: FlowDefs = foldr(consFlow, nilFlow(), flatMap((.flowDefs), allLatestGrammars)); + local allSpecDefs :: [(String, String, [String], [String])] = flatMap((.specDefs), allLatestGrammars); + local allRefDefs :: [(String, [String])] = flatMap((.refDefs), allLatestGrammars); + local allFlowEnv :: FlowEnv = fromFlowDefs(allSpecDefs, allRefDefs, allFlowDefs); + + -- Look up tree for production info + local prodTree :: EnvTree = directBuildTree(allFlowDefs.prodGraphContribs); + + -- We need to know about all attributes and occurences on nonterminals. + -- It's possible (likely) we could do better than using the overall env here. + local allRealDefs :: [Def] = flatMap((.defs), allLatestGrammars); + local allRealOccursDefs :: [OccursDclInfo] = flatMap((.occursDefs), allLatestGrammars); + local allRealEnv :: Decorated Env = occursEnv(allRealOccursDefs, toEnv(allRealDefs)); + + -- List of all productions + local allProds :: [ValueDclInfo] = foldr(consDefs, nilDefs(), allRealDefs).prodDclList; + local allNts :: [String] = nub(map(getProdNt, allProds)); + + -- Construct production graphs. + production prodGraph :: [ProductionGraph] = + computeAllProductionGraphs(allProds, prodTree, allFlowEnv, allRealEnv) ++ + -- Add in phantom graphs + map(constructPhantomProductionGraph(_, allFlowEnv, allRealEnv), allNts); + + local initialFT :: EnvTree = + computeInitialFlowTypes(allSpecDefs); + + -- Now, solve for flow types!! + local flowTypes1 :: Pair<[ProductionGraph] EnvTree> = + fullySolveFlowTypes(prodGraph, initialFT); + + production flowTypes :: EnvTree = flowTypes1.snd; + production finalGraphs :: [ProductionGraph] = flowTypes1.fst; + production finalGraphEnv :: EnvTree = directBuildTree(map(prodGraphToEnv, finalGraphs)); + + g.productionFlowGraphs = finalGraphEnv; + g.grammarFlowTypes = flowTypes; + + r.productionFlowGraphs = finalGraphEnv; + r.grammarFlowTypes = flowTypes; +} + +function getProdNt +String ::= d::ValueDclInfo +{ + return d.namedSignature.outputElement.typerep.typeName; +} diff --git a/grammars/silver/driver/util/ModuleGraph.sv b/grammars/silver/compiler/driver/util/ModuleGraph.sv similarity index 96% rename from grammars/silver/driver/util/ModuleGraph.sv rename to grammars/silver/compiler/driver/util/ModuleGraph.sv index 1c9cf7d83..4e688a651 100644 --- a/grammars/silver/driver/util/ModuleGraph.sv +++ b/grammars/silver/compiler/driver/util/ModuleGraph.sv @@ -1,4 +1,4 @@ -grammar silver:driver:util; +grammar silver:compiler:driver:util; {- Quick guide to graph closure computations: @@ -163,7 +163,7 @@ function computeOptionalDeps local initPlusExported :: [String] = computeDependencies(init, e); local closeOptions :: [String] = expandOptionalsIter(initPlusExported, [], e); - return if null(rem(closeOptions, initPlusExported)) then initPlusExported + return if null(removeAll(initPlusExported, closeOptions)) then initPlusExported else computeOptionalDeps(closeOptions, e); } @@ -174,7 +174,7 @@ function computeOptionalDeps function completeDependencyClosure [String] ::= init::[String] e::EnvTree { - local n :: [String] = rem(makeSet(flatMap(skipNulls((.moduleNames), _), map(searchEnvTree(_, e), init))), init); + local n :: [String] = removeAll(init, nub(flatMap(skipNulls((.moduleNames), _), map(searchEnvTree(_, e), init)))); return if null(n) then computeOptionalDeps(init, e) else completeDependencyClosure(computeOptionalDeps(n ++ init, e), e); @@ -211,7 +211,7 @@ function noninductiveExpansion [String] ::= initial::[String] rules::[[String]] { return if null(rules) then [] - else if containsAny(tail(head(rules)), initial) && !contains(head(head(rules)), initial) + else if any(map(contains(_, initial), tail(head(rules)))) && !contains(head(head(rules)), initial) then head(head(rules)) :: noninductiveExpansion(initial, tail(rules)) else noninductiveExpansion(initial, tail(rules)); } diff --git a/grammars/silver/compiler/driver/util/RootSpec.sv b/grammars/silver/compiler/driver/util/RootSpec.sv new file mode 100644 index 000000000..783ac5e32 --- /dev/null +++ b/grammars/silver/compiler/driver/util/RootSpec.sv @@ -0,0 +1,424 @@ +grammar silver:compiler:driver:util; + +import silver:reflect; +import silver:reflect:nativeserialize; +import silver:langutil; +import silver:langutil:pp only show; + +import silver:compiler:definition:core only Grammar, grammarErrors, allFileErrors, grammarName, importedDefs, importedOccursDefs, grammarDependencies, globalImports; +import silver:compiler:definition:flow:env only flowEnv, flowDefs, specDefs, refDefs, fromFlowDefs; +import silver:compiler:definition:flow:ast only nilFlow, consFlow, FlowDef; + +import silver:compiler:definition:core only jarName; + +import silver:compiler:analysis:warnings:flow only warnMissingInh; + +{-- + - A representation of a grammar, from an unknown source. TODO: rename GrammarSpec + -} +nonterminal RootSpec with + -- compiler-wide inherited attributes + config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, + -- driver-specific inherited attributes + dependentGrammars, + -- synthesized attributes + declaredName, moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, + defs, occursDefs, grammarErrors, grammarSource, grammarTime, dirtyGrammars, recompiledGrammars, + parsingErrors, allFileErrors, jarName, generateLocation, serInterface; + +flowtype RootSpec = decorate {config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, dependentGrammars}; + +propagate exportedGrammars, optionalGrammars, condBuild, defs, occursDefs on RootSpec; + +{-- + - Grammars (a, b) where b depends on a + -} +inherited attribute dependentGrammars :: [(String, String)]; + +{-- + - Grammars that must be recompiled + -} +monoid attribute dirtyGrammars :: [String]; + +{-- + - Grammars that have been recompiled + -} +monoid attribute recompiledGrammars :: [Decorated RootSpec]; + +{-- + - Parse errors present in this grammar (only for errorRootSpec!) + -} +synthesized attribute parsingErrors :: [Pair]; + +{-- Where generated files are or should be created -} +synthesized attribute generateLocation :: String; + +{-- The serialized interface file for this grammar -} +synthesized attribute serInterface :: ByteArray; + +{-- + - Create a RootSpec from a real grammar, a set of Silver files. + -} +abstract production grammarRootSpec +top::RootSpec ::= g::Grammar oldInterface::Maybe grammarName::String grammarSource::String grammarTime::Integer generateLocation::String +{ + g.grammarName = grammarName; + + -- Create the environments for this grammar + g.env = occursEnv(g.occursDefs, toEnv(g.defs)); + g.globalImports = + occursEnv( + if contains("silver:core", g.moduleNames) || grammarName == "silver:core" then g.importedOccursDefs + else g.importedOccursDefs ++ head(searchEnvTree("silver:core", top.compiledGrammars)).occursDefs, + toEnv( + if contains("silver:core", g.moduleNames) || grammarName == "silver:core" then g.importedDefs + else g.importedDefs ++ head(searchEnvTree("silver:core", top.compiledGrammars)).defs)); + + -- This grammar, its direct imports, and only transitively close over exports and TRIGGERED conditional imports. + -- i.e. these are the things that we really, truly depend upon. (in the sense that we get their symbols) + local actualDependencies :: [String] = + nub(computeDependencies(grammarName :: top.moduleNames, top.compiledGrammars)); + + -- Compute flow information for this grammar, (closing over imports and options, too:) + local depsPlusOptions :: [String] = + nub(completeDependencyClosure(actualDependencies, top.compiledGrammars)); + local rootSpecs :: [Decorated RootSpec] = flatMap(searchEnvTree(_, top.compiledGrammars), depsPlusOptions); + g.grammarDependencies = actualDependencies; + g.flowEnv = + fromFlowDefs( + flatMap((.specDefs), rootSpecs), + flatMap((.refDefs), rootSpecs), + foldr(consFlow, nilFlow(), flatMap((.flowDefs), rootSpecs))); + + production newInterface::InterfaceItems = packInterfaceItems(top); + top.serInterface = + case nativeSerialize(new(newInterface)) of + | left(msg) -> error("Fatal internal error generating interface file: \n" ++ show(80, reflect(new(newInterface)).pp) ++ "\n" ++ msg) + | right(ser) -> ser + end; + + -- Echo down global compiler info + g.config = top.config; + g.compiledGrammars = top.compiledGrammars; + + top.grammarSource = grammarSource; + top.grammarTime = grammarTime; + top.generateLocation = generateLocation; + top.dirtyGrammars := + -- If the interface file is unchanged and we aren't running the flow analysis, + -- we don't need to rebuild the dependent grammars. + -- If we are running the flow analysis, then we unconditionally rebuild all + -- dependent grammars to propagate changes in flow deps, since we ignore flow + -- defs when comparing interface files. + -- This also avoids a circularity issue: computing whether an interface file + -- changed depends on error checking, which depends on computed flow types when + -- the flow analysis is enabled, which depends on which grammars were compiled. + if !top.config.warnMissingInh && oldInterface == just(newInterface) + then [] -- Dependent grammars don't need to be re-translated + else lookupAll(grammarName, top.dependentGrammars); + {- Useful for debugging: + top.dirtyGrammars <- unsafeTracePrint([], + if oldInterface == just(newInterface) + then s"Interface for ${grammarName} unchanged\n" + else s"Interface for ${grammarName} changed\nDependent grammars: ${implode(", ", lookupAll(grammarName, top.dependentGrammars))}\n");-} + + top.recompiledGrammars := [top]; + + top.declaredName = g.declaredName; + top.moduleNames := nub(g.moduleNames ++ ["silver:core"]); -- Ensure the prelude is in the deps, always + top.allGrammarDependencies := actualDependencies; + top.grammarErrors = g.grammarErrors; + top.parsingErrors = []; + top.allFileErrors = g.allFileErrors; + + top.jarName := g.jarName; +} + +{-- + - Create a RootSpec from an interface file, representing a grammar. + -} +abstract production interfaceRootSpec +top::RootSpec ::= i::InterfaceItems generateLocation::String +{ + top.grammarSource = i.maybeGrammarSource.fromJust; + top.grammarTime = i.maybeGrammarTime.fromJust; + top.generateLocation = generateLocation; + + local ood :: Boolean = isOutOfDate(i.maybeGrammarTime.fromJust, top.allGrammarDependencies, top.compiledGrammars); + top.dirtyGrammars := if ood then [i.maybeDeclaredName.fromJust] else []; + top.recompiledGrammars := []; + + top.declaredName = i.maybeDeclaredName.fromJust; + propagate moduleNames, allGrammarDependencies; + top.grammarErrors = []; -- TODO: consider getting grammarName and comparing against declaredName? + top.parsingErrors = []; + top.allFileErrors = []; + + top.jarName := nothing(); + top.serInterface = + case nativeSerialize(new(i)) of + | left(msg) -> error("Fatal internal error generating interface file: \n" ++ show(80, reflect(new(i)).pp) ++ "\n" ++ msg) + | right(ser) -> ser + end; +} + +{-- + - A RootSpec that represents a failure to parse (part) of a grammar. + -} +abstract production errorRootSpec +top::RootSpec ::= e::[ParseError] grammarName::String grammarSource::String grammarTime::Integer generateLocation::String +{ + top.grammarSource = grammarSource; + top.grammarTime = grammarTime; + top.generateLocation = generateLocation; + + top.dirtyGrammars := []; + top.recompiledGrammars := []; + + top.declaredName = grammarName; + propagate moduleNames, allGrammarDependencies; + top.grammarErrors = []; + top.parsingErrors = map(parseErrorToMessage(grammarSource, _), e); + top.allFileErrors = top.parsingErrors; + + top.jarName := nothing(); + top.serInterface = error("errorRootSpec demanded interface"); +} + +function parseErrorToMessage +Pair ::= grammarSource::String e::ParseError +{ + return case e of + | syntaxError(str, locat, _, _) -> + pair(locat.filename, + [err(locat, + "Syntax error:\n" ++ str)]) + | unknownParseError(str, file) -> + pair(file, + [err(loc(grammarSource ++ file, -1, -1, -1, -1, -1, -1), + "Unknown error while parsing:\n" ++ str)]) + end; +} + +monoid attribute maybeGrammarSource::Maybe with nothing(), orElse; +monoid attribute maybeGrammarTime::Maybe with nothing(), orElse; +monoid attribute maybeDeclaredName::Maybe with nothing(), orElse; +monoid attribute hasModuleNames::Boolean with false, ||; +monoid attribute hasExportedGrammars::Boolean with false, ||; +monoid attribute hasOptionalGrammars::Boolean with false, ||; +monoid attribute hasCondBuild::Boolean with false, ||; +monoid attribute hasAllGrammarDependencies::Boolean with false, ||; +monoid attribute hasDefs::Boolean with false, ||; +monoid attribute hasOccursDefs::Boolean with false, ||; + +monoid attribute interfaceErrors::[String]; + +{-- + - Representation of all environment info for a grammar, to be serialized/deserialize to/from an interface + - file. + -} +nonterminal InterfaceItems with + maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, + moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, defs, occursDefs, interfaceErrors, + hasModuleNames, hasExportedGrammars, hasOptionalGrammars, hasCondBuild, hasAllGrammarDependencies, hasDefs, hasOccursDefs, + compareTo, isEqual; + +propagate + maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, + moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, defs, occursDefs, + hasModuleNames, hasExportedGrammars, hasOptionalGrammars, hasCondBuild, hasAllGrammarDependencies, hasDefs, hasOccursDefs, + compareTo, isEqual + on InterfaceItems; + +abstract production consInterfaceItem +top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems +{ + top.interfaceErrors := []; + top.interfaceErrors <- if !top.maybeGrammarSource.isJust then ["Missing item grammarSource"] else []; + top.interfaceErrors <- if !top.maybeGrammarTime.isJust then ["Missing item grammarTime"] else []; + top.interfaceErrors <- if !top.maybeDeclaredName.isJust then ["Missing item declaredName"] else []; + top.interfaceErrors <- if !top.hasModuleNames then ["Missing item moduleNames"] else []; + top.interfaceErrors <- if !top.hasExportedGrammars then ["Missing item exportedGrammars"] else []; + top.interfaceErrors <- if !top.hasOptionalGrammars then ["Missing item optionalGrammars"] else []; + top.interfaceErrors <- if !top.hasCondBuild then ["Missing item condBuild"] else []; + top.interfaceErrors <- if !top.hasAllGrammarDependencies then ["Missing item allGrammarDependencies"] else []; + top.interfaceErrors <- if !top.hasDefs then ["Missing item defs"] else []; + top.interfaceErrors <- if !top.hasOccursDefs then ["Missing item occursDefs"] else []; +} + +abstract production nilInterfaceItem +top::InterfaceItems ::= +{ + top.interfaceErrors := ["Missing all items"]; +} + +closed nonterminal InterfaceItem with + maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, + moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, defs, occursDefs, + hasModuleNames, hasExportedGrammars, hasOptionalGrammars, hasCondBuild, hasAllGrammarDependencies, hasDefs, hasOccursDefs, + compareTo, isEqual; + +propagate + moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, defs, occursDefs, + hasModuleNames, hasExportedGrammars, hasOptionalGrammars, hasCondBuild, hasAllGrammarDependencies, hasDefs, hasOccursDefs + on InterfaceItem; + +aspect default production +top::InterfaceItem ::= +{ + propagate + maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, + moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, defs, occursDefs, + hasModuleNames, hasExportedGrammars, hasOptionalGrammars, hasCondBuild, hasAllGrammarDependencies, hasDefs, hasOccursDefs; +} + +abstract production grammarSourceInterfaceItem +top::InterfaceItem ::= val::String +{ + propagate isEqual; + top.maybeGrammarSource := just(val); +} + +abstract production grammarTimeInterfaceItem +top::InterfaceItem ::= val::Integer +{ + top.isEqual = true; -- Ignore + top.maybeGrammarTime := just(val); +} + +abstract production declaredNameInterfaceItem +top::InterfaceItem ::= val::String +{ + propagate isEqual; + top.maybeDeclaredName := just(val); +} + +abstract production moduleNamesInterfaceItem +top::InterfaceItem ::= val::[String] +{ + propagate isEqual; + top.moduleNames <- val; + top.hasModuleNames <- true; +} + +abstract production exportedGrammarsInterfaceItem +top::InterfaceItem ::= val::[String] +{ + propagate isEqual; + top.exportedGrammars <- val; + top.hasExportedGrammars <- true; +} + +abstract production optionalGrammarsInterfaceItem +top::InterfaceItem ::= val::[String] +{ + propagate isEqual; + top.optionalGrammars <- val; + top.hasOptionalGrammars <- true; +} + +abstract production condBuildInterfaceItem +top::InterfaceItem ::= val::[[String]] +{ + propagate isEqual; + top.condBuild <- val; + top.hasCondBuild <- true; +} + +abstract production allDepsInterfaceItem +top::InterfaceItem ::= val::[String] +{ + propagate isEqual; + top.allGrammarDependencies <- val; + top.hasAllGrammarDependencies <- true; +} + +abstract production defsInterfaceItem +top::InterfaceItem ::= val::[Def] +{ + propagate isEqual; + top.defs <- val; + top.hasDefs <- true; +} + +abstract production occursDefsInterfaceItem +top::InterfaceItem ::= val::[OccursDclInfo] +{ + propagate isEqual; + top.occursDefs <- val; + top.hasOccursDefs <- true; +} + +{-- + - How RootSpecs are turned into interface files shouldn't change + - depending on what the source it, so we give this function externally + - to the productions, instead of as an attribute. + -} +function packInterfaceItems +InterfaceItems ::= r::Decorated RootSpec +{ + production attribute interfaceItems :: [InterfaceItem] with ++; + interfaceItems := [ + grammarSourceInterfaceItem(r.grammarSource), + grammarTimeInterfaceItem(r.grammarTime), + declaredNameInterfaceItem(r.declaredName), + moduleNamesInterfaceItem(r.moduleNames), + exportedGrammarsInterfaceItem(r.exportedGrammars), + optionalGrammarsInterfaceItem(r.optionalGrammars), + condBuildInterfaceItem(r.condBuild), + allDepsInterfaceItem(r.allGrammarDependencies), + defsInterfaceItem(r.defs), + occursDefsInterfaceItem(r.occursDefs) + ]; + + return foldr(consInterfaceItem, nilInterfaceItem(), interfaceItems); +} + +{-- + - All grammar names mentioned by this root spec (not transitive!) + -} +function mentionedGrammars +[String] ::= r::Decorated RootSpec +{ + return nub(r.moduleNames ++ concat(r.condBuild) ++ r.optionalGrammars); +} + +-- We're comparing INTERFACE TIME against GRAMMAR TIME, just to emphasize what's going on here... +function isOutOfDate +Boolean ::= mine::Integer l::[String] e::EnvTree +{ + local n :: [Decorated RootSpec] = searchEnvTree(head(l), e); + + return if null(l) then + false + else if null(n) || mine >= head(n).grammarTime then + isOutOfDate(mine, tail(l), e) + else + true; +} + +{-- + - Write out the interface file for root spec. + - Note that this is normally done during translation, however in the language + - server implementation we would like to just write out interface files + - without translating. + -} +function writeInterface +IO<()> ::= silverGen::String r::Decorated RootSpec +{ + local srcPath :: String = silverGen ++ "src/" ++ grammarToPath(r.declaredName); + + return do { + eprintln("\t[" ++ r.declaredName ++ "]"); + isD::Boolean <- isDirectory(srcPath); + unless(isD, do { + mkDSuccess::Boolean <- mkdir(srcPath); + unless(mkDSuccess, do { + eprintln("Unrecoverable Error: Unable to create directory: " ++ srcPath); + exit(-5); + }); + }); + deleteDirFiles(srcPath); + writeBinaryFile(srcPath ++ "Silver.svi", r.serInterface); + }; +} diff --git a/grammars/silver/compiler/driver/util/Util.sv b/grammars/silver/compiler/driver/util/Util.sv new file mode 100644 index 000000000..af289ae97 --- /dev/null +++ b/grammars/silver/compiler/driver/util/Util.sv @@ -0,0 +1,32 @@ +grammar silver:compiler:driver:util; + +imports silver:compiler:definition:type; +imports silver:compiler:definition:env; + +-- Note that CompileLiterateFile.sv.md needs to be updated too if this changes. +global allowedSilverFileExtensions::[String] = [".sv", ".ag", ".sv.md", ".ag.md"]; + +{-- + - Turns a grammar name into a path, including trailing slash. + -} +function grammarToPath +String ::= g::String +{ + return substitute(":", "/", g) ++ "/"; +} + +@{-- + - Given a path (with terminating /) and list of (file names relative to that root, contents), + - write these out. + -} +function writeFiles +IO<()> ::= path::String s::[(String, String)] +{ + return traverse_(\ item::(String, String) -> writeFile(path ++ item.1, item.2), s); +} + +function writeBinaryFiles +IO<()> ::= path::String s::[(String, ByteArray)] +{ + return traverse_(\ item::(String, ByteArray) -> writeBinaryFile(path ++ item.1, item.2), s); +} diff --git a/grammars/silver/compiler/extension/astconstruction/Syntax.sv b/grammars/silver/compiler/extension/astconstruction/Syntax.sv new file mode 100644 index 000000000..4555cc34d --- /dev/null +++ b/grammars/silver/compiler/extension/astconstruction/Syntax.sv @@ -0,0 +1,83 @@ +grammar silver:compiler:extension:astconstruction; + +imports silver:langutil:pp; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:modification:list; +imports silver:compiler:extension:patternmatching; + +exports silver:reflect:concretesyntax; + +concrete production quoteAST +top::Expr ::= 'AST' '{' ast::AST_c '}' +layout {silver:reflect:concretesyntax:WhiteSpace} +{ + top.unparse = s"AST {${ast.unparse}}"; + forwards to translate(top.location, reflect(ast.ast)); +} + +concrete production quoteASTPattern +top::Pattern ::= 'AST' '{' ast::AST_c '}' +layout {silver:reflect:concretesyntax:WhiteSpace} +{ + top.unparse = s"AST {${ast.unparse}}"; + forwards to translatePattern(top.location, reflect(ast.ast)); +} + +concrete production antiquoteAST_c +top::AST_c ::= '$' '{' e::Expr '}' +layout {silver:compiler:definition:core:WhiteSpace} +{ + top.unparse = s"$${${e.unparse}}"; + top.ast = antiquoteAST(e); + top.errors := []; +} + +concrete production varAST_c +top::AST_c ::= n::QName_t +{ + top.unparse = n.lexeme; + top.ast = antiquotePatternAST(varPattern(name(n.lexeme, n.location), location=top.location)); + top.errors := + if indexOf(":", n.lexeme) != -1 + then [err(n.location, "Pattern variable name must be unqualified")] + else []; +} + +concrete production wildAST_c +top::AST_c ::= '_' +{ + top.unparse = "_"; + top.ast = antiquotePatternAST(wildcPattern('_', location=top.location)); + top.errors := []; +} + +abstract production antiquoteAST +top::AST ::= e::Expr +{ + top.translation = + errorExpr( + [err(top.givenLocation, "${} should only occur inside AST { } expression")], + location=top.givenLocation); + top.patternTranslation = + errorPattern( + [err(top.givenLocation, "${} should only occur inside AST { } expression")], + location=top.givenLocation); + forwards to error("forward shouldn't be needed here"); +} + +abstract production antiquotePatternAST +top::AST ::= p::Pattern +{ + top.translation = + errorExpr( + [err(top.givenLocation, "Variable and wildcard patterns should only occur inside AST { } pattern")], + location=top.givenLocation); + top.patternTranslation = + errorPattern( + [err(top.givenLocation, "Variable and wildcard patterns should only occur inside AST { } pattern")], + location=top.givenLocation); + forwards to error("forward shouldn't be needed here"); +} diff --git a/grammars/silver/compiler/extension/astconstruction/Terminals.sv b/grammars/silver/compiler/extension/astconstruction/Terminals.sv new file mode 100644 index 000000000..48dd3285b --- /dev/null +++ b/grammars/silver/compiler/extension/astconstruction/Terminals.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:extension:astconstruction; + +marking terminal AST_t 'AST' lexer classes {KEYWORD}; + +lexer class Escape; + +terminal EscapeAST_t '$' lexer classes {Escape, SPECOP}; + +disambiguate AST_t, IdUpper_t { + pluck AST_t; +} diff --git a/grammars/silver/compiler/extension/astconstruction/Translation.sv b/grammars/silver/compiler/extension/astconstruction/Translation.sv new file mode 100644 index 000000000..b56292cbe --- /dev/null +++ b/grammars/silver/compiler/extension/astconstruction/Translation.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:extension:astconstruction; + +imports silver:reflect; +imports silver:compiler:metatranslation; + +aspect production nonterminalAST +top::AST ::= prodName::String children::ASTs annotations::NamedASTs +{ + directAntiquoteProductions <- + ["silver:compiler:extension:astconstruction:antiquoteAST"]; + patternAntiquoteProductions <- + ["silver:compiler:extension:astconstruction:antiquotePatternAST"]; +} diff --git a/grammars/silver/compiler/extension/attrsection/AttrSection.sv.md b/grammars/silver/compiler/extension/attrsection/AttrSection.sv.md new file mode 100644 index 000000000..06ded8585 --- /dev/null +++ b/grammars/silver/compiler/extension/attrsection/AttrSection.sv.md @@ -0,0 +1,93 @@ +```silver +grammar silver:compiler:extension:attrsection; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:definition:type; +imports silver:compiler:definition:env; +imports silver:compiler:modification:lambda_fn; + +import silver:util:treeset as ts; +``` + +Attribute sections are a shorthand syntax for a lambda function that simply access an attribute on their argument. + +```silver +concrete production attributeSection +top::Expr ::= '(' '.' q::QNameAttrOccur ')' +{ + top.unparse = s"(.${q.unparse})"; + propagate freeVars; +``` + +In constructing the forward we need to know on what type the attribute will be accessed. +This requires knowing the final substitution, thus the forward here depends on top.finalSubst. +Thus type inference must take place without looking at the forward to avoid a circularity. + +First, try to determine the type of the attribute if it can be looked up unambiguously. +Note that this will have fresh type variables filled in if the attribute has type arguments. +E.g. with `synthesized attribute bar::[a];`, `attrType` for `(.bar)` will always be +`listType(freshType())`. +```silver + local attrType::Type = + case q of + | qNameAttrOccur(at) when at.lookupAttribute.dcls matches [dcl] -> dcl.typeScheme.typerep + | _ -> freshType() + end; +``` + +We must override top.typerep and upSubst as these are used in type inference to compute top.finalSubst. +Construct a function type with a fresh type variable as the input type, +and a "closest guess" of the attribute's type as the output. This type must eventually unify with +the actual input and output types elsewhere in type inference. +```silver + top.typerep = appTypes(functionType(1, []), [freshType(), attrType]); +``` + +No direct type inference happens here. +```silver + top.upSubst = top.downSubst; +``` + +Determine the actual final input and output types that were computed elsewhere during type inference. +```silver + local finalTy::Type = performSubstitution(top.typerep, top.finalSubst); + local inputTy::Type = head(finalTy.inputTypes); + local outputTy::Type = finalTy.outputType; +``` + +The inferred output type must be unambiguous: otherwise it is possible that a consumer of this +expression (e.g. another attribute section...) could assume, after type inference, that some more +specific type that differs from the output type is safe to use for the attribute section here. +```silver + top.errors := + if !null(outputTy.freeFlexibleVars) + then [err(top.location, s"The attribute section ${top.unparse} has an ambiguous inferred output type ${prettyType(outputTy)}, where ${implode(", ", map(findAbbrevFor(_, outputTy.freeVariables), outputTy.freeFlexibleVars))} ${if length(outputTy.freeFlexibleVars) > 1 then "are" else "is"} unspecialized")] + else forward.errors; +``` + +Finally, check that the output type that was inferred matches the type that was actually computed +on the forward (where the type of the attribute was properly looked up knowing the input type that +was inferred elsewhere.) +```silver + local checkOutputType::TypeCheck = check(outputTy, forward.typerep.outputType); + checkOutputType.downSubst = forward.upSubst; + checkOutputType.finalSubst = composeSubst(forward.upSubst, top.finalSubst); + top.errors <- + if checkOutputType.typeerror + then [err(top.location, s"Attribute section ${top.unparse} has inferred output type ${checkOutputType.leftpp} that does not match the attribute's type ${checkOutputType.rightpp}")] + else []; +``` + +The forward is just equivalent to `\ x::inputTy -> x.attr` +```silver + forwards to + lambdap( + productionRHSCons( + productionRHSElem(name("x", top.location), '::', typerepTypeExpr(inputTy, location=top.location), location=top.location), + productionRHSNil(location=top.location), + location=top.location), + access(baseExpr(qName(top.location, "x"), location=top.location), '.', q, location=top.location), + location=top.location); +} +``` diff --git a/grammars/silver/extension/auto_ast/AutoAst.sv b/grammars/silver/compiler/extension/auto_ast/AutoAst.sv similarity index 75% rename from grammars/silver/extension/auto_ast/AutoAst.sv rename to grammars/silver/compiler/extension/auto_ast/AutoAst.sv index de8bac5e1..bd2f07652 100644 --- a/grammars/silver/extension/auto_ast/AutoAst.sv +++ b/grammars/silver/compiler/extension/auto_ast/AutoAst.sv @@ -1,9 +1,9 @@ -grammar silver:extension:auto_ast; +grammar silver:compiler:extension:auto_ast; -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; ---import silver:analysis:typechecking:core; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +--import silver:compiler:analysis:typechecking:core; concrete production autoAstDcl @@ -11,22 +11,22 @@ top::ProductionStmt ::= 'abstract' v::QName ';' { top.unparse = "abstract " ++ v.unparse ++ ";"; - local vty :: Type = - freshenCompletely(v.lookupValue.typerep); + local vty :: Type = v.lookupValue.typeScheme.typerep; local hasLoc :: Boolean = - !null(vty.namedTypes) && head(vty.namedTypes).argName == "location"; + !null(vty.namedTypes) && head(vty.namedTypes).fst == "location"; local elems :: [NamedSignatureElement] = filter(hasAst(_, top.env), top.frame.signature.inputElements); local inferredType :: Type = - functionType( - astType(top.frame.signature.outputElement, top.env), - map(astType(_, top.env), elems), + appTypes( + functionType(length(elems), if hasLoc then ["location"] else []), + map(astType(_, top.env), elems) ++ (if hasLoc - then [namedArgType("location", nonterminalType("core:Location", []))] - else [])); + then [ nonterminalType("silver:core:Location", [], false)] + else []) ++ + [astType(top.frame.signature.outputElement, top.env)]); top.errors <- if hasAst(top.frame.signature.outputElement, top.env) then [] @@ -34,8 +34,7 @@ top::ProductionStmt ::= 'abstract' v::QName ';' local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - errCheck1.downSubst = top.downSubst; - forward.downSubst = errCheck1.upSubst; + thread downSubst, upSubst on top, errCheck1, forward; errCheck1 = check(vty, inferredType); top.errors <- @@ -43,7 +42,7 @@ top::ProductionStmt ::= 'abstract' v::QName ';' else [err(v.location, "Signature yields ast type " ++ errCheck1.rightpp ++ ", but the supplied ast constructor has type " ++ errCheck1.leftpp)]; top.errors <- - if hasLoc && null(getOccursDcl("core:location", top.frame.lhsNtName, top.env)) + if hasLoc && null(getOccursDcl("silver:core:location", top.frame.lhsNtName, top.env)) then [err(top.location, "Ast constructor wants 'location' but this nonterminal does not have a location")] else []; @@ -79,13 +78,13 @@ top::ProductionStmt ::= 'abstract' v::QName ';' function hasAst Boolean ::= ns::NamedSignatureElement env::Decorated Env { - return ns.typerep.isDecorable && + return isDecorable(ns.typerep, env) && !null(getOccursDcl("silver:langutil:ast", ns.typerep.typeName, env)); } function astType Type ::= ns::NamedSignatureElement env::Decorated Env { - local occursCheck :: [DclInfo] = + local occursCheck :: [OccursDclInfo] = getOccursDcl("silver:langutil:ast", ns.typerep.typeName, env); return diff --git a/grammars/silver/compiler/extension/autoattr/DclInfo.sv b/grammars/silver/compiler/extension/autoattr/DclInfo.sv new file mode 100644 index 000000000..26017e411 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/DclInfo.sv @@ -0,0 +1,205 @@ +grammar silver:compiler:extension:autoattr; + +synthesized attribute propagateDispatcher :: (ProductionStmt ::= PartiallyDecorated QName Location) occurs on AttributeDclInfo; + +synthesized attribute emptyVal::Expr occurs on AttributeDclInfo; + +aspect default production +top::AttributeDclInfo ::= +{ + top.propagateDispatcher = propagateError(_, location=_); + top.emptyVal = error("Internal compiler error: must be defined for all monoid attribute declarations"); +} + +aspect production inhDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.propagateDispatcher = propagateInh(_, location=_); +} + +abstract production functorDcl +top::AttributeDclInfo ::= fn::String +{ + top.fullName = fn; + propagate compareKey, isEqual; + + production tyVar::TyVar = freshTyVar(starKind()); + top.typeScheme = polyType([tyVar], varType(tyVar)); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = functorAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateFunctor(_, location=_); +} + +abstract production monoidDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type empty::Expr append::Operation +{ + top.fullName = fn; + propagate compareKey; + top.isEqual = + case top.compareTo of + | monoidDcl(fn2, _, _, empty2, append2) -> + fn == fn2 && top.typeScheme == top.compareTo.typeScheme && empty.unparse == empty2.unparse && append == append2 + | _ -> false + end; + + top.typeScheme = polyType(bound, ty); + top.isSynthesized = true; + top.emptyVal = empty; + top.operation = append; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, attr.name ++ " is a monoid collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); + top.attrBaseDefDispatcher = synBaseColAttributeDef(_, _, _, location=_); + top.attrAppendDefDispatcher = synAppendColAttributeDef(_, _, _, location=_); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateMonoid(_, location=_); +} + +abstract production destructDcl +top::AttributeDclInfo ::= fn::String +{ + top.fullName = fn; + propagate compareKey, isEqual; + + production tyVar::TyVar = freshTyVar(starKind()); + production inhsTyVar::TyVar = freshTyVar(inhSetKind()); + top.typeScheme = polyType([tyVar, inhsTyVar], decoratedType(varType(tyVar), varType(inhsTyVar))); + top.isInherited = true; + + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: should probably be an error handler! access inh from undecorated? + top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); -- Allow normal inh equations + top.attributionDispatcher = destructAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateDestruct(_, location=_); +} + +abstract production equalityDcl +top::AttributeDclInfo ::= inh::String syn::String +{ + top.fullName = syn; + propagate compareKey, isEqual; + + top.typeScheme = monoType(boolType()); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateEquality(inh, _, location=_); +} + +abstract production orderingKeyDcl +top::AttributeDclInfo ::= syn::String +{ + top.fullName = syn; + propagate compareKey, isEqual; + + top.typeScheme = monoType(stringType()); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateOrderingKey(_, location=_); +} + +abstract production orderingDcl +top::AttributeDclInfo ::= inh::String keySyn::String syn::String +{ + top.fullName = syn; + propagate compareKey, isEqual; + + top.typeScheme = monoType(intType()); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateOrdering(inh, keySyn, _, location=_); +} + +abstract production unificationPartialDcl +top::AttributeDclInfo ::= inh::String synPartial::String syn::String +{ + top.fullName = synPartial; + propagate compareKey, isEqual; + + top.typeScheme = monoType(boolType()); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateUnificationSynPartial(inh, _, syn, location=_); +} + +abstract production unificationDcl +top::AttributeDclInfo ::= inh::String synPartial::String syn::String +{ + top.fullName = syn; + propagate compareKey, isEqual; + + top.typeScheme = monoType(boolType()); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateUnificationSyn(inh, synPartial, _, location=_); +} + +abstract production threadedInhDcl +top::AttributeDclInfo ::= inh::String syn::String bound::[TyVar] ty::Type rev::Boolean +{ + top.fullName = inh; + propagate compareKey; + top.isEqual = + case top.compareTo of + | threadedInhDcl(inh2, syn2, _, _, rev2) -> + inh == inh2 && syn == syn2 && top.typeScheme == top.compareTo.typeScheme && rev == rev2 + | _ -> false + end; + + top.typeScheme = polyType(bound, ty); + top.isInherited = true; + + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); -- Allow normal inh equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateThreadedInh(rev, _, syn, location=_); +} + +abstract production threadedSynDcl +top::AttributeDclInfo ::= inh::String syn::String bound::[TyVar] ty::Type rev::Boolean +{ + top.fullName = syn; + propagate compareKey; + top.isEqual = + case top.compareTo of + | threadedSynDcl(inh2, syn2, _, _, rev2) -> + inh == inh2 && syn == syn2 && top.typeScheme == top.compareTo.typeScheme && rev == rev2 + | _ -> false + end; + + top.typeScheme = polyType(bound, ty); + top.isSynthesized = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateThreadedSyn(rev, inh, _, location=_); +} diff --git a/grammars/silver/compiler/extension/autoattr/Destruct.sv b/grammars/silver/compiler/extension/autoattr/Destruct.sv new file mode 100644 index 000000000..d7b8b9bfb --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Destruct.sv @@ -0,0 +1,119 @@ +grammar silver:compiler:extension:autoattr; + +concrete production destructAttributeDcl +top::AGDcl ::= 'destruct' 'attribute' inh::Name ';' +{ + top.unparse = s"destruct attribute ${inh.unparse};"; + top.moduleNames := []; + + production attribute inhFName :: String; + inhFName = top.grammarName ++ ":" ++ inh.name; + + top.errors <- + if length(getAttrDclAll(inhFName, top.env)) > 1 + then [err(inh.location, "Attribute '" ++ inhFName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(destructDcl(inhFName, sourceGrammar=top.grammarName, sourceLocation=inh.location)))], + location=top.location); +} + +abstract production destructAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + top.moduleNames := []; + + forwards to + defaultAttributionDcl( + at, + case attl.types of + | [] -> + botlSome( + bTypeList( + '<', + typeListCons( + case nttl of + | botlSome(tl) -> + appTypeExpr( + nominalTypeExpr(nt.qNameType, location=top.location), + tl, location=top.location) + | botlNone() -> nominalTypeExpr(nt.qNameType, location=top.location) + end, + typeListSingle( + typerepTypeExpr(inhSetType([]), location=top.location), + location=top.location), + location=top.location), + '>', location=top.location), + location=top.location) + | [i] -> + botlSome( + bTypeList( + '<', + typeListCons( + case nttl of + | botlSome(tl) -> + appTypeExpr( + nominalTypeExpr(nt.qNameType, location=top.location), + tl, location=top.location) + | botlNone() -> nominalTypeExpr(nt.qNameType, location=top.location) + end, + typeListSingle( + typerepTypeExpr(i, location=top.location), + location=top.location), + location=top.location), + '>', location=top.location), + location=top.location) + | _ -> attl + end, + nt, nttl, + location=top.location); +} + +{-- + - Propagate a destruct inherited attribute on the enclosing production + - @param attr The name of the attribute to propagate + -} +abstract production propagateDestruct +top::ProductionStmt ::= attr::PartiallyDecorated QName +{ + top.unparse = s"propagate ${attr.unparse};"; + + local numChildren::Integer = length(top.frame.signature.inputElements); + forwards to + foldr( + productionStmtAppend(_, _, location=top.location), + errorProductionStmt([], location=top.location), -- No emptyProductionStmt? + map( + \ ie::Pair -> + Silver_ProductionStmt { + $name{ie.snd.elementName}.$QName{new(attr)} = + case $name{top.frame.signature.outputElement.elementName}.$QName{new(attr)} of + | $Pattern{ + prodAppPattern( + qName(top.location, top.frame.signature.fullName), + '(', + foldr( + patternList_more(_, ',', _, location=top.location), + patternList_nil(location=top.location), + repeat(wildcPattern('_', location=top.location), ie.fst) ++ + Silver_Pattern { a } :: + repeat(wildcPattern('_', location=top.location), numChildren - (ie.fst + 1)) ), + ')', + location=top.location)} -> a + | a -> + error( + "Destruct attribute " ++ $Expr{stringConst(terminal(String_t, s"\"${attr.name}\"", top.location), location=top.location)} ++ + " demanded on child " ++ $Expr{stringConst(terminal(String_t, s"\"${ie.snd.elementName}\"", top.location), location=top.location)} ++ + " of production " ++ $Expr{stringConst(terminal(String_t, s"\"${top.frame.signature.fullName}\"", top.location), location=top.location)} ++ + " when given value " ++ silver:core:hackUnparse(a) ++ " does not match.") + end; + }, + filter( + \ ie::Pair -> + !null(getOccursDcl(attr.lookupAttribute.dcl.fullName, ie.snd.typerep.typeName, top.env)), + zipWith(pair, range(0, numChildren), top.frame.signature.inputElements)))); +} + diff --git a/grammars/silver/compiler/extension/autoattr/Equality.sv b/grammars/silver/compiler/extension/autoattr/Equality.sv new file mode 100644 index 000000000..1561793fc --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Equality.sv @@ -0,0 +1,63 @@ +grammar silver:compiler:extension:autoattr; + +concrete production equalityAttributeDcl +top::AGDcl ::= 'equality' 'attribute' syn::Name 'with' inh::QName ';' +{ + top.unparse = s"equality attribute ${syn.unparse} with ${inh.unparse};"; + top.moduleNames := []; + + production attribute inhFName :: String; + inhFName = inh.lookupAttribute.fullName; + production attribute synFName :: String; + synFName = top.grammarName ++ ":" ++ syn.name; + + top.errors <- + if length(getAttrDclAll(synFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ synFName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(equalityDcl(inhFName, synFName, sourceGrammar=top.grammarName, sourceLocation=syn.location)))], + location=top.location); +} + +{-- + - Propagate a equality synthesized attribute on the enclosing production + - @param attr The name of the attribute to propagate + -} +abstract production propagateEquality +top::ProductionStmt ::= inh::String syn::PartiallyDecorated QName +{ + top.unparse = s"propagate ${syn.unparse};"; + + forwards to + Silver_ProductionStmt { + $name{top.frame.signature.outputElement.elementName}.$QName{new(syn)} = + case $name{top.frame.signature.outputElement.elementName}.$name{inh} of + | $Pattern{ + prodAppPattern( + qName(top.location, top.frame.signature.fullName), + '(', + foldr( + patternList_more(_, ',', _, location=top.location), + patternList_nil(location=top.location), + map( + \ ie::NamedSignatureElement -> Silver_Pattern { $name{ie.elementName ++ "2"} }, + top.frame.signature.inputElements)), + ')', + location=top.location)} -> + $Expr{ + foldr( + and(_, '&&', _, location=top.location), + trueConst('true', location=top.location), + map( + \ ie::NamedSignatureElement -> + if null(getOccursDcl(syn.lookupAttribute.dcl.fullName, ie.typerep.typeName, top.env)) + then Silver_Expr { silver:core:eq($name{ie.elementName}, $name{ie.elementName ++ "2"}) } + else Silver_Expr { $name{ie.elementName}.$QName{new(syn)} }, + top.frame.signature.inputElements))} + | _ -> false + end; + }; +} diff --git a/grammars/silver/compiler/extension/autoattr/Functor.sv b/grammars/silver/compiler/extension/autoattr/Functor.sv new file mode 100644 index 000000000..87acb52e1 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Functor.sv @@ -0,0 +1,137 @@ +grammar silver:compiler:extension:autoattr; + +concrete production functorAttributeDcl +top::AGDcl ::= 'functor' 'attribute' a::Name ';' +{ + top.unparse = "functor attribute " ++ a.unparse ++ ";"; + top.moduleNames := []; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(functorDcl(fName, sourceGrammar=top.grammarName, sourceLocation=a.location)))], + location=top.location); +} + +abstract production functorAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + top.moduleNames := []; + + forwards to + defaultAttributionDcl( + at, + if length(attl.types) > 0 + then attl + else + botlSome( + bTypeList( + '<', + typeListSingle( + case nttl of + | botlSome(tl) -> + appTypeExpr( + nominalTypeExpr(nt.qNameType, location=top.location), + tl, location=top.location) + | botlNone() -> nominalTypeExpr(nt.qNameType, location=top.location) + end, + location=top.location), + '>', location=top.location), + location=top.location), + nt, nttl, + location=top.location); +} + +{-- + - Propagate a functor attribute on the enclosing production + - @param attr The name of the attribute to propagate + -} +abstract production propagateFunctor +top::ProductionStmt ::= attr::PartiallyDecorated QName +{ + top.unparse = s"propagate ${attr.unparse};"; + + -- No explicit errors, for now. The only conceivable issue is the attribute not + -- occuring on the LHS but this should be caught by the forward errors. + + -- Generate the arguments for the constructor + local inputs :: [Expr] = + map(makeArg(top.location, top.env, attr, _), top.frame.signature.inputElements); + local annotations :: [Pair] = + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements); + + -- Construct an attribute def and call with the generated arguments + forwards to + attributeDef( + concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), + '.', + qNameAttrOccur(new(attr), location=top.location), + '=', + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + inputs, + annotations), + ';', + location=top.location); +} + +{-- + - Generates the expression we should use for an argument + - @param loc The parent location to use in construction + - @param env The environment + - @param attrName The name of the attribute being propagated + - @param input The NamedSignatureElement being propagated + - @return Either this the child, or accessing `attrName` on the child + -} +function makeArg +Expr ::= loc::Location env::Decorated Env attrName::Decorated QName input::NamedSignatureElement +{ + local at::QName = qName(loc, input.elementName); + at.env = env; + + -- Check if the attribute occurs on the first child + local attrOccursOnHead :: Boolean = + !null(getOccursDcl(attrName.lookupAttribute.dcl.fullName, input.typerep.typeName, env)); + local validTypeHead :: Boolean = isDecorable(input.typerep, env) && !input.typerep.isPartiallyDecorated; + + return + if validTypeHead && attrOccursOnHead + then access( + baseExpr(at, location=loc), '.', + qNameAttrOccur(new(attrName), location=loc), + location=loc) + else baseExpr(at, location=loc); +} + +{-- + - Generates the list of AnnoExprs used in calling the constructor + - @param loc The parent location to use in construction + - @param baseName The name of the parent from the signature + - @param input The NamedSignatureElement for an annotation + - @return A list of AnnoExprs to be used to build the named arguments + -} +function makeAnnoArg +Pair ::= loc::Location baseName::String input::NamedSignatureElement +{ + -- TODO: This is a hacky way of getting the base name, not sure if correct + -- trouble is the annotations are listed as fullnames, but have to be supplied as shortnames. weird. + local annoName :: String = last(explode(":", input.elementName)); + + return + pair(annoName, + access( + baseExpr(qName(loc, baseName), location=loc), '.', + qNameAttrOccur(qName(loc, annoName), location=loc), + location=loc)); +} diff --git a/grammars/silver/compiler/extension/autoattr/Inherited.sv b/grammars/silver/compiler/extension/autoattr/Inherited.sv new file mode 100644 index 000000000..abf6d37f7 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Inherited.sv @@ -0,0 +1,33 @@ +grammar silver:compiler:extension:autoattr; + +abstract production propagateInh +top::ProductionStmt ::= attr::PartiallyDecorated QName +{ + top.unparse = s"propagate ${attr.unparse};"; + + local attrFullName::String = attr.lookupAttribute.dcl.fullName; + local inputsWithAttr::[NamedSignatureElement] = + filter( + \ input::NamedSignatureElement -> + isDecorable(input.typerep, top.env) && + !input.typerep.isPartiallyDecorated && -- Don't copy on partially decorated children + !null(getOccursDcl(attrFullName, input.typerep.typeName, top.env)), + top.frame.signature.inputElements); + forwards to + foldr( + productionStmtAppend(_, _, location=top.location), + errorProductionStmt([], location=top.location), -- No emptyProductionStmt? + map( + \ ie::NamedSignatureElement -> + attributeDef( + concreteDefLHS(qName(top.location, ie.elementName), location=top.location), '.', + qNameAttrOccur(new(attr), location=top.location), + '=', + access( + baseExpr(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), '.', + qNameAttrOccur(new(attr), location=top.location), + location=top.location), + ';', + location=top.location), + inputsWithAttr)); +} diff --git a/grammars/silver/compiler/extension/autoattr/Monoid.sv b/grammars/silver/compiler/extension/autoattr/Monoid.sv new file mode 100644 index 000000000..0d6d29d4c --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Monoid.sv @@ -0,0 +1,152 @@ +grammar silver:compiler:extension:autoattr; + +import silver:compiler:driver:util; +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; + +concrete production monoidAttributeDcl +top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' e::Expr ',' q::NameOrBOperator ';' +{ + top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ e.unparse ++ ", " ++ q.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + q.operatorForType = te.typerep; + + -- TODO: We want to define our own defs here but can't forward to defsAGDcl because collections define different translation. + -- Not sure about the best way to refactor this. + top.defs := + [attrDef(defaultEnvItem(monoidDcl(fName, tl.freeVariables, te.typerep, e, q.operation, sourceGrammar=top.grammarName, sourceLocation=a.location)))]; + + top.errors <- e.errors; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + local errCheck1 :: TypeCheck = check(e.typerep, te.typerep); + top.errors <- + if errCheck1.typeerror + then [err(e.location, "Monoid attribute " ++ fName ++ " of type " ++ errCheck1.rightpp ++ " has empty value specified with type " ++ errCheck1.leftpp)] + else []; + + e.downSubst = emptySubst(); + errCheck1.downSubst = e.upSubst; + + errCheck1.finalSubst = errCheck1.upSubst; + e.finalSubst = errCheck1.upSubst; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); + + e.frame = globalExprContext(fName, nilContext(), te.typerep, myFlowGraph, sourceGrammar=top.grammarName); + e.isRoot = false; + e.originRules = []; + + forwards to + collectionAttributeDclSyn( + 'synthesized', 'attribute', a, tl, '::', te, 'with', q, ';', + location=top.location); +} + +concrete production tcMonoidAttributeDcl +top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + forwards to + monoidAttributeDcl( + $1, $2, a, tl, $5, te, 'with', + baseExpr(qName(te.location, "silver:core:mempty"), location=te.location), ',', + exprOperator(baseExpr(qName(te.location, "silver:core:append"), location=te.location), location=te.location), $7, + location=top.location); +} + +synthesized attribute appendProd :: (Expr ::= Expr Expr Location) occurs on Operation; + +aspect production functionOperation +top::Operation ::= e::Expr _ _ +{ + top.appendProd = \ e1::Expr e2::Expr l::Location -> mkFunctionInvocation(l, e, [e1, e2]); +} +aspect production plusPlusOperationString +top::Operation ::= +{ + top.appendProd = plusPlus(_, '++', _, location=_); +} +aspect production plusPlusOperationList +top::Operation ::= +{ + top.appendProd = plusPlus(_, '++', _, location=_); +} +aspect production borOperation +top::Operation ::= +{ + top.appendProd = or(_, '||', _, location=_); +} +aspect production bandOperation +top::Operation ::= +{ + top.appendProd = and(_, '&&', _, location=_); +} +aspect production addOperation +top::Operation ::= +{ + top.appendProd = plus(_, '+', _, location=_); +} +aspect production mulOperation +top::Operation ::= +{ + top.appendProd = multiply(_, '*', _, location=_); +} + +{-- + - Propagate a monoid attribute on the enclosing production + - @param attr The name of the attribute to propagate + -} +abstract production propagateMonoid +top::ProductionStmt ::= attr::PartiallyDecorated QName +{ + top.unparse = s"propagate ${attr.unparse};"; + + -- No explicit errors, for now. The only conceivable issue is the attribute not + -- occuring on the LHS but this should be caught by the forward errors. + + local attrFullName::String = attr.lookupAttribute.dcl.fullName; + local inputsWithAttr::[NamedSignatureElement] = + filter( + \ input::NamedSignatureElement -> + isDecorable(input.typerep, top.env) && + !null(getOccursDcl(attrFullName, input.typerep.typeName, top.env)), + top.frame.signature.inputElements); + local res :: Expr = + if null(inputsWithAttr) + then attr.lookupAttribute.dcl.emptyVal + else + foldr1( + attr.lookupAttribute.dcl.operation.appendProd(_, _, top.location), + map( + \ i::NamedSignatureElement -> + access( + baseExpr(qName(top.location, i.elementName), location=top.location), + '.', + qNameAttrOccur(new(attr), location=top.location), + location=top.location), + inputsWithAttr)); + + -- Construct an attribute def and call with the generated arguments + forwards to + attrContainsBase( + concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), + '.', + qNameAttrOccur(new(attr), location=top.location), + ':=', res, ';', location=top.location); +} diff --git a/grammars/silver/compiler/extension/autoattr/Ordering.sv b/grammars/silver/compiler/extension/autoattr/Ordering.sv new file mode 100644 index 000000000..5a990df61 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Ordering.sv @@ -0,0 +1,89 @@ +grammar silver:compiler:extension:autoattr; + +concrete production orderingAttributeDcl +top::AGDcl ::= 'ordering' 'attribute' keySyn::Name ',' syn::Name 'with' inh::QName ';' +{ + top.unparse = s"ordering attribute ${keySyn.unparse}, ${syn.unparse} with ${inh.unparse};"; + top.moduleNames := []; + + production attribute inhFName :: String; + inhFName = inh.lookupAttribute.fullName; + production attribute keySynFName :: String; + keySynFName = top.grammarName ++ ":" ++ keySyn.name; + production attribute synFName :: String; + synFName = top.grammarName ++ ":" ++ syn.name; + + top.errors <- + if length(getAttrDclAll(synFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ synFName ++ "' is already bound.")] + else []; + + top.errors <- + if length(getAttrDclAll(keySynFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ keySynFName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(orderingKeyDcl(keySynFName, sourceGrammar=top.grammarName, sourceLocation=syn.location))), + attrDef(defaultEnvItem(orderingDcl(inhFName, keySynFName, synFName, sourceGrammar=top.grammarName, sourceLocation=syn.location)))], + location=top.location); +} + +{-- + - Propagate a ordering key synthesized attribute on the enclosing production + -} +abstract production propagateOrderingKey +top::ProductionStmt ::= syn::PartiallyDecorated QName +{ + top.unparse = s"propagate ${syn.unparse};"; + + forwards to + Silver_ProductionStmt { + $name{top.frame.signature.outputElement.elementName}.$QName{new(syn)} = + $Expr{stringConst(terminal(String_t, s"\"${top.frame.fullName}\""), location=top.location)}; + }; +} + +{-- + - Propagate a ordering synthesized attribute on the enclosing production + -} +abstract production propagateOrdering +top::ProductionStmt ::= inh::String keySyn::String syn::PartiallyDecorated QName +{ + top.unparse = s"propagate ${syn.unparse};"; + + local topName::String = top.frame.signature.outputElement.elementName; + forwards to + Silver_ProductionStmt { + $name{topName}.$QName{new(syn)} = + case $name{topName}.$name{inh} of + | $Pattern{ + prodAppPattern( + qName(top.location, top.frame.signature.fullName), + '(', + foldr( + patternList_more(_, ',', _, location=top.location), + patternList_nil(location=top.location), + map( + \ ie::NamedSignatureElement -> Silver_Pattern { $name{ie.elementName ++ "2"} }, + top.frame.signature.inputElements)), + ')', + location=top.location)} -> + $Expr{ + if null(top.frame.signature.inputElements) + then Silver_Expr { 0 } + else + foldr1( + \ e1::Expr e2::Expr -> + Silver_Expr { if $Expr{e1} == 0 then $Expr{e2} else $Expr{e1} }, + map( + \ ie::NamedSignatureElement -> + if null(getOccursDcl(syn.lookupAttribute.dcl.fullName, ie.typerep.typeName, top.env)) + then Silver_Expr { silver:core:compare($name{ie.elementName}, $name{ie.elementName ++ "2"}) } + else Silver_Expr { $name{ie.elementName}.$QName{new(syn)} }, + top.frame.signature.inputElements))} + | _ -> silver:core:compare($name{topName}.$name{keySyn}, $name{topName}.$name{inh}.$name{keySyn}) + end; + }; +} diff --git a/grammars/silver/compiler/extension/autoattr/Project.sv b/grammars/silver/compiler/extension/autoattr/Project.sv new file mode 100644 index 000000000..4fac1e21d --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Project.sv @@ -0,0 +1,12 @@ +grammar silver:compiler:extension:autoattr; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:modification:collection; +imports silver:compiler:modification:let_fix; +imports silver:compiler:extension:patternmatching; +imports silver:compiler:metatranslation; + +exports silver:compiler:extension:autoattr:convenience; diff --git a/grammars/silver/extension/autoattr/Propagate.sv b/grammars/silver/compiler/extension/autoattr/Propagate.sv similarity index 85% rename from grammars/silver/extension/autoattr/Propagate.sv rename to grammars/silver/compiler/extension/autoattr/Propagate.sv index df8962182..48dc0bcec 100644 --- a/grammars/silver/extension/autoattr/Propagate.sv +++ b/grammars/silver/compiler/extension/autoattr/Propagate.sv @@ -1,4 +1,4 @@ -grammar silver:extension:autoattr; +grammar silver:compiler:extension:autoattr; concrete production propagateOnNTListExcludingDcl_c top::AGDcl ::= 'propagate' attrs::NameList 'on' nts::NameList 'excluding' ps::ProdNameList ';' @@ -47,16 +47,17 @@ top::AGDcl ::= attrs::NameList nt::QName ps::ProdNameList top.defs := []; top.occursDefs := []; top.moduleNames := []; + top.specDefs := []; - local includedProds::[DclInfo] = + local includedProds::[ValueDclInfo] = filter( - \ d::DclInfo -> !d.hasForward && !containsBy(stringEq, d.fullName, ps.names), + \ d::ValueDclInfo -> !d.hasForward && !contains(d.fullName, ps.names), getKnownProds(nt.lookupType.fullName, top.env)); local dcl::AGDcl = foldr( appendAGDcl(_, _, location=top.location), emptyAGDcl(location=top.location), map(propagateAspectDcl(_, attrs, location=nt.location), includedProds)); - + forwards to if !null(nt.lookupType.errors) then errorAGDcl(nt.lookupType.errors, location=top.location) @@ -64,7 +65,7 @@ top::AGDcl ::= attrs::NameList nt::QName ps::ProdNameList } abstract production propagateAspectDcl -top::AGDcl ::= d::DclInfo attrs::NameList +top::AGDcl ::= d::ValueDclInfo attrs::NameList { top.errors := if null(forward.errors) @@ -87,7 +88,7 @@ top::AGDcl ::= d::DclInfo attrs::NameList \ ie::NamedSignatureElement -> aspectRHSElemFull( name(ie.elementName, top.location), - ie.typerep, + freshenType(ie.typerep, ie.typerep.freeVariables), location=top.location), d.namedSignature.inputElements)), location=top.location), @@ -124,6 +125,14 @@ abstract production propagateOneAttr top::ProductionStmt ::= attr::QName { top.unparse = s"propagate ${attr.unparse};"; + + -- We make an exception to permit propagated equations in places that would otherwise be orphaned. + -- Since this is the only possible equation permitted in these contexts, having duplicates will + -- still yield a well-defined specification. + -- TOOD: Filtering based on the error message is a bit of a hack. + -- With https://github.com/melt-umn/silver/issues/648 we could instead filter on an error code. + top.errors := + filter(\ m::Message -> !startsWith("Orphaned equation:", m.message), forward.errors); -- Ugh, workaround for circular dependency top.defs := []; @@ -135,7 +144,7 @@ top::ProductionStmt ::= attr::QName } abstract production propagateError -top::ProductionStmt ::= attr::Decorated QName +top::ProductionStmt ::= attr::PartiallyDecorated QName { forwards to errorProductionStmt( @@ -165,7 +174,7 @@ top::ProdNameList ::= n::QName if n.lookupValue.found then case n.lookupValue.dcl of - | prodDcl(_, _, _, _) -> [] + | prodDcl(_, _) -> [] | _ -> [err(n.location, n.name ++ " is not a production")] end else []; @@ -181,7 +190,7 @@ top::ProdNameList ::= h::QName ',' t::ProdNameList if h.lookupValue.found then case h.lookupValue.dcl of - | prodDcl(_, _, _, _) -> [] + | prodDcl(_, _) -> [] | _ -> [err(h.location, h.name ++ " is not a production")] end else []; diff --git a/grammars/silver/compiler/extension/autoattr/Terminals.sv b/grammars/silver/compiler/extension/autoattr/Terminals.sv new file mode 100644 index 000000000..f31ae0287 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Terminals.sv @@ -0,0 +1,14 @@ +grammar silver:compiler:extension:autoattr; + +terminal Propagate_kwd 'propagate' lexer classes {KEYWORD,RESERVED}; +terminal Excluding_kwd 'excluding' lexer classes {KEYWORD}; +terminal Thread_kwd 'thread' lexer classes {KEYWORD,RESERVED}; +terminal Direction_kwd 'direction' lexer classes {KEYWORD}; + +terminal Functor_kwd 'functor' lexer classes {KEYWORD}; +terminal Monoid_kwd 'monoid' lexer classes {KEYWORD}; +terminal Destruct_kwd 'destruct' lexer classes {KEYWORD}; +terminal Equality_kwd 'equality' lexer classes {KEYWORD}; +terminal Ordering_kwd 'ordering' lexer classes {KEYWORD}; +terminal Unification_kwd 'unification' lexer classes {KEYWORD}; +terminal Threaded_kwd 'threaded' lexer classes {KEYWORD}; \ No newline at end of file diff --git a/grammars/silver/compiler/extension/autoattr/Threaded.sv b/grammars/silver/compiler/extension/autoattr/Threaded.sv new file mode 100644 index 000000000..48843cd8c --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Threaded.sv @@ -0,0 +1,214 @@ +grammar silver:compiler:extension:autoattr; + +import silver:compiler:definition:concrete_syntax only Left_kwd, Right_kwd; + +concrete production threadedAttributeDcl +top::AGDcl ::= 'threaded' 'attribute' inh::Name ',' syn::Name tl::BracketedOptTypeExprs '::' te::TypeExpr d::OptDirectionMod ';' +{ + top.unparse = s"threaded attribute ${inh.unparse}, ${syn.unparse} ${tl.unparse} :: ${te.unparse};"; + top.moduleNames := []; + + production attribute inhFName :: String; + inhFName = top.grammarName ++ ":" ++ inh.name; + production attribute synFName :: String; + synFName = top.grammarName ++ ":" ++ syn.name; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors <- + if length(getAttrDclAll(inhFName, top.env)) > 1 + then [err(inh.location, "Attribute '" ++ inhFName ++ "' is already bound.")] + else []; + top.errors <- + if length(getAttrDclAll(synFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ synFName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(threadedInhDcl(inhFName, synFName, tl.freeVariables, te.typerep, d.reversed, sourceGrammar=top.grammarName, sourceLocation=inh.location))), + attrDef(defaultEnvItem(threadedSynDcl(inhFName, synFName, tl.freeVariables, te.typerep, d.reversed, sourceGrammar=top.grammarName, sourceLocation=syn.location)))], + location=top.location); +} + +synthesized attribute reversed::Boolean; + +nonterminal OptDirectionMod with reversed; +concrete productions top::OptDirectionMod +| 'direction' '=' d::Direction + { top.reversed = d.reversed; } +| + { top.reversed = false; } + +nonterminal Direction with reversed; +concrete productions top::Direction +| 'left' 'to' 'right' + { top.reversed = false; } +| 'right' 'to' 'left' + { top.reversed = true; } + +abstract production propagateThreadedInh +top::ProductionStmt ::= rev::Boolean inh::PartiallyDecorated QName syn::String +{ + top.unparse = s"propagate ${inh.unparse};"; + + local lhsName::String = top.frame.signature.outputElement.elementName; + local occursChildren::[String] = + map( + (.elementName), + filter( + \ ie::NamedSignatureElement -> + isDecorable(ie.typerep, top.env) && + !ie.typerep.isPartiallyDecorated && -- Don't thread on partially decorated children + !null(getOccursDcl(inh.lookupAttribute.fullName, ie.typerep.typeName, top.env)) && + !null(getOccursDcl(syn, ie.typerep.typeName, top.env)), + if null(getOccursDcl(syn, top.frame.lhsNtName, top.env)) && !null(top.frame.signature.inputElements) + then init(top.frame.signature.inputElements) + else top.frame.signature.inputElements)); + forwards to + threadInhDcl( + inh.name, syn, + map( + name(_, top.location), + lhsName :: + (if rev then reverse(occursChildren) else occursChildren) ++ + if null(getOccursDcl(syn, top.frame.lhsNtName, top.env)) && !null(top.frame.signature.inputElements) + then if !null(getOccursDcl(inh.lookupAttribute.fullName, last(top.frame.signature.inputElements).typerep.typeName, top.env)) + then [last(top.frame.signature.inputElements).elementName] + else [] + else [if !null(getValueDcl("forward", top.env)) then "forward" else lhsName]), + location=top.location); +} + +abstract production propagateThreadedSyn +top::ProductionStmt ::= rev::Boolean inh::String syn::PartiallyDecorated QName +{ + top.unparse = s"propagate ${syn.unparse};"; + + local lhsName::String = top.frame.signature.outputElement.elementName; + local occursChildren::[String] = + map( + (.elementName), + filter( + \ ie::NamedSignatureElement -> + isDecorable(ie.typerep, top.env) && + !ie.typerep.isPartiallyDecorated && -- Don't thread on partially decorated children + !null(getOccursDcl(inh, ie.typerep.typeName, top.env)) && + !null(getOccursDcl(syn.lookupAttribute.fullName, ie.typerep.typeName, top.env)), + top.frame.signature.inputElements)); + forwards to + threadSynDcl( + inh, syn.name, + map( + name(_, top.location), + lhsName :: + (if rev then reverse(occursChildren) else occursChildren) ++ + [if !null(getValueDcl("forward", top.env)) then "forward" else lhsName]), + location=top.location); +} + +concrete production threadDcl_c +top::ProductionStmt ::= 'thread' inh::QName ',' syn::QName 'on' children::ChildNameList ';' +{ + top.unparse = s"thread ${inh.unparse}, ${syn.unparse} on ${children.unparse};"; + forwards to + productionStmtAppend( + threadInhDcl(inh.name, syn.name, children.ids, location=top.location), + threadSynDcl(inh.name, syn.name, children.ids, location=top.location), + location=top.location); +} + +abstract production threadInhDcl +top::ProductionStmt ::= inh::String syn::String children::[Name] +{ + top.unparse = s"thread ${inh}, ${syn} on ${implode(", ", map((.unparse), children))};"; + + local lhsName::String = top.frame.signature.outputElement.elementName; + forwards to + foldr( + productionStmtAppend(_, _, location=top.location), + errorProductionStmt([], location=top.location), -- No emptyProductionStmt? + zipWith( + \ c1::Name c2::Name -> + if c1.name != lhsName + then + attributeDef( + concreteDefLHS(qNameId(c1, location=top.location), location=top.location), + '.', + qNameAttrOccur(qName(top.location, inh), location=top.location), + '=', + access( + baseExpr(qNameId(c2, location=top.location), location=top.location), '.', + qNameAttrOccur(qName(top.location, if c2.name == lhsName then inh else syn), location=top.location), + location=top.location), + ';', + location=top.location) + else errorProductionStmt([], location=top.location), + tail(children), children)); +} + +abstract production threadSynDcl +top::ProductionStmt ::= inh::String syn::String children::[Name] +{ + top.unparse = s"thread ${inh}, ${syn} on ${implode(", ", map((.unparse), children))};"; + + local lhsName::String = top.frame.signature.outputElement.elementName; + forwards to + foldr( + productionStmtAppend(_, _, location=top.location), + errorProductionStmt([], location=top.location), -- No emptyProductionStmt? + zipWith( + \ c1::Name c2::Name -> + if c1.name == lhsName + then + attributeDef( + concreteDefLHS(qNameId(c1, location=top.location), location=top.location), + '.', + qNameAttrOccur(qName(top.location, syn), location=top.location), + '=', + access( + baseExpr(qNameId(c2, location=top.location), location=top.location), '.', + qNameAttrOccur(qName(top.location, if c2.name == lhsName then inh else syn), location=top.location), + location=top.location), + ';', + location=top.location) + else errorProductionStmt([], location=top.location), + tail(children), children)); +} + +synthesized attribute ids :: [Name]; + +nonterminal ChildNameList with location, unparse, ids; +concrete production idSingle +top::ChildNameList ::= id::ChildName +{ + top.unparse = id.unparse; + top.ids = [id.id]; +} + +concrete production idCons +top::ChildNameList ::= id1::ChildName ',' id2::ChildNameList +{ + top.unparse = id1.unparse ++ ", " ++ id2.unparse; + top.ids = id1.id :: id2.ids; +} + +synthesized attribute id :: Name; + +nonterminal ChildName with location, unparse, id; +concrete production idName +top::ChildName ::= id::Name +{ + top.unparse = id.unparse; + top.id = id; +} + +concrete production idForward +top::ChildName ::= 'forward' +{ + top.unparse = "forward"; + top.id = name("forward", top.location); +} + diff --git a/grammars/silver/compiler/extension/autoattr/Unification.sv b/grammars/silver/compiler/extension/autoattr/Unification.sv new file mode 100644 index 000000000..d32680fdc --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/Unification.sv @@ -0,0 +1,109 @@ +grammar silver:compiler:extension:autoattr; + +concrete production unificationAttributeDcl +top::AGDcl ::= 'unification' 'attribute' synPartial::Name ',' syn::Name 'with' inh::QName ';' +{ + top.unparse = s"unification attribute ${synPartial.unparse}, ${syn.unparse} with ${inh.unparse};"; + top.moduleNames := []; + + production attribute inhFName :: String; + inhFName = inh.lookupAttribute.fullName; + production attribute synPartialFName :: String; + synPartialFName = top.grammarName ++ ":" ++ synPartial.name; + production attribute synFName :: String; + synFName = top.grammarName ++ ":" ++ syn.name; + + top.errors <- + if length(getAttrDclAll(synPartialFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ synPartialFName ++ "' is already bound.")] + else []; + top.errors <- + if length(getAttrDclAll(synFName, top.env)) > 1 + then [err(syn.location, "Attribute '" ++ synFName ++ "' is already bound.")] + else []; + + forwards to + defsAGDcl( + [attrDef(defaultEnvItem(unificationPartialDcl(inhFName, synPartialFName, synFName, sourceGrammar=top.grammarName, sourceLocation=synPartial.location))), + attrDef(defaultEnvItem(unificationDcl(inhFName, synPartialFName, synFName, sourceGrammar=top.grammarName, sourceLocation=syn.location)))], + location=top.location); +} + +abstract production unificationInhAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; + top.moduleNames := []; + + forwards to + defaultAttributionDcl( + at, + if length(attl.types) > 0 + then attl + else + botlSome( + bTypeList( + '<', + typeListSingle( + case nttl of + | botlSome(tl) -> + appTypeExpr( + nominalTypeExpr(nt.qNameType, location=top.location), + tl, location=top.location) + | botlNone() -> nominalTypeExpr(nt.qNameType, location=top.location) + end, + location=top.location), + '>', location=top.location), + location=top.location), + nt, nttl, + location=top.location); +} + +abstract production propagateUnificationSynPartial +top::ProductionStmt ::= inh::String synPartial::PartiallyDecorated QName syn::String +{ + top.unparse = s"propagate ${synPartial.unparse};"; + + forwards to + Silver_ProductionStmt { + $name{top.frame.signature.outputElement.elementName}.$QName{new(synPartial)} = + case $name{top.frame.signature.outputElement.elementName}.$name{inh} of + | $Pattern{ + prodAppPattern( + qName(top.location, top.frame.signature.fullName), + '(', + foldr( + patternList_more(_, ',', _, location=top.location), + patternList_nil(location=top.location), + map( + \ ie::NamedSignatureElement -> Silver_Pattern { $name{ie.elementName ++ "2"} }, + top.frame.signature.inputElements)), + ')', + location=top.location)} -> + $Expr{ + foldr( + and(_, '&&', _, location=top.location), + trueConst('true', location=top.location), + map( + \ ie::NamedSignatureElement -> + if null(getOccursDcl(syn, ie.typerep.typeName, top.env)) + then Silver_Expr { silver:core:eq($name{ie.elementName}, $name{ie.elementName ++ "2"}) } + else Silver_Expr { $name{ie.elementName}.$qName{syn} }, + top.frame.signature.inputElements))} + | _ -> false + end; + }; +} + +abstract production propagateUnificationSyn +top::ProductionStmt ::= inh::String synPartial::String syn::PartiallyDecorated QName +{ + top.unparse = s"propagate ${syn.unparse};"; + + forwards to + Silver_ProductionStmt { + $name{top.frame.signature.outputElement.elementName}.$QName{new(syn)} = + $name{top.frame.signature.outputElement.elementName}.$qName{synPartial} || + $name{top.frame.signature.outputElement.elementName}.$qName{inh}.$qName{synPartial}; + }; +} diff --git a/grammars/silver/compiler/extension/autoattr/convenience/Convenience.sv b/grammars/silver/compiler/extension/autoattr/convenience/Convenience.sv new file mode 100644 index 000000000..5daea4e10 --- /dev/null +++ b/grammars/silver/compiler/extension/autoattr/convenience/Convenience.sv @@ -0,0 +1,149 @@ +grammar silver:compiler:extension:autoattr:convenience; + +import silver:compiler:extension:autoattr; +import silver:compiler:extension:convenience; +import silver:compiler:modification:collection; +import silver:compiler:definition:core; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:env; + +concrete production functorAttributeDclMultiple +top::AGDcl ::= 'functor' 'attribute' a::Name 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "functor attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + functorAttributeDcl($1, $2, a, $7, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production monoidAttributeDclMultiple +top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' e::Expr ',' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ e.unparse ++ ", " ++ q.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + monoidAttributeDcl($1, $2, a, tl, $5, te, $7, e, $9, q, $14, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production tcMonoidAttributeDclMultiple +top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + tcMonoidAttributeDcl($1, $2, a, tl, $5, te, $10, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production destructAttributeDclMultiple +top::AGDcl ::= 'destruct' 'attribute' a::Name 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "destruct attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + destructAttributeDcl($1, $2, a, ';', location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production equalityAttributeDclMultiple +top::AGDcl ::= 'equality' 'attribute' syn::Name 'with' inh::QName 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "equality attribute " ++ syn.name ++ " with " ++ inh.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + equalityAttributeDcl($1, $2, syn, $4, inh, $9, location=top.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(syn, location=syn.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production orderingAttributeDclMultiple +top::AGDcl ::= 'ordering' 'attribute' keySyn::Name ',' syn::Name 'with' inh::QName 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "ordering attribute " ++ keySyn.name ++ ", " ++ syn.name ++ " with " ++ inh.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + orderingAttributeDcl($1, $2, keySyn, $4, syn, $6, inh, $11, location=top.location), + appendAGDcl( + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(keySyn, location=syn.location), botlNone(location=top.location)), qs.qnames), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(syn, location=syn.location), botlNone(location=top.location)), qs.qnames), + location=top.location), + location=top.location); +} + +-- Deprecate? Eric suggested keeping this: https://github.com/melt-umn/silver/issues/431#issuecomment-760552226 +concrete production destructEqualityAttributeDcl +top::AGDcl ::= 'equality' 'attribute' inh::Name ',' syn::Name ';' +{ + top.unparse = "equality attribute " ++ inh.unparse ++ ", " ++ syn.name ++ ";"; + forwards to + appendAGDcl( + destructAttributeDcl('destruct', $2, inh, $6, location=top.location), + equalityAttributeDcl($1, $2, syn, 'with', qNameId(inh, location=top.location), $6, location=top.location), + location=top.location); +} +concrete production destructEqualityAttributeDclMultiple +top::AGDcl ::= 'equality' 'attribute' inh::Name ',' syn::Name 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "equality attribute " ++ inh.unparse ++ ", " ++ syn.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + destructAttributeDclMultiple('destruct', $2, inh, $6, $7, qs, $9, location=top.location), + equalityAttributeDclMultiple($1, $2, syn, 'with', qNameId(inh, location=top.location), $6, $7, qs, $9, location=top.location), + location=top.location); +} +concrete production destructOrderingAttributeDcl +top::AGDcl ::= 'ordering' 'attribute' inh::Name ',' keySyn::Name ',' syn::Name ';' +{ + top.unparse = "ordering attribute " ++ inh.unparse ++ ", " ++ keySyn.name ++ ", " ++ syn.name ++ ";"; + forwards to + appendAGDcl( + destructAttributeDcl('destruct', $2, inh, $8, location=top.location), + orderingAttributeDcl($1, $2, keySyn, $6, syn, 'with', qNameId(inh, location=top.location), $8, location=top.location), + location=top.location); +} +concrete production destructOrderingAttributeDclMultiple +top::AGDcl ::= 'ordering' 'attribute' inh::Name ',' keySyn::Name ',' syn::Name 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "ordering attribute " ++ inh.unparse ++ ", " ++ keySyn.name ++ ", " ++ syn.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + destructAttributeDclMultiple('destruct', $2, inh, $8, $9, qs, $11, location=top.location), + orderingAttributeDclMultiple($1, $2, keySyn, $6, syn, 'with', qNameId(inh, location=top.location), $8, $9, qs, $11, location=top.location), + location=top.location); +} + +concrete production unificationAttributeDclMultiple +top::AGDcl ::= 'unification' 'attribute' synPartial::Name ',' syn::Name 'with' inh::QName 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "unification attribute " ++ synPartial.name ++ ", " ++ syn.name ++ " with " ++ inh.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + unificationAttributeDcl($1, $2, synPartial, ',', syn, 'with', inh, ';', location=top.location), + appendAGDcl( + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(synPartial, location=synPartial.location), botlNone(location=top.location)), qs.qnames), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(syn, location=syn.location), botlNone(location=top.location)), qs.qnames), + location=top.location), + location=top.location); +} + +concrete production threadedAttributeDclMultiple +top::AGDcl ::= 'threaded' 'attribute' inh::Name ',' syn::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames d::OptDirectionMod ';' +{ + top.unparse = "threaded attribute " ++ inh.unparse ++ ", " ++ syn.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + threadedAttributeDcl($1, $2, inh, $4, syn, tl, $7, te, d, ';', location=top.location), + appendAGDcl( + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(inh, location=inh.location), botlNone(location=top.location)), qs.qnames), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(syn, location=syn.location), botlNone(location=top.location)), qs.qnames), + location=top.location), + location=top.location); +} diff --git a/grammars/silver/extension/constructparser/Construct.sv b/grammars/silver/compiler/extension/constructparser/Construct.sv similarity index 77% rename from grammars/silver/extension/constructparser/Construct.sv rename to grammars/silver/compiler/extension/constructparser/Construct.sv index 5ee29916a..4a6408263 100644 --- a/grammars/silver/extension/constructparser/Construct.sv +++ b/grammars/silver/compiler/extension/constructparser/Construct.sv @@ -1,10 +1,10 @@ -grammar silver:extension:constructparser; -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:definition:type:syntax; -import silver:modification:copper; -import silver:extension:list; +grammar silver:compiler:extension:constructparser; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:copper; +import silver:compiler:modification:list; terminal Construct_t 'construct' lexer classes {KEYWORD}; terminal Translator_t 'translator' lexer classes {KEYWORD}; @@ -23,8 +23,7 @@ top::Root ::= gdcl::GrammarDcl mStmts::ModuleStmts is::ImportStmts local prsr :: AGDcl = parserDcl('parser', name("extendedParser", top.location), '::', - nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Root"), location=top.location), - botlNone(location=top.location), location=top.location), + nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Root"), location=top.location), location=top.location), '{', consParserComponent( parserComponent(moduleName(m, location=top.location), @@ -35,10 +34,14 @@ top::Root ::= gdcl::GrammarDcl mStmts::ModuleStmts is::ImportStmts local main :: AGDcl = functionDcl('function', name("main", top.location), functionSignature( + nilConstraint(location=top.location), + '=>', functionLHS( - nominalTypeExpr( - qNameTypeId(terminal(IdUpper_t, "IOVal"), location=top.location), - botlSome('<', typeListSingle(integerTypeExpr('Integer', location=top.location), + appTypeExpr( + nominalTypeExpr( + qNameTypeId(terminal(IdUpper_t, "IOVal"), location=top.location), + location=top.location), + bTypeList('<', typeListSingle(integerTypeExpr('Integer', location=top.location), location=top.location), '>', location=top.location), location=top.location ), @@ -50,8 +53,7 @@ top::Root ::= gdcl::GrammarDcl mStmts::ModuleStmts is::ImportStmts location=top.location), location=top.location), productionRHSCons( productionRHSElem(name("ioIn", top.location), '::', - nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "IO"), location=top.location), - botlNone(location=top.location), location=top.location), + nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "IOToken"), location=top.location), location=top.location), location=top.location), productionRHSNil(location=top.location), location=top.location diff --git a/grammars/silver/extension/convenience/Children.sv b/grammars/silver/compiler/extension/convenience/Children.sv similarity index 95% rename from grammars/silver/extension/convenience/Children.sv rename to grammars/silver/compiler/extension/convenience/Children.sv index 11f8041a4..5cd81cc3b 100644 --- a/grammars/silver/extension/convenience/Children.sv +++ b/grammars/silver/compiler/extension/convenience/Children.sv @@ -1,4 +1,4 @@ -grammar silver:extension:convenience; +grammar silver:compiler:extension:convenience; terminal Children_kwd '$' lexer classes {LITERAL}; diff --git a/grammars/silver/compiler/extension/convenience/Convenience.sv b/grammars/silver/compiler/extension/convenience/Convenience.sv new file mode 100644 index 000000000..28bf8c10f --- /dev/null +++ b/grammars/silver/compiler/extension/convenience/Convenience.sv @@ -0,0 +1,133 @@ +grammar silver:compiler:extension:convenience; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:core; +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +import silver:compiler:modification:collection; + +-- Multiple attribute occurs on statements +concrete production multipleAttributionDclsManyMany +top::AGDcl ::= 'attribute' a::QNames2 'occurs' 'on' nts::QNames2 ';' +{ + top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, a.qnames, nts.qnames); +} +concrete production multipleAttributionDclsSingleMany +top::AGDcl ::= 'attribute' a::QName tl::BracketedOptTypeExprs 'occurs' 'on' nts::QNames2 ';' +{ + top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, [qNameWithTL(a, tl)], nts.qnames); +} +concrete production multipleAttributionDclsManySingle +top::AGDcl ::= 'attribute' a::QNames2 'occurs' 'on' nts::QNameWithTL ';' +{ + top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, a.qnames, [nts]); +} + +-- Multiple annotation occurs on statements +concrete production multipleAnnotationDclsManyMany +top::AGDcl ::= 'annotation' a::QNames2 'occurs' 'on' nts::QNames2 ';' +{ + top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, a.qnames, nts.qnames); +} +concrete production multipleAnnotationDclsSingleMany +top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs 'occurs' 'on' nts::QNames2 ';' +{ + top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, [qNameWithTL(a, tl)], nts.qnames); +} +concrete production multipleAnnotationDclsManySingle +top::AGDcl ::= 'annotation' a::QNames2 'occurs' 'on' nts::QNameWithTL ';' +{ + top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; + forwards to makeOccursDcls(top.location, a.qnames, [nts]); +} + + +concrete production nonterminalWithDcl +top::AGDcl ::= quals::NTDeclQualifiers 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers 'with' attrs::QNames ';' +{ + top.unparse = "nonterminal " ++ id.unparse ++ tl.unparse ++ " " ++ nm.unparse ++ " with " ++ attrs.unparse ++ " ;"; + forwards to appendAGDcl( + nonterminalDcl(quals, $2, id, tl, nm, $8, location=top.location), + makeOccursDcls(top.location, attrs.qnames, [qNameWithTL(qNameId(id, location=id.location), tl)]), + location=top.location); +} action { + insert semantic token IdTypeDcl_t at id.location; +} + + +concrete production attributeDclInhMultiple +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; + forwards to appendAGDcl( + attributeDclInh($1, $2, a, tl, $5, te, $10, location=top.location), + makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), + location=top.location); +} + +concrete production attributeDclSynMultiple +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; + forwards to appendAGDcl( + attributeDclSyn($1, $2, a, tl, $5, te, $10, location=top.location), + makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), + location=top.location); +} + +concrete production collectionAttributeDclInhMultiple +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; + forwards to appendAGDcl( + collectionAttributeDclInh($1, $2, a, tl, $5, te, $7, q, $12, location=top.location), + makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), + location=top.location); +} + +concrete production collectionAttributeDclSynMultiple +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; + forwards to appendAGDcl( + collectionAttributeDclSyn($1, $2, a, tl, $5, te, $7, q, $12, location=top.location), + makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), + location=top.location); +} + + + + +{- TEMPORARILY(?) DISABLED this aren't commonly used anyhow + + + + +concrete production nonterminalWithDcl3 +top::AGDcl ::= 'nonterminal' id::Names2 ';' +{ + top.unparse = "nonterminal " ++ id.unparse ++ " ;" ; + forwards to makeNTDcls($1.line, $1.column, id.ids) ; +} +concrete production attributeDclInhMultiple1 +top::AGDcl ::= 'inherited' 'attribute' a::Names2 '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "inherited attribute " ++ a.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; + forwards to appendAGDcl(makeInhDcls($1.line, $1.column, te, a.ids), makeOccursDcls($1.line, $1.column, qualifyNames(a.ids), qs.qnames)); +} +concrete production attributeDclSynMultiple1 +top::AGDcl ::= 'synthesized' 'attribute' a::Names2 '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "synthesized attribute " ++ a.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; + forwards to appendAGDcl(makeSynDcls($1.line, $1.column, te, a.ids), makeOccursDcls($1.line, $1.column, qualifyNames(a.ids), qs.qnames)); +} + + + +-} diff --git a/grammars/silver/extension/convenience/Lists.sv b/grammars/silver/compiler/extension/convenience/Lists.sv similarity index 94% rename from grammars/silver/extension/convenience/Lists.sv rename to grammars/silver/compiler/extension/convenience/Lists.sv index 05ca481d7..700be4edc 100644 --- a/grammars/silver/extension/convenience/Lists.sv +++ b/grammars/silver/compiler/extension/convenience/Lists.sv @@ -1,4 +1,4 @@ -grammar silver:extension:convenience; +grammar silver:compiler:extension:convenience; nonterminal QNameWithTL with unparse,qnwtQN, qnwtTL; synthesized attribute qnwtQN :: QName; @@ -110,7 +110,7 @@ top::Names ::= id1::Name ',' id2::Names function qualifyNames [QName] ::= i::[Name] { - return if null(i) then [] else [qNameId(head(i))] ++ qualifyNames(tail(i)); + return if null(i) then [] else qNameId(head(i), location=head(i).location) :: qualifyNames(tail(i)); } -} diff --git a/grammars/silver/extension/convenience/Productions.sv b/grammars/silver/compiler/extension/convenience/Productions.sv similarity index 93% rename from grammars/silver/extension/convenience/Productions.sv rename to grammars/silver/compiler/extension/convenience/Productions.sv index a8e677067..2a7557d09 100644 --- a/grammars/silver/extension/convenience/Productions.sv +++ b/grammars/silver/compiler/extension/convenience/Productions.sv @@ -1,6 +1,6 @@ -grammar silver:extension:convenience; +grammar silver:compiler:extension:convenience; -import silver:modification:copper; +import silver:compiler:modification:copper; -- "production" short for "abstract production" concrete production productionDclImplicitAbs @@ -65,7 +65,7 @@ top::ProductionDclStmt ::= optn::OptionalName v::ProdVBar end; local newSig :: ProductionSignature = - productionSignature(top.lhsdcl, '::=', rhs, location=rhs.location); + productionSignature(nilConstraint(location=top.location), '=>', top.lhsdcl, '::=', rhs, location=rhs.location); top.proddcls = case opta of diff --git a/grammars/silver/extension/convenience/ShortLocalProdAttrDeclDef.sv b/grammars/silver/compiler/extension/convenience/ShortLocalProdAttrDeclDef.sv similarity index 97% rename from grammars/silver/extension/convenience/ShortLocalProdAttrDeclDef.sv rename to grammars/silver/compiler/extension/convenience/ShortLocalProdAttrDeclDef.sv index 20389ff7f..4fba93f62 100644 --- a/grammars/silver/extension/convenience/ShortLocalProdAttrDeclDef.sv +++ b/grammars/silver/compiler/extension/convenience/ShortLocalProdAttrDeclDef.sv @@ -1,4 +1,4 @@ -grammar silver:extension:convenience; +grammar silver:compiler:extension:convenience; concrete production shortLocalDecl top::ProductionStmt ::= lk::'local' a::Name ht::'::' te::TypeExpr diff --git a/grammars/silver/compiler/extension/convenienceaspects/AbstractSyntax.sv b/grammars/silver/compiler/extension/convenienceaspects/AbstractSyntax.sv new file mode 100644 index 000000000..34ea7e785 --- /dev/null +++ b/grammars/silver/compiler/extension/convenienceaspects/AbstractSyntax.sv @@ -0,0 +1,639 @@ +grammar silver:compiler:extension:convenienceaspects; + +import silver:compiler:modification:let_fix; + +@{- + - From a list of Patterns, makes a PatternList with the right list-shaped productions. + - @param l the list of patterns to modify + - @param defaultLoc the location to provide for the patternListNil construct (which needs a location). + - @return A patternList List-Like Nonterminal instance. + -} +function makePatternListFromListofPatterns +PatternList ::= l::[Pattern] defaultLoc::Location +{ + return + foldr( + \next::Pattern accum::PatternList -> patternList_more(next, ',', accum, location=next.location), + patternList_nil(location=defaultLoc), + l); +} + + + + +@{- + - @param l The next patternList element to extract out patterns from. + - @param accum The accumulating list of patterns. + - @return A list containing the patterns + From a PatternList, makes a proper list of the patterns in them. +-} +function collectPatternsFromPatternList +[Pattern] ::= l::PatternList accum::[Pattern] +{ + return case l of + | patternList_one(p) -> p::accum + | patternList_snoc(ps,_,p) -> collectPatternsFromPatternList(ps, p::accum) + | patternList_more(p,_,ps) -> [p] ++ collectPatternsFromPatternList(ps, accum) + | patternList_nil() -> accum + end; +} + + +@{- + - Extracts out the subpatterns of productions in a patternList, but in a way that doesn't demand attributes. + - @param pl The patternList List-like construct we're extracting subpatterns from. + - @return A patternList List-like construct containing only the subpatterns of the list that was provided. + - @warning Note that the subpatterns being extracted here are only applications of productions. +-} +function extractSubPatternListsFromProdPatterns +PatternList ::= pl::PatternList +{ + return makePatternListFromListofPatterns( + foldr( + append, + [], + (map( + \pat::Pattern -> case pat of + | prodAppPattern(_,_,ps,_) -> collectPatternsFromPatternList(ps,[]) + | _ -> [] + end, + collectPatternsFromPatternList(pl,[])))), + pl.location); +} + + +@{- + - Takes in a regular list of Expr, turns them into an instance of the Exprs production. + - @param l A list of Expr's. + - @param defaultLoc The default location to provide for the ExprsEmpty production (nil-like construct). + - @return A combined Exprs List-like construct made from the Expr's in the input list. +-} +function makeExprsFromExprList +Exprs ::= l::[Expr] defaultLoc::Location +{ + return + if null(l) then exprsEmpty(location=defaultLoc) + else + foldrLastElem( + \leftelem::Expr accum::Exprs -> exprsCons(leftelem,',',accum,location=leftelem.location), + \elem::Expr -> exprsSingle(elem,location=elem.location), + l); +} + + +@{- + - Takes in a regular list of MatchRule, turns them into an instance of the MRuleList production. + - @param l A list of match rules. + - @return A MRuleList list-like construct from the list of match rules. +-} +function makeMRuleListFromListMatchRules +MRuleList ::= l::[MatchRule] +{ + return foldrLastElem( + \leftelem::MatchRule accum::MRuleList -> mRuleList_cons(leftelem,'|',accum, location=leftelem.location), + \a::MatchRule -> mRuleList_one(a,location=a.location), + l); + +} + +@{- + - Given a MRuleList element, transforms it into a regular list of MatchRules + - @param l A List-like construct MRuleList instance. + - @param accum An accumulated list of MatchRule's + - @return A regular list of MatchRule's +-} +function collectMatchRulesfromMRuleList +[MatchRule] ::= l::MRuleList accum::[MatchRule] +{ + return case l of + | mRuleList_one(m) -> m::accum + | mRuleList_cons(h,_,t) -> collectMatchRulesfromMRuleList(t,(h :: accum)) + end; +} + +@{- + - This function goes into a production pattern (if it is one), extracts out the sub pattern for that production, and generates names for each element of that sub pattern. + - e.g Given `silver_matchRule {foo(bar(3,x),y) -> y+1 }` where `foo`,`bar` are productions, it returns `[_gen1,_gen2]` (where the numbers are generated from genInt) + - @param mr An instance of MatchRule + - @param loc A default location to provide for when we use the patternList_nil production. + - @return A list of names where each name corresponds to an argument to the production subpattern. +-} +function makeGeneratedNamesFromMatchRule +[Name] ::= mr::MatchRule loc::Location +{ + local patList::PatternList = + case mr of + | matchRule_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_) -> pl + | matchRuleWhen_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_,_,_) -> pl + | matchRuleWhenMatches_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_,_,_,_,_) -> pl + | _ -> patternList_nil(location=loc) + end; + + return + map(\pat::Pattern -> + name("__generated_" ++ toString(genInt()), loc), + collectPatternsFromPatternList(patList,[])); + +} + + +@{- + - This function goes into a production pattern (if it is one), extracts out the sub pattern for that production, and generates wildcard patterns for each param, giving back a + production pattern with the appropriate number of wildcards. + - @param mr An instance of MatchRule + - @param loc A default location to provide for when we use the patternList_nil production. + - @return A wildcard matchRule with the same number of params as the production pattern. +-} +function makeWildcardsFromMatchRule +PatternList ::= mr::MatchRule loc::Location +{ + local patList::PatternList = + case mr of + | matchRule_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_) -> pl + | matchRuleWhen_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_,_,_) -> pl + | matchRuleWhenMatches_c(patternList_one(prodAppPattern(_,_,pl,_)),_,_,_,_,_,_) -> pl + | _ -> patternList_nil(location=loc) + end; + + return makePatternListFromListofPatterns( + map(\pat::Pattern -> + wildcPattern('_', location=loc), + collectPatternsFromPatternList(patList,[])), + loc); +} + + +@{- + - This function takes in a name and location and returns a concrete definition LHS element that is the result of applying the concrete definition production to them. + - @param name The name being defined. + - @param loc the location of the definition. + - @return a concrete definition LHS element that uses the name and location provided. +-} +function makeDefinitionLHSFromName +DefLHS ::= name::Name loc::Location +{ + return concreteDefLHS(qNameId(name,location=loc), location=loc); +} + +@{- + - This function takes in a name, aspectRHS, and expr, returning a let expression that binds the name we provide to the name of + - the "top" term in our aspect production. This allows one to define convenience aspects that make use of variable named wildcards, and + - have those names be usable in our convenience aspect definition to access other attributes of the top level term. + - @param newName The name being defined. + - @param aspectLHS a convenience aspect LHS expression that contains the name and type of the term that our generated aspect production returns. + - @param e The expression that uses the name we're defining and will be surrounded by let. + - @param loc the location of the definition. + - @return A Let expr that binds the name we provide to the "top" term in our aspect production with let, and surrounds the expression we gave. +-} +function makeLetExprForTopRenaming +Expr ::= newName::Name aspectLHS::Decorated ConvAspectLHS e::Expr loc::Location +{ + return letp( + assignExpr( + newName, + '::', + aspectLHS.aspectType, + '=', + baseExpr(qNameId(aspectLHS.aspectName,location=loc),location=loc), + location=loc), + e, + location=loc); + +} + + +@{- @hide + - @param patList A list of matchrules that represents a grouping of match rules with similar patterns. + - @param aspectLHS a convenience aspect LHS expression that contains the name and type of the term that our generated aspect production returns. + - @param e The Expr on the other side of the arrow of the match rule + - @param loc The location where the aspect pattern is defined + - @return An expression from the wildcard matchrule we can use in making convenience aspects. +-} +function makeWildcardExprFromPatternList +Expr ::= patList::PatternList aspectLHS::Decorated ConvAspectLHS e::Expr loc::Location +{ + return case patList of + | patternList_one(wildcPattern(_)) -> e + | patternList_more(wildcPattern(_),_,_) -> e + | patternList_one(varPattern(name)) -> makeLetExprForTopRenaming(name, aspectLHS, e, loc) + | patternList_more(varPattern(name),_,_) -> makeLetExprForTopRenaming(name, aspectLHS, e, loc) + | _ -> errorExpr([],location=loc) + end; +} + +@{- + - @param rules A list of matchrules that represents a grouping of match rules with similar patterns. + - @param aspectLHS a convenience aspect LHS expression that contains the name and type of the term that our generated aspect production returns. + - @param aspectAttr The aspect attribute we're generating productions for + - @param eqKind The operator that assigns or binds to the attribute + - @param location The location where the aspect pattern is defined + - @param env A decorated environment for looking up production types. + - @return A pair of a single AgDcl that defines the aspect production we're generating, and a list of warnings or errors that came from generating the AgDcl. +-} +function extractAspectAgDclFromRuleList +Pair ::= rules::[MatchRule] aspectLHS::Decorated ConvAspectLHS aspectAttr::QNameAttrOccur eqKind::ConvenienceAspectEquationKind location::Location env::Decorated Env +{ + + local lookupProdInputTypes::([Type] ::= String) = \prodName::String -> + case (getValueDcl(prodName,env)) of + -- Productions that aren't in scope, and names that + -- aren't productions will be caught later in the primitive match. + | [] -> [] + | dcl:: _ -> + if dcl.typeScheme.typerep.isApplicable + then dcl.typeScheme.typerep.inputTypes + else [] + end; + + local makeAspectRHSElemListFromNameAndTypeLists::([AspectRHSElem] ::= [Name] [Type]) = + zipWith(aspectRHSElemFull(_, _, location=location), _, _); + + local makeAspectRHSFromParamsList::(AspectRHS ::= [AspectRHSElem] ) = foldr( + aspectRHSElemCons(_, _, location=location), + aspectRHSElemNil(location=location), + _); + + local makeQNamesFromNames::([QName] ::= [Name]) = map(qNameId(_, location=location),_); + + local makeBaseExprFromQNames::([Expr] ::= [QName]) = map(baseExpr(_,location=location),_); + + -- Transforms it to extract a subpattern and bring it up as the + -- main pattern. + local transformPatternMatchRule::([MatchRule]::=[MatchRule]) = + map((\mRule::MatchRule -> case mRule of + | matchRule_c(pl,arrow,e) -> matchRule_c( + extractSubPatternListsFromProdPatterns(pl), + arrow, + e, + location=location) + | matchRuleWhen_c(pl,whenKWD,cond,arrow,e) -> + matchRuleWhen_c( + extractSubPatternListsFromProdPatterns(pl), + whenKWD, + cond, + arrow, + e, + location=location) + | matchRuleWhenMatches_c(pl,whenKWD,cond,matches,p,arrow,e) -> + matchRuleWhenMatches_c( + extractSubPatternListsFromProdPatterns(pl), + whenKWD, + cond, + matches, + p, + arrow, + e, + location=location) + end), + _); + + -- This function makes the case expression that we insert into + -- the aspect production, which is generated from the original pattern + -- match statement we had, with the production names taken out (so we + -- can use the list of patterns there against the parameters) + local makeParamsCaseExpr::(Expr ::= [Expr] [MatchRule]) = + \paramsCaseSubExpr::[Expr] mRules::[MatchRule] -> + caseExpr_c( + 'case', + makeExprsFromExprList(paramsCaseSubExpr,location), + 'of', + terminal(Opt_Vbar_t, "|"), + makeMRuleListFromListMatchRules(transformPatternMatchRule(mRules)), + 'end', + location=location); + + -- This function makes our aspect production from the Expression, QName, and AspectRHS + -- We've generated elsewhere. + local makeAspectProduction::(AGDcl ::= Expr QName AspectRHS) = + \paramsCaseExpr::Expr prod::QName prodParams::AspectRHS -> + Silver_AGDcl { + aspect production $QName{prod} + $Name{aspectLHS.aspectName}::$TypeExpr{aspectLHS.aspectType} ::= $AspectRHS{prodParams} + { $ProductionStmt{ + eqKind.makeAspectEquation( + makeDefinitionLHSFromName( + aspectLHS.aspectName, + head(rules).location), + aspectAttr, + paramsCaseExpr, + paramsCaseExpr.location)}} + }; + + + + return case rules of + | matchRule_c(patternList_one(prodAppPattern(name,_,_,_)),_,e) :: _ + -> + -- Handling for production patterns + let paramNames :: [Name] = makeGeneratedNamesFromMatchRule(head(rules),location) + in + pair( + makeAspectProduction( + makeParamsCaseExpr( + makeBaseExprFromQNames(makeQNamesFromNames(paramNames)), + rules), + name, + makeAspectRHSFromParamsList( + makeAspectRHSElemListFromNameAndTypeLists( + paramNames, + lookupProdInputTypes(name.name)))), + []) + end + | matchRule_c(patternList_more(prodAppPattern(name,_,_,_),_,_),_,e) :: _ + -> + let paramNames :: [Name] = makeGeneratedNamesFromMatchRule(head(rules),location) + in + pair( + makeAspectProduction( + makeParamsCaseExpr( + makeBaseExprFromQNames(makeQNamesFromNames(paramNames)), + rules), + name, + makeAspectRHSFromParamsList( + makeAspectRHSElemListFromNameAndTypeLists( + paramNames, + lookupProdInputTypes(name.name)))), + []) + end + -- Handling for wildcard patterns + | matchRule_c(patternList_one(wildcPattern(_)),_,e) :: _ -> + pair( + Silver_AGDcl { + aspect default production + $Name{aspectLHS.aspectName}::$TypeExpr{aspectLHS.aspectType} ::= + { $ProductionStmt{eqKind.makeAspectEquation( + makeDefinitionLHSFromName( + aspectLHS.aspectName, + head(rules).location), + aspectAttr, + e, + head(rules).location)}} + }, + []) + | matchRule_c(patternList_more(wildcPattern(_),_,_),_,e) :: _ -> + pair( + Silver_AGDcl { + aspect default production + $Name{aspectLHS.aspectName}::$TypeExpr{aspectLHS.aspectType} ::= + { $ProductionStmt{eqKind.makeAspectEquation( + makeDefinitionLHSFromName( + aspectLHS.aspectName, + head(rules).location), + aspectAttr, + e, + head(rules).location)}} + }, + []) + -- Handling for varpatterns + | matchRule_c(patternList_one(varPattern(name)),_,e) :: _ -> + pair( + Silver_AGDcl { + aspect default production + $Name{aspectLHS.aspectName}::$TypeExpr{aspectLHS.aspectType} ::= + { $ProductionStmt{eqKind.makeAspectEquation( + makeDefinitionLHSFromName( + aspectLHS.aspectName, + head(rules).location), + aspectAttr, + makeLetExprForTopRenaming(name, aspectLHS, e, head(rules).location), + head(rules).location)}} + }, + []) + | matchRule_c(patternList_more(varPattern(name),_,_),_,e) :: _ -> + pair( + Silver_AGDcl { + aspect default production + $Name{aspectLHS.aspectName}::$TypeExpr{aspectLHS.aspectType} ::= + { $ProductionStmt{eqKind.makeAspectEquation( + makeDefinitionLHSFromName( + aspectLHS.aspectName, + head(rules).location), + aspectAttr, + makeLetExprForTopRenaming(name, aspectLHS, e, head(rules).location), + head(rules).location)}} + }, + []) + | _ -> + pair( + emptyAGDcl(location=location), + [err(location,"Patterns in aspect convenience syntax should be productions,wildcards, or varpatterns only")]) + end; +} + + + +@{- + - Compares patterns, if they're both production patterns, compares production name otherwise compares the kind of pattern, (varname or wildcard, mostly). + - As a note, patterns with kinds other than varPattern,Wildcard, or prodAppPattern compare favorably with eachother even if they dont have the same kind, as this function is intended to sort patterns for convenience aspect purposes. + - @param l first pattern we're comparing + - @param r second pattern we're comparing + - @return boolean telling us if two production patterns have the same name, otherwise compares the kind of pattern. +-} +function eqProdNamePattern +Boolean ::= l::Pattern r::Pattern +{ + return case l,r of + | prodAppPattern_named(nameL,_,_,_,_,_),prodAppPattern_named(nameR,_,_,_,_,_) -> + nameR.name == nameL.name + | wildcPattern(_),wildcPattern(_) -> true + | varPattern(_),varPattern(_) -> true + | prodAppPattern_named(_,_,_,_,_,_),_ -> false + | wildcPattern(_),_ -> false + | varPattern(_),_ -> false + -- other patterns compare favorably with eachother, for our purposes they're all the + -- same "kind" as not being a varPattern,wildcard,or prod. + | _,_ -> true + end; +} + + +@{- + - Extracts out the head pattern from the given PatternList. + - @param pList a PatternList construct + - @return The head pattern from the given PatternList + - @warning throws an error if the pattern list is nil. +-} +function extractHeadPatternFromPatternList +Pattern ::= pList::PatternList +{ + return case pList of + | patternList_one(patHead) -> patHead + | patternList_more(patHead,_,_) -> patHead + | patternList_snoc(ps,_,_) -> extractHeadPatternFromPatternList(ps) + | patternList_nil() -> error("No head pattern in patternList_nil()") + end; +} + + + +@{- + - Extracts out the head pattern from the given matchRule. + - @param mRule a MatchRule construct + - @return The head pattern from the given MatchRule +-} +function extractHeadPatternFromMatchRule +Pattern ::= mRule::MatchRule +{ + return case mRule of + | matchRule_c(ps,_,_) -> extractHeadPatternFromPatternList(ps) + | matchRuleWhen_c(ps,_,_,_,_) -> extractHeadPatternFromPatternList(ps) + | matchRuleWhenMatches_c(ps,_,_,_,_,_,_) -> extractHeadPatternFromPatternList(ps) + end; +} + + +@{- + - Compares the head pattern of two match rules, but without demanding attributes + - Modeled after comparison used for AbstractMatchRules + - @param l first match rule + - @param r second match rule + - @return Boolean telling us if the head pattern of two match rules uses the same production (or are equivalent in terms of being a wildcard or varpattern). +-} +function eqHeadPatternMatchRule +Boolean ::= l::MatchRule r::MatchRule +{ + -- The reason this isn't done with attributes is that demanding attributes + -- (from silver core lang elements) has caused infinite loops and mwda errors + -- for me (due to me working with concrete syntax elements, I'm presuming), so I've been + -- avoiding demanding attributes as much as possible. + return + eqProdNamePattern( + extractHeadPatternFromMatchRule(l), + extractHeadPatternFromMatchRule(r)); +} + + +@{- + - @param mRule a MatchRule construct + - @return Boolean indicating whether its a wildcard match rule + - Given a MatchRule, tells you if its a "wildcard" match rule. + - varpatterns aren't called wildcards, but they match everything just the same so they return true here. +-} +function isWildcardMatchRule +Boolean ::= mRule::MatchRule +{ return + case mRule of + | matchRule_c(patternList_one(wildcPattern(_)),_,_) -> true + | matchRule_c(patternList_more(wildcPattern(_),_,_),_,e) -> true + | matchRule_c(patternList_snoc(patternList_one(wildcPattern(_)),_,_),_,e) -> true + | matchRule_c(patternList_one(varPattern(_)),_,_) -> true + | matchRule_c(patternList_more(varPattern(_),_,_),_,e) -> true + | matchRule_c(patternList_snoc(patternList_one(varPattern(_)),_,_),_,e) -> true + | _ -> false + end; +} + + + +@{- + - Gives back a single AgDcl defining all the aspect productions according to the parameters given. + - This is the abstract production for convenience aspects. + - It's generally advised if you intend to use convenience aspects to use them as concrete syntax (using the concrete production starting with `aspect on ...` ) + - @param attr The attribute for which you'd like to define aspect productions for. + - @param aspectLHS a convenience aspect LHS expression that contains the name and type of the term that our generated aspect production returns. + - @param eqKind The operator that assigns or binds to the attribute + - @param ml The Match Rules that define what aspects we'd like to generate. +-} +abstract production convenienceAspects +top::AGDcl ::= attr::QNameAttrOccur aspectLHS::Decorated ConvAspectLHS eqKind::ConvenienceAspectEquationKind ml::MRuleList +{ + top.moduleNames := []; + top.unparse = "aspect " ++ attr.unparse ++ " on " ++ aspectLHS.unparse ++ " " ++ eqKind.unparse ++ " of |" ++ ml.unparse ++ " end"; + + + -- Everything past the first wildcard (or varpattern) gets dropped before grouping of match patterns + local mList::[MatchRule] = reverse(collectMatchRulesfromMRuleList(ml,[])); + local mListUpToFirstWildcard::[MatchRule] = + foldr(\next::MatchRule accum::[MatchRule] -> + if !isWildcardMatchRule(next) then next::accum else [next], + [], + mList); + local mListWildcardAndAfter::[MatchRule] = + dropWhile(\mRule::MatchRule -> !isWildcardMatchRule(mRule),mList); + local mListAfterWildcard::[MatchRule] = + if null(mListWildcardAndAfter) then [] else tail(mListWildcardAndAfter); + + -- groups MatchRules by their kind, which for our purposes is production name, wildcard, or varpattern. + local groupedMRules::[[MatchRule]] = groupBy(eqHeadPatternMatchRule, mListUpToFirstWildcard); + + -- adds a wildcard to each non-wildcard section if available. + local groupedMRulesWithExtraWildcards::[[MatchRule]] = + case mListWildcardAndAfter of + | [] -> groupedMRules + | matchRule_c(patList,_,e) :: _ -> map( + \mList::[MatchRule] -> + case mList of + | matchRule_c(patternList_one(prodAppPattern(name,leftparen,patternList,rightparen)),arrow,_) :: _ -> + let wildcardPatternList :: PatternList = makeWildcardsFromMatchRule(head(mList),(head(mListWildcardAndAfter)).location) + in + let expr :: Expr = makeWildcardExprFromPatternList(patList, aspectLHS, e, (head(mListWildcardAndAfter)).location) + in + mList ++ [matchRule_c(patternList_one(prodAppPattern(name,leftparen,wildcardPatternList,rightparen, location=(head(mListWildcardAndAfter)).location),location=(head(mListWildcardAndAfter)).location),arrow,expr,location=(head(mListWildcardAndAfter)).location)] + end + end + | matchRule_c(patternList_more(prodAppPattern(name,_,wildcardPatternList,_),_,_),arrow,_) :: _ -> + let wildcardPatternList :: PatternList = makeWildcardsFromMatchRule(head(mList),(head(mListWildcardAndAfter)).location) + in + let expr :: Expr = makeWildcardExprFromPatternList(patList, aspectLHS, e, (head(mListWildcardAndAfter)).location) + in + mList ++ [matchRule_c( + patternList_one( + prodAppPattern( + name, + '(', + wildcardPatternList, + ')', + location=(head(mListWildcardAndAfter)).location), + location=(head(mListWildcardAndAfter)).location), + arrow, + expr, + location=(head(mListWildcardAndAfter)).location)] + end + end + | otherwise -> otherwise + end, + groupedMRules) + | firstRule::rest -> groupedMRules + end; + + + local groupExtractResults::[Pair] = map( + extractAspectAgDclFromRuleList(_,aspectLHS,attr,eqKind,top.location, top.env), + groupedMRulesWithExtraWildcards); + + local groupExtractErrors::[Message] = foldr(append, [], (map(snd(_), groupExtractResults))); + + + + local combinedAspectProds::[AGDcl] = map(fst(_),groupExtractResults); + + local combinedAspectDcls::AGDcls = foldr( + consAGDcls(_,_,location=top.location), + nilAGDcls(location=top.location), + combinedAspectProds); + + + top.errors <- if null(mListAfterWildcard) + -- This means that nothing is past the wildcard pattern, which is good. + then groupExtractErrors + -- Something _is_ past the wildcard pattern + else [wrn(((head(mListAfterWildcard)).location),"This pattern and the ones that follow are being ignored.")] + ++ groupExtractErrors; + + -- Errors are filtered out here in a move we call in the business "an infelicity" + -- The errors here arise from inserting the first wildcard pattern we find into + -- the non-default aspect productions we generate. This means that we get semantics + -- of not having incomplete cases in some productions but because we can't check case completeness from + -- The convenience aspects side it produces more errors about overlapping cases, causing us to filter here. + -- If pattern matching and case completeness change this approach might no longer be necessary. + top.errors := filter( + \message::Message -> + -- Note: If you see this error unexpectedly that might mean the string for this error has changed. + case message of + | err(l, "Pattern has overlapping cases!") when l == top.location -> false + | _ -> true + end, + forward.errors); + + forwards to makeAppendAGDclOfAGDcls(combinedAspectDcls); +} diff --git a/grammars/silver/compiler/extension/convenienceaspects/ConcreteSyntax.sv b/grammars/silver/compiler/extension/convenienceaspects/ConcreteSyntax.sv new file mode 100644 index 000000000..395ab92af --- /dev/null +++ b/grammars/silver/compiler/extension/convenienceaspects/ConcreteSyntax.sv @@ -0,0 +1,79 @@ +grammar silver:compiler:extension:convenienceaspects; +import silver:langutil:pp; +import silver:compiler:modification:collection; +import silver:compiler:extension:constructparser; + +@{- @hide -} +synthesized attribute makeAspectEquation::(ProductionStmt ::= DefLHS QNameAttrOccur Expr Location); + +@{- + - A nonterminal describing what binding to use on the attribute in the generated aspects. +-} +nonterminal ConvenienceAspectEquationKind with location, unparse, pp, makeAspectEquation; + +@{- @hide -} +concrete productions top::ConvenienceAspectEquationKind +| 'using' '=' +{ + top.makeAspectEquation = attributeDef(_,'.',_,'=',_,';',location=_); + top.pp = pp"using ="; + top.unparse = "using ="; +} +| 'using' ':=' +{ + top.makeAspectEquation = attrContainsBase(_,'.',_,':=',_,';',location=_); + top.pp = pp"using :="; + top.unparse = "using :="; +} +| 'using' '<-' +{ + top.makeAspectEquation = attrContainsAppend(_,'.',_,'<-',_,';', location=_); + top.pp = pp"using <-"; + top.unparse = "using <-"; +} +| +{ + top.makeAspectEquation = attributeDef(_,'.',_,'=',_,';',location=_); + top.pp = pp""; + top.unparse = ""; +} + + +@{- @hide -} +synthesized attribute aspectName::Name; +@{- @hide -} +synthesized attribute aspectType::TypeExpr; + +@{- + - Nonterminal for the name and type of the term for which you're constructing aspect productions for. -} +nonterminal ConvAspectLHS with aspectName, aspectType, unparse; +@{- @hide -} +concrete productions top::ConvAspectLHS +| name::Name '::' ty::TypeExpr +{ + top.aspectType = ty; + top.aspectName = name; + top.unparse = name.unparse ++ "::" ++ ty.unparse; +} +| ty::TypeExpr +{ + top.aspectType = ty; + top.aspectName = name("__generatedTop_" ++ toString(genInt()), ty.location); + top.unparse = ty.unparse; +} + + +@{- Takes in the following: + - - attr: The attribute you're defining aspect productions for. + - - aspectLHS: the type for your aspect productions, as well as a custom name for if you define it. + - - eqKind: The binding method for defining the new attribute within the generated aspect productions. + - - ml: A List of MatchRules that describe the aspects productions you'd like to make. + + - And returns a a single AgDcl defining all the aspect productions according to the parameters given. + - This is the concrete syntax for defining convenience aspects. +-} +concrete production convenienceAspects_c +top::AGDcl ::= 'aspect' attr::QNameAttrOccur 'on' aspectLHS::ConvAspectLHS eqKind::ConvenienceAspectEquationKind 'of' Opt_Vbar_t ml::MRuleList 'end' ';' +{ + forwards to convenienceAspects(attr, aspectLHS, eqKind, ml, location=top.location); +} diff --git a/grammars/silver/compiler/extension/convenienceaspects/Project.sv b/grammars/silver/compiler/extension/convenienceaspects/Project.sv new file mode 100644 index 000000000..38040336e --- /dev/null +++ b/grammars/silver/compiler/extension/convenienceaspects/Project.sv @@ -0,0 +1,13 @@ +grammar silver:compiler:extension:convenienceaspects; + +imports silver:core; +imports silver:compiler:analysis:typechecking:core; +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:extension:patternmatching; +imports silver:compiler:extension:silverconstruction; +imports silver:compiler:modification:defaultattr; + +exports silver:compiler:extension:strategyattr:convenience; diff --git a/grammars/silver/extension/deprecation/BuildWith.sv b/grammars/silver/compiler/extension/deprecation/BuildWith.sv similarity index 88% rename from grammars/silver/extension/deprecation/BuildWith.sv rename to grammars/silver/compiler/extension/deprecation/BuildWith.sv index c7dfdd2d1..361aeadd3 100644 --- a/grammars/silver/extension/deprecation/BuildWith.sv +++ b/grammars/silver/compiler/extension/deprecation/BuildWith.sv @@ -1,4 +1,4 @@ -grammar silver:extension:deprecation; +grammar silver:compiler:extension:deprecation; terminal Build_kwd 'build' lexer classes {MODSTMT}; diff --git a/grammars/silver/extension/deprecation/Deprecation.sv b/grammars/silver/compiler/extension/deprecation/Deprecation.sv similarity index 77% rename from grammars/silver/extension/deprecation/Deprecation.sv rename to grammars/silver/compiler/extension/deprecation/Deprecation.sv index 37c15f637..0701d2ec4 100644 --- a/grammars/silver/extension/deprecation/Deprecation.sv +++ b/grammars/silver/compiler/extension/deprecation/Deprecation.sv @@ -1,7 +1,7 @@ -grammar silver:extension:deprecation; +grammar silver:compiler:extension:deprecation; -imports silver:definition:core; -imports silver:definition:env; +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; terminal Deprecated_kwd 'deprecated' lexer classes {KEYWORD}; diff --git a/grammars/silver/extension/deprecation/NameTicks.sv b/grammars/silver/compiler/extension/deprecation/NameTicks.sv similarity index 96% rename from grammars/silver/extension/deprecation/NameTicks.sv rename to grammars/silver/compiler/extension/deprecation/NameTicks.sv index 6245d6da9..4c8065b24 100644 --- a/grammars/silver/extension/deprecation/NameTicks.sv +++ b/grammars/silver/compiler/extension/deprecation/NameTicks.sv @@ -1,4 +1,4 @@ -grammar silver:extension:deprecation; +grammar silver:compiler:extension:deprecation; terminal IdTick_t /[A-Za-z][A-Za-z0-9\_]*[\']/ lexer classes {IDENTIFIER}; terminal IdTickTick_t /[A-Za-z][A-Za-z0-9\_]*[\'][\']/ lexer classes {IDENTIFIER}; diff --git a/grammars/silver/extension/deprecation/ProductionSemi.sv b/grammars/silver/compiler/extension/deprecation/ProductionSemi.sv similarity index 87% rename from grammars/silver/extension/deprecation/ProductionSemi.sv rename to grammars/silver/compiler/extension/deprecation/ProductionSemi.sv index 74c3375b2..b6fdc8eef 100644 --- a/grammars/silver/extension/deprecation/ProductionSemi.sv +++ b/grammars/silver/compiler/extension/deprecation/ProductionSemi.sv @@ -1,4 +1,4 @@ -grammar silver:extension:deprecation; +grammar silver:compiler:extension:deprecation; concrete production emptyProductionBodySemi top::ProductionBody ::= ';' diff --git a/grammars/silver/compiler/extension/do_notation/Project.sv b/grammars/silver/compiler/extension/do_notation/Project.sv new file mode 100644 index 000000000..0b51ef27d --- /dev/null +++ b/grammars/silver/compiler/extension/do_notation/Project.sv @@ -0,0 +1,15 @@ +grammar silver:compiler:extension:do_notation; + +imports silver:util:treeset as ts; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:type; + +imports silver:compiler:extension:convenience; +imports silver:compiler:extension:tuple; +imports silver:compiler:modification:lambda_fn; +imports silver:compiler:modification:let_fix; +imports silver:compiler:metatranslation; + diff --git a/grammars/silver/compiler/extension/do_notation/Syntax.sv b/grammars/silver/compiler/extension/do_notation/Syntax.sv new file mode 100644 index 000000000..ecf8f0449 --- /dev/null +++ b/grammars/silver/compiler/extension/do_notation/Syntax.sv @@ -0,0 +1,280 @@ +grammar silver:compiler:extension:do_notation; + +concrete production do_c +top::Expr ::= 'do' '{' body::DoBody '}' +{ + top.unparse = s"do {${body.unparse}}"; + + forwards to body.transform; +} + +{- + Like do, but rewrites mutually recursive bindings using mfix. + For example + mdo { + x :: Integer <- something; + let y :: Boolean = bar(x, z); + z :: Expr <- baz(y); + thing(y); + return z; + } + would transform into + do { + x :: Integer <- something; + _rec_items_123 :: (Boolean, Expr) <- + mfix(\ _rec_items_123 :: (Boolean, Expr) -> + do { + let y :: Boolean = _rec_items_123.1; + let z :: Expr = _rec_items_123.2; + let y :: Boolean = bar(x, z); + z :: Expr <- baz(y); + }); + let y :: Boolean = _rec_items_123.1; + let z :: Expr = _rec_items_123.2; + thing(y); + return z; + } + See https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/recursive_do.html#the-mdo-notation +-} +concrete production mdo_c +top::Expr ::= 'mdo' '{' body::DoBody '}' +{ + top.unparse = s"mdo {${body.unparse}}"; + + body.boundVarEnv = mempty; + body.allBoundVars = body.boundVars; + body.recVars = mempty; + body.recRes = error("First binding cannot have unbound vars"); + + forwards to do_c('do', '{', body.mdoTransform, '}', location=top.location); +} + +-- The set of variables that have been bound within this do body so far +inherited attribute boundVarEnv::ts:Set; +-- The set of all variables that will be bound within this do body +inherited attribute allBoundVars::ts:Set; +-- The set of variables that are bound within a binding +monoid attribute boundVars::ts:Set; +-- The set of free variables contained in bindings +synthesized attribute bindingFreeVars::ts:Set; + +-- Can this do body be translated using ap/map instead of bind +synthesized attribute isApplicative::Boolean; +-- Parameter bindings that will be used in applicative translation +synthesized attribute appBindings::[ProductionRHSElem]; +-- Expressions that will be bound in applicative translation +synthesized attribute appExprs::[Expr]; +-- The final result in applicative translation +synthesized attribute appResult::Expr; + +-- Translation of a do expression into function calls and lambdas, +-- threaded backwards through a do body +inherited attribute transformIn::Expr; +synthesized attribute transform::Expr; + +-- The set of variables in this segment (series of mutually-recursive bindings) +-- that refer to a future binding +inherited attribute recVars::ts:Set; +-- Bindings in this segment that are referenced by a previous binding +synthesized attribute recBindings::[(String, TypeExpr)]; +-- A do body returning a tuple of all rec bindings, +-- to be inserted at the end of the do body generated for a segment +inherited attribute recRes::DoBody; +-- The do body generated for a segment +synthesized attribute recBody::DoBody; +-- A transformation on a do body, wrapping segments in mfix +synthesized attribute mdoTransform::DoBody; + +nonterminal DoBody with + location, unparse, frame, boundVarEnv, allBoundVars, boundVars, bindingFreeVars, + isApplicative, appBindings, appExprs, appResult, transform, + recVars, recBindings, recRes, recBody, mdoTransform; + +nonterminal DoBinding with + location, unparse, frame, boundVars, freeVars, + isApplicative, appBindings, appExprs, + transform, transformIn, + recBindings; + +propagate boundVars on DoBody, DoBinding; +propagate freeVars on DoBinding; + +concrete production consDoBody +top::DoBody ::= b::DoBinding rest::DoBody +{ + top.unparse = s"${b.unparse} ${rest.unparse}"; + top.bindingFreeVars = b.freeVars ++ ts:difference(rest.bindingFreeVars, b.boundVars); + top.isApplicative = + b.isApplicative && ts:isEmpty(ts:intersect(b.boundVars, rest.bindingFreeVars)) && + rest.isApplicative; + top.appBindings = b.appBindings ++ rest.appBindings; + top.appExprs = b.appExprs ++ rest.appExprs; + top.appResult = rest.appResult; + + rest.boundVarEnv = ts:union(b.boundVars, top.boundVarEnv); + rest.allBoundVars = top.allBoundVars; + + b.transformIn = rest.transform; + top.transform = + if top.isApplicative + then + foldl( + \ trans::Expr e::Expr -> mkStrFunctionInvocation(top.location, "silver:core:ap", [trans, e]), + mkStrFunctionInvocation(top.location, "silver:core:map", [ + foldr( + \ el::ProductionRHSElem trans::Expr -> + lambdap( + productionRHSCons(el, productionRHSNil(location=top.location), location=top.location), + trans, location=top.location), + top.appResult, top.appBindings), + head(top.appExprs)]), + tail(top.appExprs)) + else b.transform; + + -- Variables in b that are bound in the enclosing do body, but have not yet been bound + local newRecVars::ts:Set = + ts:difference(ts:intersect(b.freeVars, top.allBoundVars), top.boundVarEnv); + -- All recursvely bound variables that have not yet been bound + local allRecVars::ts:Set = top.recVars ++ newRecVars; + rest.recVars = ts:difference(allRecVars, b.boundVars); + + top.recBindings = + b.recBindings ++ + if ts:isEmpty(rest.recVars) then [] else rest.recBindings; + + rest.recRes = + if ts:isEmpty(top.recVars) && !ts:isEmpty(newRecVars) + then + finalReturnDoBody('return', + foldr1( + \ e1::Expr e2::Expr -> Silver_Expr { silver:core:pair($Expr{e1}, $Expr{e2}) }, + map(\ item::(String, TypeExpr) -> Silver_Expr { $name{item.1} }, top.recBindings)), + ';', location=top.location) + else top.recRes; + + top.recBody = consDoBody(b, + if ts:isEmpty(rest.recVars) + then rest.recRes + else rest.recBody, + location=top.location); + + local recVarName::String = s"_rec_items_${toString(genInt())}"; + local recVarType::TypeExpr = + foldr1( + \ t1::TypeExpr t2::TypeExpr -> + Silver_TypeExpr { silver:core:Pair<$TypeExpr{t1} $TypeExpr{t2}> }, + map(snd, top.recBindings)); + local wrapUnpackRecBindings::(DoBody ::= DoBody) = + foldr( + consDoBody(_, _, location=top.location), + _, + zipWith( + \ i::Integer item::(String, TypeExpr) -> + letDoBinding( + 'let', name(item.1, top.location), '::', item.2, '=', + select(Silver_Expr { $name{recVarName} }, 1, i + 1, length(top.recBindings)), ';', + location=top.location), + range(0, length(top.recBindings)), + top.recBindings)); + top.mdoTransform = + if !ts:isEmpty(top.recVars) + then rest.mdoTransform + else if !ts:isEmpty(newRecVars) + then consDoBody( + bindDoBinding( + name(recVarName, top.location), '::', recVarType, '<-', + Silver_Expr { + mfix( + \ $name{recVarName}::$TypeExpr{recVarType} -> + $Expr{ + do_c('do', '{', wrapUnpackRecBindings(top.recBody), '}', location=top.location) + }) + }, ';', + location=top.location), + wrapUnpackRecBindings(rest.mdoTransform), + location=top.location) + else consDoBody(b, rest.mdoTransform, location=top.location); +} + +concrete production finalExprDoBody +top::DoBody ::= e::Expr ';' +{ + top.unparse = s"${e.unparse};"; + top.bindingFreeVars = mempty; + top.isApplicative = false; + top.appBindings = error("Not applicative"); + top.appExprs = error("Not applicative"); + top.appResult = error("Not applicative"); + top.transform = e; + top.recBindings = []; + top.recBody = top.recRes; + top.mdoTransform = top; +} + +concrete production finalReturnDoBody +top::DoBody ::= 'return' e::Expr ';' +{ + top.unparse = s"return ${e.unparse};"; + top.bindingFreeVars = mempty; + top.isApplicative = true; + top.appBindings = []; + top.appExprs = []; + top.appResult = e; + top.transform = mkStrFunctionInvocation(top.location, "silver:core:pure", [e]); + top.recBindings = []; + top.recBody = top.recRes; + top.mdoTransform = top; +} + +concrete production bindDoBinding +top::DoBinding ::= n::Name DoDoubleColon_t t::TypeExpr '<-' e::Expr ';' +{ + top.unparse = s"${n.unparse}::${t.unparse} <- ${e.unparse};"; + top.boundVars <- ts:fromList([n.name]); + top.isApplicative = true; + top.appBindings = [productionRHSElem(n, terminal(ColonColon_t, "::"), t, location=top.location)]; + top.appExprs = [e]; + + local cont :: Expr = + lambdap( + productionRHSCons( + productionRHSElem(n, terminal(ColonColon_t, "::"), t, location=top.location), + productionRHSNil(location=top.location), + location=top.location), + top.transformIn, + location=top.location); + top.transform = mkStrFunctionInvocation(top.location, "silver:core:bind", [e, cont]); + + top.recBindings = [(n.name, t)]; +} + +concrete production exprDoBinding +top::DoBinding ::= e::Expr ';' +{ + top.unparse = s"${e.unparse};"; + top.isApplicative = true; + top.appBindings = + [productionRHSElemType(typerepTypeExpr(freshType(), location=top.location), location=top.location)]; + top.appExprs = [e]; + top.transform = mkStrFunctionInvocation(top.location, "silver:core:applySecond", [e, top.transformIn]); + + top.recBindings = []; +} + +concrete production letDoBinding +top::DoBinding ::= 'let' n::Name '::' t::TypeExpr '=' e::Expr ';' +{ + top.unparse = s"let ${n.unparse}::${t.unparse} = ${e.unparse};"; + top.boundVars <- ts:fromList([n.name]); + top.isApplicative = false; + top.appBindings = error("Not applicative"); + top.appExprs = error("Not applicative"); + + top.transform = + letp( + assignExpr(n, terminal(ColonColon_t, "::"), t, '=', e, location=top.location), + top.transformIn, + location=top.location); + + top.recBindings = [(n.name, t)]; +} diff --git a/grammars/silver/compiler/extension/do_notation/Terminals.sv b/grammars/silver/compiler/extension/do_notation/Terminals.sv new file mode 100644 index 000000000..a820caab0 --- /dev/null +++ b/grammars/silver/compiler/extension/do_notation/Terminals.sv @@ -0,0 +1,8 @@ +grammar silver:compiler:extension:do_notation; + +marking terminal Do_kwd 'do' lexer classes {KEYWORD,RESERVED}; +marking terminal MDo_kwd 'mdo' lexer classes {KEYWORD,RESERVED}; + +terminal LArrow_t '<-' lexer classes {SPECOP}; +terminal DoDoubleColon_t '::' lexer classes {SPECOP}, precedence=15; -- Higher precedence than cons +disambiguate ColonColon_t, DoDoubleColon_t { pluck DoDoubleColon_t; } diff --git a/grammars/silver/compiler/extension/doc/Project.sv b/grammars/silver/compiler/extension/doc/Project.sv new file mode 100644 index 000000000..20ae8c31a --- /dev/null +++ b/grammars/silver/compiler/extension/doc/Project.sv @@ -0,0 +1,4 @@ +grammar silver:compiler:extension:doc; + +exports silver:compiler:extension:doc:core; +exports silver:compiler:extension:doc:driver; diff --git a/grammars/silver/compiler/extension/doc/core/AGDcl.sv b/grammars/silver/compiler/extension/doc/core/AGDcl.sv new file mode 100644 index 000000000..31863ea4e --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/AGDcl.sv @@ -0,0 +1,344 @@ +grammar silver:compiler:extension:doc:core; + +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:modification:ffi; +imports silver:compiler:extension:autoattr; +imports silver:compiler:modification:defaultattr; +imports silver:compiler:modification:copper; +imports silver:compiler:modification:copper_mda; +imports silver:compiler:definition:flow:syntax; +imports silver:compiler:modification:collection; + +@@{- @warning INTENDED TO BE INTERFERED WITH like .pp. -} +synthesized attribute docUnparse::String occurs on AGDcl; +@@{- @warning INTENDED TO BE INTERFERED WITH like .pp. -} +synthesized attribute docForName::String occurs on AGDcl; + +aspect production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + top.docForName = id.name; + top.docUnparse = "`function " ++ id.name ++ "`   (`" ++ ns.unparse ++ "`)"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + top.docForName = id.name; + top.docUnparse = "`aspect function " ++ id.name ++ "`   (`" ++ ns.unparse ++ "`)"; + top.docDcls := []; + top.docs := []; -- Not considered to need docs +} + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + top.docForName = id.name; + top.docUnparse = "`abstract production " ++ id.name ++ "`   (`" ++ ns.unparse ++ "`)"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production concreteProductionDcl +top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody +{ + top.docForName = id.name; + top.docUnparse = "`concrete production " ++ id.name ++ "`   (`" ++ ns.unparse ++ "`)"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production attributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.docForName = a.name; + top.docUnparse = s"`synthesized attribute ${a.name}${tl.unparse} :: ${te.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production attributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.docForName = a.name; + top.docUnparse = s"`inherited attribute ${a.name}${tl.unparse} :: ${te.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production nonterminalDcl +top::AGDcl ::= quals::NTDeclQualifiers 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' +{ + top.docForName = id.name; + top.docUnparse = s"`nonterminal ${id.name}${tl.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + top.docForName = ""; + top.docUnparse = "`aspect production " ++ id.name ++ "`   (`" ++ ns.unparse ++ "`)"; + top.docDcls := []; + top.docs := []; -- Not considered to need docs +} + +aspect production terminalDclDefault +top::AGDcl ::= t::TerminalKeywordModifier id::Name r::RegExpr tm::TerminalModifiers +{ + top.docForName = id.name; + top.docUnparse = s"`terminal ${id.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production lexerClassDecl +top::AGDcl ::= 'lexer' 'class' id::Name modifiers::LexerClassModifiers ';' +{ + top.docForName = id.name; + top.docUnparse = s"`lexer class ${id.unparse} ${modifiers.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production parserDcl +top::AGDcl ::= 'parser' n::Name '::' t::TypeExpr '{' m::ParserComponents '}' +{ + top.docForName = n.name; + top.docUnparse = s"`parser ${n.unparse} :: ${t.unparse}`"; + top.docDcls := [pair(n.name, docDclInfo(n.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production annotationDcl +top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.docForName = a.name; + top.docUnparse = s"`annotation ${a.unparse}${tl.unparse} :: ${te.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production equalityAttributeDcl +top::AGDcl ::= 'equality' 'attribute' syn::Name 'with' inh::QName ';' +{ + top.docForName = syn.name; + top.docUnparse = s"`equality attribute ${syn.name} with ${inh.name}`"; + top.docDcls := [pair(syn.name, docDclInfo(syn.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production orderingAttributeDcl +top::AGDcl ::= 'ordering' 'attribute' keySyn::Name ',' syn::Name 'with' inh::QName ';' +{ + top.docForName = keySyn.name++" and "++syn.name++" (ordering pair)"; + top.docUnparse = s"`ordering attribute ${keySyn.name}, ${syn.name} with ${inh.name}`"; + top.docDcls := [pair(keySyn.name, docDclInfo(keySyn.name, sourceLocation=top.location, sourceGrammar=top.grammarName)), + pair(syn.name, docDclInfo(syn.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production monoidAttributeDcl +top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' e::Expr ',' _ ';' +{ + top.docForName = a.name; + top.docUnparse = s"`monoid attribute ${a.unparse}${tl.unparse} :: ${te.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production threadedAttributeDcl +top::AGDcl ::= 'threaded' 'attribute' inh::Name ',' syn::Name tl::BracketedOptTypeExprs '::' te::TypeExpr d::OptDirectionMod ';' +{ + top.docForName = inh.name++" and "++syn.name++" (threaded pair)"; + top.docUnparse = s"`threaded attribute ${inh.name}, ${syn.name}${tl.unparse} :: ${te.unparse} direction=${if d.reversed then "right to left" else "left to right"}`"; + top.docDcls := [pair(inh.name, docDclInfo(inh.name, sourceLocation=top.location, sourceGrammar=top.grammarName)), + pair(syn.name, docDclInfo(syn.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production functorAttributeDcl +top::AGDcl ::= 'functor' 'attribute' a::Name ';' +{ + top.docForName = a.name; + top.docUnparse = s"`monoid attribute ${a.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +-- Bizzare mismatching signature errors when uncommented: +-- aspect production unificationAttributeDcl +-- top::AGDcl ::= _ _ inh::Name i::TypeExpr _ synPartial::Name _ syn::Name _ _ qs::QNames _ +-- { +-- top.docForName = inh.name++" and "++synPartial.name++" and "++syn.name++" (unification set)"; +-- top.docUnparse = s"`unification attribute ${inh.name}, ${syn.name}, ${syn.name}`"; +-- top.docDcls := [pair(inh.name, docDclInfo(inh.name, sourceLocation=top.location, sourceGrammar=top.grammarName)), +-- pair(syn.name, docDclInfo(syn.name, sourceLocation=top.location, sourceGrammar=top.grammarName)), +-- pair(synPartial.name, docDclInfo(synPartial.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; +-- top.docs := [mkUndocumentedItem(top.docForName, top)]; +-- } + +aspect production aspectDefaultProduction +top::AGDcl ::= 'aspect' 'default' 'production' ns::AspectDefaultProductionSignature body::ProductionBody +{ + top.docForName = "aspect default production "++ns.namedSignature.outputElement.typerep.typeName; + top.docUnparse = s"`aspect default production ${ns.unparse}`"; + top.docDcls := []; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production ffiTypeDclUgly +top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs 'foreign' '=' trans::String_t ';' +{ + top.docForName = id.name; + top.docUnparse = s"`ffi type ${id.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + + +aspect production defaultAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production emptyAGDcl +top::AGDcl ::= +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production flowtypeDcl +top::AGDcl ::= 'flowtype' nt::QName '=' specs::FlowSpecs ';' +{ + -- TODO: Enable documenting these? + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production flowtypeAttrDcl +top::AGDcl ::= 'flowtype' attr::FlowSpec 'on' nts::NtList ';' +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production disambiguationGroupDcl +top::AGDcl ::= 'disambiguate' terms::TermList acode::ActionCode_c +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production attributeDclParser +top::AGDcl ::= 'parser' 'attribute' a::Name '::' te::TypeExpr 'action' acode::ActionCode_c ';' +{ + top.docForName = a.name; + top.docUnparse = s"`parser attribute ${a.unparse}`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production attributeAspectParser +top::AGDcl ::= 'aspect' 'parser' 'attribute' a::QName 'action' acode::ActionCode_c ';' +{ + top.docForName = a.name; + top.docUnparse = s"`aspect parser attribute ${a.unparse}`"; + top.docDcls := []; + top.docs := []; -- Not considered to need docs +} + +aspect production errorAGDcl +top::AGDcl ::= e::[Message] +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production defsAGDcl +top::AGDcl ::= d::[Def] +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production appendAGDcl +top::AGDcl ::= h::AGDcl t::AGDcl +{ + -- Should be overridden if relevant on what forwards to this + top.docForName = h.docForName; + top.docUnparse = h.docUnparse; +} + +aspect production jarNameDcl +top::AGDcl ::= n::Name +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production errorAttributionDcl +top::AGDcl ::= msg::[Message] at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} + +aspect production globalValueDclConcrete +top::AGDcl ::= 'global' id::Name '::' cl::ConstraintList '=>' t::TypeExpr '=' e::Expr ';' +{ + top.docForName = id.name; + top.docUnparse = s"`global ${id.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production typeAliasDecl +top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs '=' te::TypeExpr ';' +{ + top.docForName = id.name; + top.docUnparse = s"`type ${id.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(id.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production collectionAttributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + top.docForName = a.name; + top.docUnparse = s"`synthesized attribute ${a.unparse} (collection)`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production collectionAttributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + top.docForName = a.name; + top.docUnparse = s"`synthesized attribute ${a.unparse} (collection)`"; + top.docDcls := [pair(a.name, docDclInfo(a.name, sourceLocation=top.location, sourceGrammar=top.grammarName))]; + top.docs := [mkUndocumentedItem(top.docForName, top)]; +} + +aspect production copperMdaDcl +top::AGDcl ::= 'copper_mda' testname::Name '(' orig::QName ')' '{' m::ParserComponents '}' +{ + top.docForName = ""; + top.docUnparse = ""; + top.docs := []; +} diff --git a/grammars/silver/compiler/extension/doc/core/ArgNames.sv b/grammars/silver/compiler/extension/doc/core/ArgNames.sv new file mode 100644 index 000000000..c15ee41eb --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/ArgNames.sv @@ -0,0 +1,81 @@ +grammar silver:compiler:extension:doc:core; + +synthesized attribute argNames::[String] +occurs on FunctionSignature, ProductionSignature, AspectProductionSignature, + AspectFunctionSignature, AspectRHS, AspectRHSElem, ProductionRHS, ProductionRHSElem; + +aspect production functionSignature +top::FunctionSignature ::= cl::ConstraintList '=>' lhs::FunctionLHS '::=' rhs::ProductionRHS +{ + top.argNames = rhs.argNames; +} + +aspect production productionSignature +top::ProductionSignature ::= cl::ConstraintList '=>' lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + top.argNames = rhs.argNames; +} + +aspect production productionRHSNil +top::ProductionRHS ::= +{ + top.argNames = []; +} + +aspect production productionRHSCons +top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS +{ + top.argNames = h.argNames ++ t.argNames; +} + +aspect production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + top.argNames = [id.name]; +} + +aspect production productionRHSElemType +top::ProductionRHSElem ::= t::TypeExpr +{ + top.argNames = []; -- Don't consider unnamed parameters against count +} + + + + +aspect production aspectProductionSignature +top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS +{ + top.argNames = rhs.argNames; +} + +aspect production aspectFunctionSignature +top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS +{ + top.argNames = rhs.argNames; +} + + +aspect production aspectRHSElemNil +top::AspectRHS ::= +{ + top.argNames = []; +} + +aspect production aspectRHSElemCons +top::AspectRHS ::= h::AspectRHSElem t::AspectRHS +{ + top.argNames = h.argNames ++ t.argNames; +} + +aspect production aspectRHSElemNone +top::AspectRHSElem ::= '_' +{ + top.argNames = []; -- Don't consider _ parameters against count +} + +aspect production aspectRHSElemFull +top::AspectRHSElem ::= id::Name t::Type +{ + top.argNames = [id.name]; +} diff --git a/grammars/silver/compiler/extension/doc/core/CommentItem.sv b/grammars/silver/compiler/extension/doc/core/CommentItem.sv new file mode 100644 index 000000000..b5c2914b4 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/CommentItem.sv @@ -0,0 +1,70 @@ +grammar silver:compiler:extension:doc:core; + +synthesized attribute body :: String; +synthesized attribute loc :: Location; +synthesized attribute stub :: Boolean; +synthesized attribute docNames :: [String]; +synthesized attribute undocNames :: [String]; +nonterminal CommentItem with body, loc, doEmit, stub, docNames, undocNames; + +{- +Used by other productions to construct +items for the list of CommentItems that +AGDcl is given +-} + +function sanitizeAnchor +String ::= n::String +{ + return substitute(" ", "_", substitute("[", "_", substitute("]", "_", + substitute("<", "_", substitute(">", "_", substitute("(", "_", + substitute(")", "_", n))))))); +} + +function makeStub +String ::= forName::String docUnparse::String grammarName::String loc::Location +{ + return +s"""## ${docUnparse} {#${sanitizeAnchor(forName)}} +Contained in grammar `[${grammarName}]`. Defined at [${substitute(":", "/", grammarName)}/${loc.filename} line ${toString(loc.line)}](https://github.com/melt-umn/silver/blob/develop/grammars/${substitute(":", "/", grammarName)}/${loc.filename}#L${toString(loc.line)})."""; +} + +abstract production dclCommentItem +top::CommentItem ::= forName::String docUnparse::String grammarName::String location::Location body::Decorated DclComment +{ + top.body = body.indentBy ++ substitute("\n", "\n"++body.indentBy, + makeStub(forName, docUnparse, grammarName, location) ++ "\n\n" ++ body.body); + top.loc = location; + top.doEmit = body.doEmit; + top.stub = false; + top.docNames = [forName]; + top.undocNames = []; +} + +abstract production standaloneDclCommentItem +top::CommentItem ::= body::Decorated DclComment +{ + top.body = substitute("\n", "\n"++body.indentBy, body.body); + top.loc = body.location; + top.doEmit = body.doEmit; + top.stub = false; + top.docNames = []; + top.undocNames = []; +} + +abstract production undocumentedItem +top::CommentItem ::= forName::String docUnparse::String grammarName::String location::Location +{ + top.body = makeStub(forName, docUnparse, grammarName, location) ++ "\n\n (Undocumented.)"; + top.loc = location; + top.doEmit = true; + top.stub = true; + top.docNames = []; + top.undocNames = [forName]; +} + +function mkUndocumentedItem +CommentItem ::= f::String t::Decorated AGDcl +{ + return undocumentedItem(f, t.docUnparse, t.grammarName, t.location); +} diff --git a/grammars/silver/compiler/extension/doc/core/DocConfig.sv b/grammars/silver/compiler/extension/doc/core/DocConfig.sv new file mode 100644 index 000000000..cf1b052f4 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/DocConfig.sv @@ -0,0 +1,142 @@ +grammar silver:compiler:extension:doc:core; + +@{- + - Represents a single setting (key = value) of a doc configuration option. + - Some are file-scope, and some are grammar-scope (see @link[fileScope].) + -} +nonterminal DocConfigSetting; + +@{- + - Is this @link[DocConfigSetting] local to the file (e.g. @@title) or to the + - grammar (e.g. @@grammarTitle)? + -} +synthesized attribute fileScope::Boolean occurs on DocConfigSetting; + +abstract production splitConfig +top::DocConfigSetting ::= v::Boolean +{ + top.fileScope = false; +} + +abstract production fileSplitConfig +top::DocConfigSetting ::= v::Boolean +{ + top.fileScope = true; +} + +abstract production weightConfig +top::DocConfigSetting ::= v::Integer +{ + top.fileScope = true; +} + +abstract production grammarWeightConfig +top::DocConfigSetting ::= v::Integer +{ + top.fileScope = false; +} + +abstract production grammarTitleConfig +top::DocConfigSetting ::= v::String +{ + top.fileScope = false; +} + +abstract production titleConfig +top::DocConfigSetting ::= v::String +{ + top.fileScope = true; +} + +abstract production grammarNoDocsConfig +top::DocConfigSetting ::= v::Boolean +{ + top.fileScope = false; +} + +abstract production fileNoDocsConfig +top::DocConfigSetting ::= v::Boolean +{ + top.fileScope = true; +} + +abstract production tocConfig +top::DocConfigSetting ::= v::Boolean +{ + top.fileScope = true; +} + + +-- a grammar with @excludeGrammar containing file(s) with @excludeFile false will +-- only emit docs for that file(s) +function doesExcludeFile +Boolean ::= args::[DocConfigSetting] +{ + return case args of + | fileNoDocsConfig(v)::_ -> v + | grammarNoDocsConfig(true)::_ -> true + | _::r -> doesExcludeFile(r) + | [] -> false + end; +} + +function getFileTitle +String ::= args::[DocConfigSetting] fallback::String +{ + return case args of + | titleConfig(x)::_ -> x + | _::r -> getFileTitle(r, fallback) + | [] -> fallback + end; +} + +function getGrammarTitle +String ::= args::[DocConfigSetting] fallback::String +{ + return case args of + | grammarTitleConfig(x)::_ -> x + | _::r -> getGrammarTitle(r, fallback) + | [] -> fallback + end; +} + +function getFileWeight +Integer ::= args::[DocConfigSetting] +{ + return case args of + | weightConfig(x)::_ -> x + | _::r -> getFileWeight(r) + | [] -> 0 + end; +} + +function getGrammarWeight +Integer ::= args::[DocConfigSetting] +{ + return case args of + | grammarWeightConfig(x)::_ -> x + | _::r -> getGrammarWeight(r) + | [] -> 0 + end; +} + +function getSplit +Boolean ::= args::[DocConfigSetting] +{ + return case args of + | fileSplitConfig(v)::_ -> v + | splitConfig(true)::_ -> true + | _::r -> getSplit(r) + | [] -> false + end; +} + +function getToc +Boolean ::= args::[DocConfigSetting] +{ + return case args of + | tocConfig(v)::_ -> v + | _::r -> getToc(r) + | [] -> false + end; +} diff --git a/grammars/silver/compiler/extension/doc/core/DocumentedAGDcl.sv b/grammars/silver/compiler/extension/doc/core/DocumentedAGDcl.sv new file mode 100644 index 000000000..14c555730 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/DocumentedAGDcl.sv @@ -0,0 +1,115 @@ +grammar silver:compiler:extension:doc:core; + +import silver:util:cmdargs only CmdArgs; +import silver:compiler:extension:doc:driver; +import silver:compiler:definition:type; + +@{- + - Parse the doc-comment mini language in a DocComment_t, returning a DclComment. + - + - @param conf Global compiler config, used to see if any of the doc-related options + are set. If none are, comment is not parsed + - @param body The Doc-comment token. + - @return If we are parsing docs, the actual doc-comment parsed into a DclComment + - (which could be an errorDclComment) or if we aren't then theEmptyDclComment. + -} +function parseComment +DclComment ::= conf::Decorated CmdArgs body::DocComment_t +{ + local docCommentContent::String = body.lexeme; + local parsed::ParseResult = parseDocComment(docCommentContent, body.location.filename); + local comment::DclComment = if parsed.parseSuccess then parsed.parseTree else errorDclComment(docCommentContent, parsed.parseError, location=body.location); + return if conf.parseDocs then comment else theEmptyDclComment; +} + +function getFreeTypeNames +[String] ::= l::[TyVar] +{ + return case l of + | tyVarNamed(_, _, s)::xs -> s :: getFreeTypeNames(xs) + | _::xs -> getFreeTypeNames(xs) + | [] -> [] + end; +} + +function getFirstAGDcl +Decorated AGDcl ::= a::Decorated AGDcl +{ + return case a of + | appendAGDcl(x, _) -> getFirstAGDcl(x) + | x -> x + end; +} + +@{- + - This wraps an AGDcl to allow it to be prefixed with a doc comment. AGDcls will by default + - emit an doc item that notes that it is undocumented (via mkUndocumentedItem.) This does not + - pass those up, since they are documented here. + - + - @forward Forwards to the wrapped AGDcl. + -} +concrete production documentedAGDcl +top::AGDcl ::= comment::DocComment_t dcl::AGDcl +{ + local parsed::DclComment = parseComment(top.config, comment); + + local paramNamesAndForWhat::Pair String> = case getFirstAGDcl(dcl) of + | functionDcl(_, _, ns, _) -> pair(just(ns.argNames), "function") + | aspectFunctionDcl(_, _, _, ns, _) -> pair(just(ns.argNames), "function") + | productionDcl(_, _, _, ns, _) -> pair(just(ns.argNames), "production") + | aspectProductionDcl(_, _, _, ns, _) -> pair(just(ns.argNames), "production") + | nonterminalDcl(_, _, _, tl, _, _) -> pair(just(getFreeTypeNames(tl.freeVariables)), "nonterminal") + | attributeDclInh(_, _, _, tl, _, _, _) -> pair(just(getFreeTypeNames(tl.freeVariables)), "attribute") + | attributeDclSyn(_, _, _, tl, _, _, _) -> pair(just(getFreeTypeNames(tl.freeVariables)), "attribute") + | _ -> pair(just([]), if isDoubleComment then "standalone" else "other") + end; + + parsed.paramNames = paramNamesAndForWhat.fst; + parsed.isForWhat = paramNamesAndForWhat.snd; + parsed.downDocConfig = top.downDocConfig; + parsed.docEnv = top.docEnv; + parsed.offsetLocation = comment.location; + parsed.indentBy = ""; + + dcl.downDocConfig = top.downDocConfig; + + top.upDocConfig <- parsed.upDocConfig ++ dcl.upDocConfig; + top.docErrors <- parsed.errors; + + local isDoubleComment::Boolean = case dcl of documentedAGDcl(_, _) -> true | _ -> false end; + top.docs := if isDoubleComment + then [standaloneDclCommentItem(parsed)] ++ dcl.docs + else [dclCommentItem(dcl.docForName, dcl.docUnparse, dcl.grammarName, dcl.location, parsed)] + ++ if length(dcl.docs) > 1 then tail(dcl.docs) else []; + top.errors <- if isDoubleComment + then [wrn(parsed.location, "Doc comment not immediately preceding AGDcl, so association is ambiguous. Treating as standalone comment. Mark with @@{- instead of @{- to silence this warning.")] + else []; + + forwards to dcl; +} + +@{- + - Doc comment without associated AGDcl. + - + - @forward emptyAGDcl. + -} +concrete production standaloneCommentAGDcl +top::AGDcl ::= '@' comment::DocComment_t +layout {} +{ + local parsed::DclComment = parseComment(top.config, comment); + + parsed.paramNames = nothing(); + parsed.isForWhat = "standalone"; + parsed.downDocConfig = top.downDocConfig; + parsed.docEnv = top.docEnv; + parsed.offsetLocation = comment.location; + parsed.indentBy = ""; + + top.upDocConfig <- parsed.upDocConfig; + top.docErrors <- parsed.errors; + + top.docs := [standaloneDclCommentItem(parsed)]; + top.unparse = ""; + forwards to emptyAGDcl(location=top.location); +} diff --git a/grammars/silver/compiler/extension/doc/core/Environment.sv b/grammars/silver/compiler/extension/doc/core/Environment.sv new file mode 100644 index 000000000..d0870e23d --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/Environment.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:extension:doc:core; + +nonterminal DocDclInfo with sourceGrammar, sourceLocation; + +synthesized attribute scopedName::String occurs on DocDclInfo; + +abstract production docDclInfo +top::DocDclInfo ::= id::String +{ + top.scopedName = top.sourceGrammar ++ ":" ++ id; +} diff --git a/grammars/silver/compiler/extension/doc/core/Project.sv b/grammars/silver/compiler/extension/doc/core/Project.sv new file mode 100644 index 000000000..1918ca336 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/Project.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:extension:doc:core; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +imports silver:compiler:extension:convenience; + +imports silver:util:treemap as tm; diff --git a/grammars/silver/compiler/extension/doc/core/Root.sv b/grammars/silver/compiler/extension/doc/core/Root.sv new file mode 100644 index 000000000..3328e634e --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/Root.sv @@ -0,0 +1,125 @@ +grammar silver:compiler:extension:doc:core; + +import silver:compiler:driver; + +synthesized attribute genFiles :: [Pair] with ++; + +@{- + - Used for getting doc comments on AGDcls to emit. + - Note that not every item really should be emitted, see doEmit. + -} +synthesized attribute docs :: [CommentItem] with ++; +attribute docs occurs on Grammar, Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +@@{- + - Doc config is managed in both a per-file, and per-grammar way. Directives are either file-scope + - or grammar-scope. A file-scoped directive for the same setting beats a grammar-scoped one. To do + - this, directives flow up via @link[upDocConfig] from AGDcls to reach `grammarRootSpec` and + - then flow back down via @link[downDocConfig]. However, when passing through `Root` the only + - doc directives that flow up to the Grammar scope are those with .fileScope = true. Then when + - flowing back down, those with .fileScope = false are re-added in front of grammar scope directives + - in @link[downDocConfig] (and stored on the `Root` as @link[localDocConfig].) -} + +@{- Final doc config flowing back down, inside files (Roots) will include file scoped settings first. -} +inherited attribute downDocConfig :: [DocConfigSetting] occurs on Grammar, Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +@{- Doc config information flowing up. File scoped settings are stripped at the Root level. -} +synthesized attribute upDocConfig :: [DocConfigSetting] with ++ occurs on Grammar, Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +@{- Snapshot of @link[downDocConfig] stored on `Root`. -} +synthesized attribute localDocConfig :: [DocConfigSetting] occurs on Root; + +synthesized attribute undocumentedNamed :: [String] occurs on Root, Grammar; +synthesized attribute documentedNamed :: [String] occurs on Root, Grammar; + +@{- + - Declarations of documented AGDcls, flowing up. Used for linking and counting documented items. + - Flows back down as @link[docEnv]. + -} +synthesized attribute docDcls :: [Pair] with ++; +attribute docDcls occurs on Grammar, Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +@{- Environment of all documented AGDcls, flowing back down after being computed from @link[docDcls]. -} +autocopy attribute docEnv :: tm:Map; +attribute docEnv occurs on Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +@{- Errors arising from ill-formed doc comments. -} +monoid attribute docErrors :: [Message]; +attribute docErrors occurs on Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; +propagate docErrors on Root, AGDcls, AGDcl, ClassBodyItem, InstanceBodyItem, ClassBody, InstanceBody; + +aspect production root +top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls +{ + top.docs := ags.docs; + top.localDocConfig = ags.downDocConfig; + top.upDocConfig := filter((\x::DocConfigSetting -> !x.fileScope), ags.upDocConfig); + top.docDcls := ags.docDcls; + top.undocumentedNamed = flatMap((.undocNames), top.docs); + top.documentedNamed = flatMap((.docNames), top.docs); + + ags.downDocConfig = filter((\x::DocConfigSetting -> x.fileScope), ags.upDocConfig) ++ top.downDocConfig; + ags.docEnv = tm:add(flatMap((.docDcls), searchEnvTree(top.grammarName, top.compiledGrammars)), tm:empty()); + top.errors <- ags.docErrors; +} + +aspect production nilAGDcls +top::AGDcls ::= +{ + top.docs := []; + top.upDocConfig := []; + top.docDcls := []; +} + +aspect production consAGDcls +top::AGDcls ::= h::AGDcl t::AGDcls +{ + top.docs := h.docs ++ t.docs; + h.downDocConfig = top.downDocConfig; + t.downDocConfig = top.downDocConfig; + top.upDocConfig := h.upDocConfig ++ t.upDocConfig; + top.docDcls := h.docDcls ++ t.docDcls; +} + +aspect default production +top::AGDcl ::= +{ + top.upDocConfig := []; + -- top.docs := [mkUndocumentedItem(s"", top)]; + top.docDcls := []; + -- top.docUnparse = head(explode("\n", top.unparse)) ++ "\n{{< hint danger >}}\nNo docUnparse defined for `" ++ hackUnparse(top) ++ "`\n{{< /hint >}}\n\n"; +} + +aspect production appendAGDcl +top::AGDcl ::= h::AGDcl t::AGDcl +{ + top.docs := h.docs ++ t.docs; + h.downDocConfig = top.downDocConfig; + t.downDocConfig = top.downDocConfig; + top.upDocConfig := h.upDocConfig ++ t.upDocConfig; + top.docDcls := h.docDcls ++ t.docDcls; +} + +aspect production nilGrammar +top::Grammar ::= +{ + top.docs := []; + top.upDocConfig := []; + top.docDcls := []; + top.undocumentedNamed = []; + top.documentedNamed = []; +} + +aspect production consGrammar +top::Grammar ::= c1::Root c2::Grammar +{ + top.docs := c1.docs ++ c2.docs; + top.upDocConfig := c1.upDocConfig ++ c2.upDocConfig; + c1.downDocConfig = top.downDocConfig; + c2.downDocConfig = top.downDocConfig; + top.docDcls := c1.docDcls ++ c2.docDcls; + top.undocumentedNamed = c1.undocumentedNamed ++ c2.undocumentedNamed; + top.documentedNamed = c1.documentedNamed ++ c2.documentedNamed; +} + +-- consGrammar(FILE1, consGrammar(FILE2, nilGrammar())) diff --git a/grammars/silver/compiler/extension/doc/core/RootSpec.sv b/grammars/silver/compiler/extension/doc/core/RootSpec.sv new file mode 100644 index 000000000..9090da3b4 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/RootSpec.sv @@ -0,0 +1,97 @@ +grammar silver:compiler:extension:doc:core; + +import silver:compiler:driver:util; + +attribute genFiles occurs on RootSpec; +attribute docDcls occurs on RootSpec; + +aspect production interfaceRootSpec +top::RootSpec ::= _ _ +{ + top.genFiles := []; + top.docDcls := []; +} + +aspect production errorRootSpec +top::RootSpec ::= _ _ _ _ _ +{ + top.genFiles := []; + top.docDcls := []; +} + +aspect production grammarRootSpec +top::RootSpec ::= g::Grammar _ _ _ _ _ +{ + top.genFiles := toSplitFiles(g, g.upDocConfig, [], []); + top.docDcls := g.docDcls; + + g.downDocConfig = g.upDocConfig; +} + +function silverToMdFilename +String ::= fileName::String +{ + return foldr( + \ ext::String file::String -> + if endsWith(ext, file) then substitute(ext, ".md", file) else file, + fileName, allowedSilverFileExtensions); +} + +@{- + - Turn the files in a grammar into zero or more single-file docs pages, and collect the rest of the docs + - (possibly zero) into the index file. + -} +function toSplitFiles +[Pair] ::= g::Decorated Grammar with {decorate, downDocConfig} grammarConf::[DocConfigSetting] forIndex::[CommentItem] soFar::[Pair] +{ + return case g of + | consGrammar(this, rest) -> + if getSplit(this.localDocConfig) then toSplitFiles(rest, grammarConf, forIndex, formatFile( + silverToMdFilename(this.location.filename), + getFileTitle(this.localDocConfig, silverToMdFilename(this.location.filename)), + getFileWeight(this.localDocConfig), true, + s"In grammar `${g.grammarName}` file `${this.location.filename}`: "++(if getToc(this.localDocConfig) then "{{< toc >}}" else ""), + this.docs) ++ soFar) else toSplitFiles(rest, grammarConf, forIndex ++ this.docs, soFar) + | nilGrammar() -> let skel::Boolean = (length(soFar) == 0 && length(grammarConf) == 0 && length(forIndex) == 0) in + formatFile("_index.md", + getGrammarTitle(grammarConf, "["++g.grammarName++"]"++(if skel then " (skel)" else "")), + getGrammarWeight(grammarConf) + (if skel then 10000 else 0), + false, s"Contents of `[${g.grammarName}]`: {{< toc-tree >}} \n\nDefined in this grammar:", forIndex) ++ soFar end + end; +} + +function formatFile +[Pair] ::= fileName::String title::String weight::Integer + skipIfEmpty::Boolean pfxText::String + comments::[CommentItem] +{ + local realDocs::[CommentItem] = filter((.doEmit), comments); + local stubDocs::[CommentItem] = filter((.stub), realDocs); + local nonStubDocs::[CommentItem] = filter((\x::CommentItem->!x.stub), realDocs); + return if length(realDocs) == 0 && skipIfEmpty then [] else [pair(fileName, s"""--- +title: "${title}" +weight: ${toString(weight)} +geekdocBreadcrumb: false +--- + +${pfxText} + +${implode("\n\n
\n\n", map((.body), nonStubDocs))} + + +""" +++ (if length(stubDocs)!=0 then s""" +{{< expand "Undocumented Items" "..." >}} + +${implode("\n\n
\n\n", map((.body), stubDocs))} + +{{< /expand >}} +""" else "" +))]; +} + +function lastPart +String ::= s::String +{ + return last(explode(":", s)); +} diff --git a/grammars/silver/compiler/extension/doc/core/Terminals.sv b/grammars/silver/compiler/extension/doc/core/Terminals.sv new file mode 100644 index 000000000..8342ba6de --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/Terminals.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:extension:doc:core; +imports silver:compiler:extension:doc:core:doclang; +imports silver:langutil:lsp as lsp; + +terminal AtSign_t '@' lexer classes {COMMENT, lsp:Documentation}; + +-- Once nested +terminal DocComment_t /@\{\-(\{\-([^\-]|\-+[^\}\-])*\-+\}|[^\-]|\-+[^\}\-])*\-+\}/ lexer classes {COMMENT, lsp:Documentation}; + +-- No nesting +-- terminal DocComment_t /@\{\-+([^\-]|\-[^\}]|[^\-\}])*\-+\}/; diff --git a/grammars/silver/compiler/extension/doc/core/TypeClassDcls.sv b/grammars/silver/compiler/extension/doc/core/TypeClassDcls.sv new file mode 100644 index 000000000..34a5c8798 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/TypeClassDcls.sv @@ -0,0 +1,174 @@ +grammar silver:compiler:extension:doc:core; + +attribute docUnparse occurs on ClassBodyItem, InstanceBodyItem; +attribute docForName occurs on ClassBodyItem, InstanceBodyItem; + +@{- What to prefix 'child' declaration names with in docs and for hyperlinking. -} +autocopy attribute scopeName :: String occurs on ClassBody, ClassBodyItem, InstanceBody, InstanceBodyItem; + +aspect production typeClassDcl +top::AGDcl ::= 'class' cl::ConstraintList '=>' id::QNameType var::TypeExpr '{' body::ClassBody '}' +{ + top.docForName = "class " ++ id.name; + top.docUnparse = s"`class ${id.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(top.docForName, sourceLocation=top.location, sourceGrammar=top.grammarName))] ++ body.docDcls; + top.docs := [mkUndocumentedItem(top.docForName, top)] ++ body.docs; + body.scopeName = top.docForName; + body.downDocConfig = top.downDocConfig; + top.upDocConfig := body.upDocConfig; +} + +aspect production instanceDcl +top::AGDcl ::= 'instance' cl::ConstraintList '=>' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + top.docForName = "instance "++id.name++" "++ty.unparse; + top.docUnparse = s"`instance ${id.unparse} ${ty.unparse}`"; + top.docDcls := [pair(id.name, docDclInfo(top.docForName, sourceLocation=top.location, sourceGrammar=top.grammarName))] ++ body.docDcls; + top.docs := [mkUndocumentedItem(top.docForName, top)] ++ body.docs; + body.scopeName = top.docForName; + body.downDocConfig = top.downDocConfig; + top.upDocConfig := body.upDocConfig; +} + +aspect production nilClassBody +top::ClassBody ::= +{ + top.docs := []; + top.upDocConfig := []; + top.docDcls := []; +} + +aspect production consClassBody +top::ClassBody ::= h::ClassBodyItem t::ClassBody +{ + top.docs := h.docs ++ t.docs; + top.upDocConfig := h.upDocConfig ++ t.upDocConfig; + top.docDcls := h.docDcls ++ t.docDcls; + h.downDocConfig = top.downDocConfig; + t.downDocConfig = top.downDocConfig; +} + +aspect default production +top::ClassBodyItem ::= +{ + top.docForName = ""; + top.upDocConfig := []; + top.docDcls := []; + top.docs := [undocumentedItem(top.scopeName ++ "." ++ top.docForName, "`" ++ top.scopeName ++ "`." ++ top.docUnparse, top.grammarName, top.location)]; + top.docUnparse = ""; +} + +concrete production documentedClassBodyItem +top::ClassBodyItem ::= comment::DocComment_t item::ClassBodyItem +{ + local parsed::DclComment = parseComment(top.config, comment); + + parsed.paramNames = nothing(); + parsed.isForWhat = "classBodyItem"; + parsed.downDocConfig = top.downDocConfig; + parsed.docEnv = top.docEnv; + parsed.offsetLocation = comment.location; + parsed.indentBy = ">"; + + item.downDocConfig = top.downDocConfig; + top.upDocConfig <- parsed.upDocConfig ++ item.upDocConfig; + top.docErrors <- parsed.errors; + + local realDclDocs::[CommentItem] = filter((\x::CommentItem->!x.stub), item.docs); + local isDoubleComment::Boolean = length(realDclDocs) != 0; + top.docs := if isDoubleComment + then [standaloneDclCommentItem(parsed)] ++ realDclDocs + else [dclCommentItem(top.scopeName ++ "." ++ item.docForName, item.docUnparse, item.grammarName, item.location, parsed)]; + top.docErrors <- + if isDoubleComment + then [wrn(parsed.location, "Doc comment not immediately preceding ClassBodyItem, so association is ambiguous. Treating as standalone comment. Mark with @@{- instead of @{- to silence this warning.")] + else []; + + forwards to item; +} + +aspect production constraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr ';' +{ + top.docForName = id.name; + top.docUnparse = "`" ++ id.unparse ++ " :: " ++ cl.unparse ++ " => " ++ ty.unparse ++ "`"; + top.docs := [undocumentedItem(top.scopeName ++ "." ++ top.docForName, "`" ++ top.scopeName ++ "`." ++ top.docUnparse, top.grammarName, top.location)]; +} + +aspect production defaultConstraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr '=' e::Expr ';' +{ + top.docForName = id.name; + top.docUnparse = "`" ++ id.unparse ++ " :: " ++ cl.unparse ++ " => " ++ ty.unparse ++ "` (has default)"; + top.docs := [undocumentedItem(top.scopeName ++ "." ++ top.docForName, "`" ++ top.scopeName ++ "`." ++ top.docUnparse, top.grammarName, top.location)]; +} + + + + + + +aspect production nilInstanceBody +top::InstanceBody ::= +{ + top.docs := []; + top.upDocConfig := []; + top.docDcls := []; +} + +aspect production consInstanceBody +top::InstanceBody ::= h::InstanceBodyItem t::InstanceBody +{ + top.docs := h.docs ++ t.docs; + top.upDocConfig := h.upDocConfig ++ t.upDocConfig; + top.docDcls := h.docDcls ++ t.docDcls; + h.downDocConfig = top.downDocConfig; + t.downDocConfig = top.downDocConfig; +} + +aspect default production +top::InstanceBodyItem ::= +{ + top.docForName = ""; + top.upDocConfig := []; + top.docDcls := []; + top.docs := [undocumentedItem(top.scopeName ++ "." ++ top.docForName, "`" ++ top.scopeName ++ "`." ++ top.docUnparse, top.grammarName, top.location)]; + top.docUnparse = ""; +} + +concrete production documentedInstanceBodyItem +top::InstanceBodyItem ::= comment::DocComment_t item::InstanceBodyItem +{ + local parsed::DclComment = parseComment(top.config, comment); + + parsed.paramNames = nothing(); + parsed.isForWhat = "instanceBodyItem"; + parsed.downDocConfig = top.downDocConfig; + parsed.docEnv = top.docEnv; + parsed.offsetLocation = comment.location; + parsed.indentBy = ">"; + + item.downDocConfig = top.downDocConfig; + top.upDocConfig <- parsed.upDocConfig ++ item.upDocConfig; + top.docErrors <- parsed.errors; + + local realDclDocs::[CommentItem] = filter((\x::CommentItem->!x.stub), item.docs); + local isDoubleComment::Boolean = length(realDclDocs) != 0; + top.docs := if isDoubleComment + then [standaloneDclCommentItem(parsed)] ++ realDclDocs + else [dclCommentItem(top.scopeName ++ "." ++ item.docForName, item.docUnparse, item.grammarName, item.location, parsed)]; + top.docErrors <- + if isDoubleComment + then [wrn(parsed.location, "Doc comment not immediately preceding InstanceBodyItem, so association is ambiguous. Treating as standalone comment. Mark with @@{- instead of @{- to silence this warning.")] + else []; + + forwards to item; +} + +aspect production instanceBodyItem +top::InstanceBodyItem ::= id::QName '=' e::Expr ';' +{ + top.docForName = id.name; + top.docUnparse = "`" ++ id.unparse ++ "`"; + top.docs := [undocumentedItem(top.scopeName ++ "." ++ top.docForName, "`" ++ top.scopeName ++ "`." ++ top.docUnparse, top.grammarName, top.location)]; +} diff --git a/grammars/silver/compiler/extension/doc/core/doclang/DclComment.sv b/grammars/silver/compiler/extension/doc/core/doclang/DclComment.sv new file mode 100644 index 000000000..95bf4013a --- /dev/null +++ b/grammars/silver/compiler/extension/doc/core/doclang/DclComment.sv @@ -0,0 +1,458 @@ +grammar silver:compiler:extension:doc:core:doclang; +imports silver:compiler:extension:doc:core; +imports silver:compiler:definition:env; +imports silver:langutil; +imports silver:util:treemap as tm; + +@@{- + - Comment is sequence of blocks + + - Blocks start with a newline or a @@param/@@return/@@prodattr/@@forward/... + + - Initial block is a 'normal' block even if no newline (but is other type if has @@tag) + -} + +@{- Does this doc comment actually result in a markdown block? -} +synthesized attribute doEmit::Boolean occurs on DclComment; + +@{- List of parameter/child names used to warn for incorrect number/names on function/production blocks. -} +inherited attribute paramNames::Maybe<[String]> occurs on DclComment; + +@{- Enum-like description of what type of construct the comment occurs on, used to warn on using irrelevant blocks. -} +inherited attribute isForWhat::String occurs on DclComment; + +@{- String to prepend to emitted markdown lines. -} +inherited attribute indentBy::String occurs on DclComment; + +@{- List of pair of (paramname, content) -} +synthesized attribute paramBlocks::[Pair]; + +@{- List of pair of (blocktype, content) -} +synthesized attribute otherBlocks::[Pair]; + +@{- Config args. -} +synthesized attribute configArgs::[Pair]; + +nonterminal DclComment layout {} with docEnv, body, errors, location, downDocConfig, upDocConfig; + +nonterminal DclCommentBlocks layout {} with paramBlocks, otherBlocks, configArgs, location, docEnv, errors; +nonterminal DclCommentStrictBlocks layout {} with paramBlocks, otherBlocks, configArgs, location, docEnv, errors; +nonterminal DclCommentBlock layout {} with paramBlocks, otherBlocks, configArgs, body, location, docEnv, errors; + +nonterminal ConfigValue layout {} with location; + +nonterminal DclCommentLines layout {} with body, location, docEnv, errors; + +nonterminal DclCommentParts layout {} with body, location, docEnv, errors; +nonterminal DclCommentPart layout {} with body, location, docEnv, errors; + +propagate errors on DclCommentBlocks, DclCommentStrictBlocks, DclCommentBlock, + DclCommentLines, DclCommentParts, DclCommentPart; + +@{- The location of the terminal whose text was parsed to create this DclComment, used when displaying errors. -} +inherited attribute offsetLocation::Location occurs on + DclComment, DclCommentBlocks, DclCommentStrictBlocks, DclCommentBlock, + DclCommentLines, DclCommentParts, DclCommentPart; + +propagate offsetLocation on + DclComment, DclCommentBlocks, DclCommentStrictBlocks, DclCommentBlock, + DclCommentLines, DclCommentParts, DclCommentPart; + +parser parseDocComment::DclComment { + silver:compiler:extension:doc:core:doclang; +} + +concrete production emptyDclComment +top::DclComment ::= EmptyDclComment_t +{ + top.errors := []; + top.body = ""; + top.upDocConfig := []; + top.doEmit = false; +} + +global theEmptyDclComment :: DclComment = emptyDclComment(terminal(EmptyDclComment_t, ""), location=txtLoc("")); + +concrete production normalDclComment +top::DclComment ::= InitialIgnore_t blocks::DclCommentBlocks FinalIgnore_t +{ + local warningBlocks::[String] = getBlocksNamed(blocks.otherBlocks, "warning"); + local prodAttrBlocks::[String] = getBlocksNamed(blocks.otherBlocks, "prodAttr"); + local returnBlocks::[String] = getBlocksNamed(blocks.otherBlocks, "return"); + local forwardBlocks::[String] = getBlocksNamed(blocks.otherBlocks, "forward"); + local normalBlocks::[String] = getBlocksNamed(blocks.otherBlocks, "normal"); + + local paramNamesOrEmpty::[String] = fromMaybe([], top.paramNames); + + local paramBlocks::[String] = + map(snd, sortBy( + (\x::Pair y::Pair -> + positionOf(x.fst, paramNamesOrEmpty) < positionOf(y.fst, paramNamesOrEmpty)), + blocks.paramBlocks)); + + local errs::[String] = + (if (length(paramBlocks) != length(paramNamesOrEmpty) && (length(paramBlocks) != 0)) + then ["Arity doesn't match in doc-comment"] + else checkParams(paramNamesOrEmpty, map(fst, blocks.paramBlocks))) ++ + (if length(forwardBlocks) > 1 then ["More than one forward block in doc-comment"] else []) ++ + (if length(returnBlocks) > 1 then ["More than one return block in doc-comment"] else []) ++ + (if length(returnBlocks) > 0 && top.isForWhat!="function" then ["@return in non-function doc-comment"] else []) ++ + (if length(forwardBlocks) > 0 && top.isForWhat!="production" then ["@forward in non-production doc-comment"] else []) ++ + (if length(prodAttrBlocks) > 0 && !(top.isForWhat=="function" || top.isForWhat == "production") then ["@prodattr in non function-or-production doc comment"] else []) ++ + (if length(paramBlocks) > 0 && !top.paramNames.isJust then ["@param does belong in this doc comment"] else []) ++ + confResult.fst; + + top.errors := map((\x::String -> wrn(top.offsetLocation, x)), errs); + top.errors <- blocks.errors; + + top.body = + implode("\n\n", + warningBlocks ++ + paramBlocks ++ + prodAttrBlocks ++ + returnBlocks ++ + forwardBlocks ++ + normalBlocks); + + local confResult::Pair<[String] [DocConfigSetting]> = processConfigOptions([], blocks.configArgs, []); + top.upDocConfig := confResult.snd; + + top.doEmit = + ((length(blocks.otherBlocks) + length(paramBlocks)) != 0) && + (!doesExcludeFile(top.downDocConfig)) && + (!fromMaybe(false, fromMaybe(kwdValue(terminal(ConfigValueKeyword_t, "off"), + location=top.location), lookup("hide", blocks.configArgs)).asBool)); +} + +function getBlocksNamed +[String] ::= l::[Pair] f::String +{ + return flatMap((\x::Pair -> if x.fst==f then [x.snd] else []), l); +} + +function processConfigOptions +Pair<[String] [DocConfigSetting]> ::= alreadyErrs::[String] args::[Pair] conf::[DocConfigSetting] +{ + local arg::Pair = + case args of + | a::_ -> a + | _ -> error("(Impossible)") + end; + + local err::[String] = + case arg of + | pair("split", v) -> if !v.asBool.isJust then ["@config split takes a boolean value (or just @config split)"] else [] + | pair("fileSplit", v) -> if !v.asBool.isJust then ["@config fileSplit takes a boolean value (or just @config fileSplit)"] else [] + | pair("noToc", v) -> if !v.asBool.isJust then ["@config noToc takes a boolean value (or just @config noToc)"] else [] + | pair("weight", v) -> if !v.asInteger.isJust then ["@config weight takes an integer"] else [] + | pair("grammarWeight", v) -> if !v.asInteger.isJust then ["@config grammarWeight takes an integer"] else [] + | pair("title", v) -> if !v.asString.isJust then ["@config title takes a string in quotes"] else [] + | pair("grammarTitle", v) -> if !v.asString.isJust then ["@config grammarTitle takes a string in quotes"] else [] + | pair("excludeFile", v) -> if !v.asBool.isJust then ["@config excludeFile takes a boolean value (or just @config excludeFile)"] else [] + | pair("excludeGrammar", v) -> if !v.asBool.isJust then ["@config excludeGrammar takes a boolean value (or just @config excludeGrammar)"] else [] + | pair("hide", v) -> if !v.asBool.isJust then ["@config hide takes a boolean value (or just @config hide or @hide)"] else [] + | pair(k, _) -> ["Unknown @config directive '"++k++"'"] + end; + + local boundConf::[DocConfigSetting] = + case arg of + | pair("split", v) -> [splitConfig(v.asBool.fromJust)] + | pair("fileSplit", v) -> [fileSplitConfig(v.asBool.fromJust)] + | pair("noToc", v) -> [tocConfig(!v.asBool.fromJust)] + | pair("weight", v) -> [weightConfig(v.asInteger.fromJust)] + | pair("grammarWeight", v) -> [grammarWeightConfig(v.asInteger.fromJust)] + | pair("title", v) -> [titleConfig(v.asString.fromJust)] + | pair("grammarTitle", v) -> [grammarTitleConfig(v.asString.fromJust)] + | pair("excludeFile", v) -> [fileNoDocsConfig(v.asBool.fromJust)] + | pair("excludeGrammar", v) -> [grammarNoDocsConfig(v.asBool.fromJust)] + | pair("hide", _) -> [] + | _ -> error("(Impossible)") + end; + + return case args of + | [] -> pair(alreadyErrs, conf) + | _::r when length(err)!=0 -> processConfigOptions(err++alreadyErrs, r, conf) + | _::r -> processConfigOptions(alreadyErrs, r, boundConf ++ conf) + end; +} + +function checkParams +[String] ::= p::[String] b::[String] +{ + return case p, b of + | pn::p_, bn::b_ when pn==bn -> checkParams(p_, b_) + | pn::p_, bn::b_ -> s"Param '${pn}' in wrong order in doc-comment" :: checkParams(p_, b_) + | _, _ -> [] + end; +} + +abstract production errorDclComment +top::DclComment ::= content::String error::ParseError +{ + top.body = s"""(Comment parse error, raw content) +``` +${content} +``` +"""; + + local errorMessage::Message = + case error of + | syntaxError(_, location, expected, matched) -> + let printLoc::Location = childParserLoc(top.offsetLocation, location, 0, 0, 0, 0) + in wrn(printLoc, + s"Doc Comment Parse Error at ${printLoc.filename} line ${toString(printLoc.line)} column ${toString(printLoc.column)}" + ++ s"\n\tExpected a token of one of the following types: [${implode(", ", expected)}]" + ++ s"\n\tInput currently matches: [${implode(", ", matched)}]") end + | unknownParseError(s, f) -> wrn(top.location, s"Doc comment unknown parse error: unknownParseError(${s}, ${f})") + end; + + top.errors := [errorMessage]; + top.doEmit = true; + + top.upDocConfig := []; +} + + + + +concrete production initialCommentBlocks +top::DclCommentBlocks ::= block::DclCommentLines blocks::DclCommentStrictBlocks +{ + top.otherBlocks = + pair("normal", block.body) :: blocks.otherBlocks; + top.paramBlocks = blocks.paramBlocks; + top.configArgs = blocks.configArgs; +} + +concrete production passThruCommentBlocks +top::DclCommentBlocks ::= blocks::DclCommentStrictBlocks +{ + top.otherBlocks = blocks.otherBlocks; + top.paramBlocks = blocks.paramBlocks; + top.configArgs = blocks.configArgs; +} + + + +concrete production nilCommentBlocks +top::DclCommentStrictBlocks ::= +{ + top.otherBlocks = []; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production consCommentBlocks +top::DclCommentStrictBlocks ::= block::DclCommentBlock rest::DclCommentStrictBlocks +{ + top.paramBlocks = block.paramBlocks ++ rest.paramBlocks; + top.otherBlocks = block.otherBlocks ++ rest.otherBlocks; + top.configArgs = block.configArgs ++ rest.configArgs; +} + + + + +concrete production commentBlock +top::DclCommentBlock ::= EmptyLines_t content::DclCommentLines +{ + top.body = content.body; + top.otherBlocks = [pair("normal", top.body)]; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production paramBlock +top::DclCommentBlock ::= Param_t Whitespace_t id::Id_t Whitespace_t content::DclCommentLines +{ + top.body = "{{< hint info >}}\n**Parameter `"++id.lexeme++"`**\\\n " ++ content.body ++ "\n{{< /hint >}}"; + top.otherBlocks = []; + top.paramBlocks = [pair(id.lexeme, top.body)]; + top.configArgs = []; +} + +concrete production prodattrBlock +top::DclCommentBlock ::= Prodattr_t Whitespace_t id::Id_t Whitespace_t content::DclCommentLines +{ + top.body = "{{< hint warning >}}\n**Production Attribute `"++id.lexeme++"`**\\\n " ++ content.body ++ "\n{{< /hint >}}"; + top.otherBlocks = [pair("prodAttr", top.body)]; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production returnBlock +top::DclCommentBlock ::= Return_t Whitespace_t content::DclCommentLines +{ + top.body = "{{< hint info >}}\n**Return**\\\n " ++ content.body ++ "\n{{< /hint >}}"; + top.otherBlocks = [pair("return", top.body)]; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production forwardBlock +top::DclCommentBlock ::= Forward_t Whitespace_t content::DclCommentLines +{ + top.body = "{{< hint warning >}}\n**Forward**\\\n " ++ content.body ++ "\n{{< /hint >}}"; + top.otherBlocks = [pair("forward", top.body)]; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production warningBlock +top::DclCommentBlock ::= Warning_t Whitespace_t content::DclCommentLines +{ + top.body = "{{< hint danger >}}\n**WARNING!**\\\n " ++ content.body ++ "\n{{< /hint >}}"; + top.otherBlocks = [pair("warning", top.body)]; + top.paramBlocks = []; + top.configArgs = []; +} + +concrete production hideBlock +top::DclCommentBlock ::= Hide_t +{ + forwards to configBlockImplicitTrue( + terminal(Config_t, "@config"), terminal(Whitespace_t, ""), + terminal(Id_t, "hide"), terminal(Whitespace_t, ""), location=top.location); +} + +concrete production configBlock +top::DclCommentBlock ::= Config_t Whitespace_t param::Id_t Whitespace_t Equals_t Whitespace_t value::ConfigValue +{ + top.body = "@config " ++ param.lexeme ++ " = " ++ hackUnparse(value); --not emitted + top.otherBlocks = []; + top.paramBlocks = []; + top.configArgs = [pair(param.lexeme, value)]; +} + +concrete production configBlockImplicitTrue +top::DclCommentBlock ::= Config_t Whitespace_t param::Id_t Whitespace_t +{ + forwards to configBlock($1, $2, $3, $4, terminal(Equals_t, ""), terminal(Whitespace_t, ""), + kwdValue(terminal(ConfigValueKeyword_t, "true"), location=top.location), location=top.location); +} + +synthesized attribute asBool::Maybe occurs on ConfigValue; +synthesized attribute asString::Maybe occurs on ConfigValue; +synthesized attribute asInteger::Maybe occurs on ConfigValue; + +concrete production kwdValue +top::ConfigValue ::= v::ConfigValueKeyword_t +{ + top.asBool = just(v.lexeme=="on" || v.lexeme=="true" || v.lexeme=="yes"); + top.asString = nothing(); + top.asInteger = nothing(); +} + +concrete production stringValue +top::ConfigValue ::= v::ConfigValueString_t +{ + top.asBool = nothing(); + top.asString = just(substring(1, length(v.lexeme)-1, v.lexeme)); + top.asInteger = nothing(); +} + +concrete production integerValue +top::ConfigValue ::= v::ConfigValueInt_t +{ + top.asBool = nothing(); + top.asString = nothing(); + top.asInteger = just(toInteger(v.lexeme)); +} + +concrete production lastCommentLines +top::DclCommentLines ::= body::DclCommentParts +{ + top.body = body.body; +} + +concrete production consCommentLines +top::DclCommentLines ::= body::DclCommentParts Newline_t rest::DclCommentLines +{ + top.body = body.body ++ "\n" ++ rest.body; +} + + + + + +concrete production firstCommentParts +top::DclCommentParts ::= part::DclCommentPart +{ + top.body = part.body; +} + +concrete production snocCommentParts +top::DclCommentParts ::= rest::DclCommentParts part::DclCommentPart +{ + top.body = rest.body ++ part.body; +} + + +concrete production textCommentPart +top::DclCommentPart ::= part::CommentContent_t +{ + top.body = part.lexeme; +} + +concrete production linkCommentPart +top::DclCommentPart ::= '@link' '[' id::Id_t ']' +{ + local res::[DocDclInfo] = tm:lookup(id.lexeme, top.docEnv); + top.body = case res of + | [dcl] -> id.lexeme ++ " at " ++ dcl.sourceGrammar ++ "/" ++ dcl.sourceLocation.filename ++ "#" ++ toString(dcl.sourceLocation.line) + | _ -> s"${id.lexeme} (**BROKEN LINK**)" + end; + top.errors <- case res of + | [_] -> [] + | _ -> [wrn(childParserLoc(top.offsetLocation, top.location, 0, 0, 0, 0), + "Broken doc link to `"++id.lexeme++"`")] + end; +} +concrete production fileLinkCommentPart +top::DclCommentPart ::= '@file' '[' path::Path_t ']' +{ + top.body = s"[${path.lexeme}](github -> ${path.lexeme})"; +} + +concrete production escapedAtPart +top::DclCommentPart ::= '@@' +{ + top.body = "@"; +} + +@@{- Most of the complexity in terminals here is to allow bullet point lists and nested comments. Be careful :) -} + +terminal InitialIgnore_t /@+\{\-[ \t]*\-*[ \t]*([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*/; +terminal FinalIgnore_t /[\- \r\n]*\-\}/ dominates {CommentContent_t}; +terminal EmptyDclComment_t /@+{\-[ \-]*\-}/; + +terminal EmptyLines_t /\r?\n([ \t]*\-*[ \t]*\r?\n)+[ \t]*\-*[ \t]*/; +terminal Newline_t /\r?\n[ \t]*\-*[ \t]*/; + +terminal CommentContent_t /([^@\r\n\-]|\-[^\r\n\}]|\-\}.)+/; + +terminal EscapedAt_t '@@'; + +terminal Param_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@(param|child)/ lexer classes {BLOCK_KWD}; +terminal Return_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@return/ lexer classes {BLOCK_KWD}; +terminal Forward_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@forward/ lexer classes {BLOCK_KWD}; +terminal Prodattr_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@prodattr/ lexer classes {BLOCK_KWD}; +terminal Warning_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@warning/ lexer classes {BLOCK_KWD}; +terminal Config_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@config/ lexer classes {BLOCK_KWD}; +terminal Hide_t /([ \t]*\-*[ \t]*\r?\n)*[ \t]*\-*[ \t]*@hide/ lexer classes {BLOCK_KWD}; + +terminal ConfigValueKeyword_t /(on|off|true|false|yes|no)/; +terminal ConfigValueString_t /[\"]([^\r\n\"\\]|[\\][\"]|[\\][\\]|[\\]b|[\\]n|[\\]r|[\\]f|[\\]t)*[\"]/; +terminal ConfigValueInt_t /\-?[0-9]+/; + +terminal Whitespace_t /[\t ]*/; +terminal Equals_t /=?/; + +terminal Link_t '@link'; +terminal FileLink_t '@file'; +terminal OpenBracket_t '['; +terminal CloseBracket_t ']'; +terminal Id_t /[a-zA-Z][a-zA-Z0-9_:]*/; +terminal Path_t /[a-zA-Z0-9_\-\/\.]+/; + +lexer class BLOCK_KWD dominates CommentContent_t; diff --git a/grammars/silver/compiler/extension/doc/driver/BuildProcess.sv b/grammars/silver/compiler/extension/doc/driver/BuildProcess.sv new file mode 100644 index 000000000..cd3e89127 --- /dev/null +++ b/grammars/silver/compiler/extension/doc/driver/BuildProcess.sv @@ -0,0 +1,142 @@ +grammar silver:compiler:extension:doc:driver; + +import silver:compiler:extension:doc:core; + +import silver:compiler:driver; +import silver:compiler:definition:env; +import silver:compiler:definition:core; + +import silver:util:cmdargs; + +synthesized attribute docGeneration :: Boolean occurs on CmdArgs; +synthesized attribute printUndoc :: Boolean occurs on CmdArgs; +synthesized attribute countUndoc :: Boolean occurs on CmdArgs; +synthesized attribute parseDocs :: Boolean occurs on CmdArgs; +synthesized attribute docOutOption :: Maybe occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= _ +{ + top.docGeneration = false; + top.printUndoc = false; + top.countUndoc = false; + top.parseDocs = false; + top.docOutOption = nothing(); +} + +abstract production docFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.docGeneration = true; + top.parseDocs = true; + forwards to rest; +} + +abstract production printUndocFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.printUndoc = true; + top.parseDocs = true; + forwards to rest; +} + +abstract production countUndocFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.countUndoc = true; + top.parseDocs = true; + forwards to rest; +} + +abstract production docOutFlag +top::CmdArgs ::= loc::String rest::CmdArgs +{ + top.docOutOption = case rest.docOutOption of + | nothing() -> just(loc) + | _ -> error("Duplicate arguments for docOutOption") + end; + forwards to rest; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ flagSpec(name="--doc", paramString=nothing(), + help="build the documentation", + flagParser=flag(docFlag)) + , flagSpec(name="--print-undoc", paramString=nothing(), + help="print names of undocumented items", + flagParser=flag(printUndocFlag)) + , flagSpec(name="--count-undoc", paramString=nothing(), + help="print names of undocumented items", + flagParser=flag(countUndocFlag)) + , flagSpec(name="--doc-out", paramString=nothing(), + help="output location for documentation", + flagParser=option(docOutFlag))]; +} + +aspect production compilation +top::Compilation ::= g::Grammars _ _ benv::BuildEnv +{ + local outputLoc::String = fromMaybe(benv.silverGen ++ "/doc/", top.config.docOutOption) ++ "/"; + top.postOps <- if top.config.docGeneration then + [genDoc(top.config, grammarsToTranslate, outputLoc)] + else []; + top.postOps <- if top.config.printUndoc || top.config.countUndoc then + [printUndoc(top.config, grammarsToTranslate)] + else []; +} + +abstract production printUndoc +top::DriverAction ::= a::Decorated CmdArgs specs::[Decorated RootSpec] +{ + local report :: String = "\nUndocumented Items Report:\n" ++ implode("\n", + flatMap((\x::Decorated RootSpec -> case x of + | grammarRootSpec(g, _, _, _, _, _) -> + if (length(g.documentedNamed)+length(g.undocumentedNamed))!=0 + then [s" - [${g.grammarName}]: ${toString(length(g.documentedNamed))}" ++ + s"/${toString(toInteger(length(g.undocumentedNamed)+length(g.documentedNamed)))} " ++ + s"(${toString((toFloat(length(g.documentedNamed))/toFloat(length(g.undocumentedNamed)+length(g.documentedNamed)))*toFloat(100))}%) items documented" + ++ (if a.printUndoc && (length(g.undocumentedNamed)!=0) + then ", missing: " ++ implode(", ", g.undocumentedNamed) + else ".")] + else [] + | _ -> [] + end), specs)); + + top.run = do { println(report); return 0; }; + top.order = 5; +} + + +abstract production genDoc +top::DriverAction ::= a::Decorated CmdArgs specs::[Decorated RootSpec] outputLoc::String +{ + top.run = do { + eprintln("Generating Documentation."); + traverse_(writeSpec(_, outputLoc), specs); + return 0; + }; + top.order = 4; +} + +function writeSpec +IO<()> ::= r::Decorated RootSpec outputLoc::String +{ + local path :: String = outputLoc ++ grammarToPath(r.declaredName); + + return do { + eprintln("\t[" ++ r.declaredName ++ "]"); + isD::Boolean <- isDirectory(path); + unless(isD, do { + mkDSuccess::Boolean <- mkdir(path); + unless(mkDSuccess, do { + eprintln("Unrecoverable Error: Unable to create directory: " ++ path); + exit(-5); + }); + }); + mkdir(path); + deleteDirFiles(path); + writeFiles(path, r.genFiles); + }; +} diff --git a/grammars/silver/compiler/extension/doc/extra/Main.sv b/grammars/silver/compiler/extension/doc/extra/Main.sv new file mode 100644 index 000000000..a715c6c5b --- /dev/null +++ b/grammars/silver/compiler/extension/doc/extra/Main.sv @@ -0,0 +1,70 @@ +grammar silver:compiler:extension:doc:extra; + +import silver:xml; + +import silver:compiler:analysis:typechecking:core; +import silver:compiler:analysis:warnings; +import silver:compiler:analysis:warnings:flow; +import silver:compiler:analysis:warnings:exporting; + +import silver:compiler:composed:Default; + +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:concrete_syntax:ast; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:flow:ast; +import silver:compiler:definition:flow:driver; +import silver:compiler:definition:flow:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; + +import silver:compiler:driver; +import silver:compiler:driver:util; + +-- Individual extensions built by silver:compiler:composed:Default + +import silver:compiler:host; + +import silver:langutil; +import silver:langutil:pp; + +-- Individual modifications built by silver:compiler:composed:Default + +import silver:testing; + +import silver:compiler:translation:java; +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:driver; +import silver:compiler:translation:java:type; + +import silver:regex; + +import silver:util:cmdargs; +import silver:util:deque as dq; +import silver:util:treemap; +import silver:util:graph; +import silver:util:treemap; +import silver:util:treeset; +import silver:util:subprocess; + +import silver:xml; + + +@@{- This grammar is just a bunch of imports. It is run by the generate-documentation script, and just builds documentation. The jar it produces is just thrown away. -} + +@@{- ## Example top-level doc comment -} + +@{- Dummy main function that does nothing. Example link: @link[dummyFunction] -} +function main +IOVal ::= args::[String] ioIn::IOToken +{ + return ioval(ioIn, 0); +} + +@{- Also a dummy function that does nothing -} +function dummyFunction +Integer ::= +{ + return 1; +} diff --git a/grammars/silver/compiler/extension/easyterminal/Env.sv b/grammars/silver/compiler/extension/easyterminal/Env.sv new file mode 100644 index 000000000..2f5c8eba3 --- /dev/null +++ b/grammars/silver/compiler/extension/easyterminal/Env.sv @@ -0,0 +1,51 @@ +grammar silver:compiler:extension:easyterminal; + +import silver:compiler:definition:env; + +function getTerminalRegexDclAll +[TypeDclInfo] ::= search::String e::Decorated Env +{ + return searchEnv(search, e.terminalTree); +} + +synthesized attribute terminalTree :: [EnvTree] occurs on Env; -- must be kept in sync with typeTree's type!! (whether its a [] or not) + +function filterAndConvertTermDcls +[Pair] ::= ei::EnvItem sofar::[Pair] +{ + return case ei.dcl of + | termDcl(fn, _, just(en), _) -> pair(en, ei.dcl) :: sofar + | _ -> sofar + end; +} + +function buildTerminalTree +EnvTree ::= eis::[EnvItem] +{ + return directBuildTree(foldr(filterAndConvertTermDcls,[],eis)); +} + +aspect production i_emptyEnv +top::Env ::= +{ + top.terminalTree = [emptyEnvTree()]; +} + +aspect production i_appendEnv +top::Env ::= e1::Decorated Env e2::Decorated Env +{ + top.terminalTree = e1.terminalTree ++ e2.terminalTree; +} + +aspect production i_newScopeEnv +top::Env ::= d::Defs e::Decorated Env +{ + top.terminalTree = buildTerminalTree(d.typeList) :: e.terminalTree; +} + +aspect production i_occursEnv +top::Env ::= _ e::Decorated Env +{ + top.terminalTree = e.terminalTree; +} + diff --git a/grammars/silver/compiler/extension/easyterminal/TerminalDcl.sv b/grammars/silver/compiler/extension/easyterminal/TerminalDcl.sv new file mode 100644 index 000000000..f03d97b66 --- /dev/null +++ b/grammars/silver/compiler/extension/easyterminal/TerminalDcl.sv @@ -0,0 +1,119 @@ +grammar silver:compiler:extension:easyterminal; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:modification:lambda_fn; + +import silver:regex only Regex, regexLiteral; + +import silver:util:treeset as ts; +import silver:langutil:lsp as lsp; + +terminal Terminal_t /\'[^\'\r\n]*\'/ lexer classes {LITERAL, lsp:Regexp}; + +-- TODO: refactor this to actually create two separate terminal declarations, one regular regex, one single quote. + -- TODO: alternatively, we keep this as a 'RegExpr', but we introduce "added terminal modifiers" synthesized + -- attribute on RegExpr. This would preserve the marking token aspect of the extension here. +-- TODO: Add a 'quoted name' terminal modifier to regular terminal declarations. + -- e.g. "terminal Attr /_?_?attribute_?_?/ quoted='__attribute__';" +-- TODO: Make this declaration forward to regular terminal declaration, adding the "quoted" modifier. + +-- TODO: We could probably remove single quoted terminal references from all the RHSs, and add them to the Type syntax + -- Standing in the way of doing this is the busted aspect syntax. We'd have to fix that first. See the forward with the TODO. + + +{-- Introduce single quoted terminal declarations -} +concrete production regExprEasyTerm +top::RegExpr ::= t::Terminal_t +{ + top.unparse = t.lexeme; + + production easyName::String = substring(1, length(t.lexeme) - 1, t.lexeme); + top.easyName = just(easyName); + + forwards to regExpr(regexLiteral(easyName), location=top.location); +} + +{-- Abstracts away looking up terminals in the environment -} +nonterminal EasyTerminalRef with config, location, grammarName, unparse, errors, typerep, easyString, env, dcls; + +{-- String literal between quotes. e.g. 'hi"' is hi" -} +synthesized attribute easyString :: String; + +concrete production easyTerminalRef +top::EasyTerminalRef ::= t::Terminal_t +{ + top.unparse = t.lexeme; + top.easyString = substring(1, length(t.lexeme) - 1, t.lexeme); + + top.dcls = getTerminalRegexDclAll(top.easyString, top.env); + + top.errors := + if null(top.dcls) then + [err(t.location, "Could not find terminal declaration for " ++ t.lexeme )] + else if length(top.dcls) > 1 then + [err(t.location, "Found ambiguous possibilities for " ++ t.lexeme ++ "\n" ++ printPossibilities(top.dcls))] + else []; + + top.typerep = if null(top.dcls) then errorType() else head(top.dcls).typeScheme.monoType; +} + + +concrete production productionRhsElemEasyReg +top::ProductionRHSElem ::= id::Name '::' reg::EasyTerminalRef +{ + top.unparse = id.unparse ++ "::" ++ reg.unparse; + top.errors <- reg.errors; + + top.lambdaBoundVars := [id.name]; -- Needed because we are forwrding based on env + + forwards to productionRHSElem(id, $2, typerepTypeExpr(reg.typerep, location=reg.location), location=top.location); +} + +concrete production productionRhsElemTypeEasyReg +top::ProductionRHSElem ::= reg::EasyTerminalRef +{ + top.unparse = reg.unparse; + top.errors <- reg.errors; + + top.lambdaBoundVars := []; -- Needed because we are forwrding based on env + + forwards to productionRHSElemType(typerepTypeExpr(reg.typerep, location=top.location), location=top.location); +} + +concrete production aspectRHSElemEasyReg +top::AspectRHSElem ::= reg::EasyTerminalRef +{ + top.unparse = reg.unparse; + top.errors <- reg.errors; + + forwards to aspectRHSElemNone('_', location=reg.location); -- TODO This isn't checking if the type is right!! +} + +concrete production aspectRHSElemTypedEasyReg +top::AspectRHSElem ::= id::Name '::' reg::EasyTerminalRef +{ + top.unparse = id.unparse ++ " :: " ++ reg.unparse; + top.errors <- reg.errors; + + forwards to aspectRHSElemTyped(id, $2, typerepTypeExpr(reg.typerep, location=reg.location), location=top.location); +} + +{-- Introduce single quoted terminal literals in expressions -} +concrete production terminalExprReg +top::Expr ::= reg::EasyTerminalRef +{ + top.unparse = reg.unparse; + propagate freeVars; + top.errors <- reg.errors; + + local escapedName :: String = escapeString(reg.easyString); + + forwards to terminalFunction('terminal', '(', + typerepTypeExpr(reg.typerep, location=reg.location), + ',', stringConst(terminal(String_t, "\"" ++ escapedName ++ "\""), location=reg.location), ')', location=top.location); +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/AttributeDefs.sv b/grammars/silver/compiler/extension/implicit_monads/AttributeDefs.sv new file mode 100644 index 000000000..79b5d5d31 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/AttributeDefs.sv @@ -0,0 +1,144 @@ +grammar silver:compiler:extension:implicit_monads; + + +concrete production attributeDclInh_Restricted +top::AGDcl ::= 'restricted' 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "restricted inherited attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + top.moduleNames := []; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors := []; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + local fwd::AGDcl = defsAGDcl([restrictedInhDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)], location=top.location); + + forwards to fwd; +} + + +concrete production attributeDclSyn_Restricted +top::AGDcl ::= 'restricted' 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "restricted synthesized attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + top.moduleNames := []; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors := []; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + local fwd::AGDcl = defsAGDcl([restrictedSynDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)], location=top.location); + + forwards to fwd; +} + + + + +concrete production attributeDclInh_Implicit +top::AGDcl ::= 'implicit' 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "implicit inherited attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + top.moduleNames := []; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors := if isMonad(te.typerep, top.env) + then [] + else [err(top.location, "Implicit attributes must have a monadic type; " ++ + prettyType(te.typerep) ++ " is not monadic")]; + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + local fwd::AGDcl = defsAGDcl([implicitInhDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)], location=top.location); + + forwards to fwd; +} + + +concrete production attributeDclSyn_Implicit +top::AGDcl ::= 'implicit' 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "implicit synthesized attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + top.moduleNames := []; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + top.errors := if isMonad(te.typerep, top.env) + then [] + else [err(top.location, "Implicit attributes must have a monadic type; " ++ + prettyType(te.typerep) ++ " is not monadic")]; + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + + top.errors <- tl.errorsTyVars; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + local fwd::AGDcl = defsAGDcl([implicitSynDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)], location=top.location); + + forwards to fwd; +} + + + + +concrete production attributeDclInh_Unrestricted +top::AGDcl ::= 'unrestricted' 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "unrestricted inherited attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + forwards to attributeDclInh('inherited', 'attribute', a, tl, '::', te, ';', location=top.location); +} + + +concrete production attributeDclSyn_Unrestricted +top::AGDcl ::= 'unrestricted' 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + top.unparse = "unrestricted synthesized attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; + + forwards to attributeDclSyn('synthesized', 'attribute', a, tl, '::', te, ';', location=top.location); +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/Case.sv b/grammars/silver/compiler/extension/implicit_monads/Case.sv new file mode 100644 index 000000000..3b0a9443a --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Case.sv @@ -0,0 +1,721 @@ +grammar silver:compiler:extension:implicit_monads; + +--import silver:compiler:definition:type:syntax only typerepTypeExpr; + +terminal MCase_kwd 'case_any' lexer classes {KEYWORD, RESERVED}; + + +synthesized attribute patternType::Type occurs on Pattern; +synthesized attribute patternTypeList::[Type] occurs on PatternList; + +attribute patternTypeList, mUpSubst, mDownSubst occurs on MRuleList; +attribute patternTypeList occurs on MatchRule; + + +aspect production caseExpr_c +top::Expr ::= 'case' es::Exprs 'of' vbar::Opt_Vbar_t ml::MRuleList 'end' +{ + ml.mDownSubst = top.mDownSubst; + local monadInExprs::Boolean = + monadicallyUsedExpr(es.rawExprs, top.env, ml.mUpSubst, top.frame, + top.grammarName, top.compiledGrammars, top.config, top.flowEnv, + top.expectedMonad, false, top.originRules); + local monadInClauses::Boolean = + foldl((\b::Boolean a::AbstractMatchRule -> + b || + let ty::Type = decorate a with {mDownSubst=ml.mUpSubst; temp_env=top.env; temp_frame=top.frame; + temp_grammarName=top.grammarName; temp_compiledGrammars=top.compiledGrammars; + temp_config=top.config; temp_flowEnv=top.flowEnv; + temp_finalSubst=ml.mUpSubst; temp_downSubst=ml.mUpSubst; + expectedMonad=top.expectedMonad;}.mtyperep + in + isMonad(ty, top.env) && + monadsMatch(ty, top.expectedMonad, ml.mUpSubst).fst && fst(monadsMatch(ty, top.expectedMonad, ml.mUpSubst)) + end), + false, + ml.matchRuleList); + + local basicFailure::Expr = mkStrFunctionInvocation(top.location, "silver:core:error", + [stringConst(terminal(String_t, + "\"Error: pattern match failed at " ++ top.grammarName ++ + " " ++ top.location.unparse ++ "\\n\""), + location=top.location)]); + {- + Inserting fails breaks down if the current monad's fail is + expecting something other than a string, integer, float, or list, + as we don't really have ways to come up with basic fail arguments + for anything more complex. + -} + local failure::Expr = + if isMonadFail(top.expectedMonad, top.env) + then monadFail(top.location) + else basicFailure; + {- + This sets up the actual output type. If there's a monad, the + return type given to the case expression is M(freshtype); if not, + the return type is just a fresh type. + -} + local outty::Type = + if monadInExprs + then monadOfType(top.expectedMonad, freshType()) + else if monadInClauses + then monadOfType(top.expectedMonad, freshType()) + else if isMonadFail(top.expectedMonad, top.env) + then monadOfType(top.expectedMonad, freshType()) + else freshType(); --absolutely nothing is a monad + --read the comment on the function below if you want to know what it is + local attribute monadStuff::([(Type, Expr, String)], [Expr]); + monadStuff = monadicMatchTypesNames(redeces.monadDecExprs, ml.patternTypeList, [], top.env, + ml.mUpSubst, top.expectedMonad, top.location, 1); + local attribute redeces::Exprs = es; + redeces.mDownSubst = ml.mUpSubst; + redeces.downSubst = ml.mUpSubst; + redeces.finalSubst = ml.mUpSubst; + redeces.env = top.env; + redeces.frame = top.frame; + redeces.grammarName = top.grammarName; + redeces.compiledGrammars = top.compiledGrammars; + redeces.config = top.config; + redeces.flowEnv = top.flowEnv; + redeces.expectedMonad = top.expectedMonad; + redeces.isRoot = top.isRoot; + redeces.originRules = top.originRules; + + {- + We rewrite by pulling the monad-typed expressions on which we are matching + out, putting them into lets over the case expression, then rewriting that. + This second rewriting step turns the lets into binds, binding the monadic + expressions into the case that is not matching on monads, which then + rewrites everything within the match rules. + -} + local monadLocal::Expr = + foldr(\ p::(Type, Expr, String) rest::Expr -> + makeLet(top.location, p.3, monadInnerType(p.1, top.location), p.2, rest), + caseExpr(monadStuff.snd, + ml.matchRuleList, !isMonadFail(top.expectedMonad, top.env), failure, + outty, location=top.location), + monadStuff.1); + monadLocal.mDownSubst = ml.mUpSubst; + monadLocal.frame = top.frame; + monadLocal.grammarName = top.grammarName; + monadLocal.compiledGrammars = top.compiledGrammars; + monadLocal.config = top.config; + monadLocal.env = top.env; + monadLocal.flowEnv = top.flowEnv; + monadLocal.downSubst = ml.mUpSubst; + monadLocal.finalSubst = top.finalSubst; + monadLocal.expectedMonad = top.expectedMonad; + monadLocal.originRules = top.originRules; + monadLocal.isRoot = false; + top.monadRewritten = monadLocal.monadRewritten; + top.mtyperep = monadLocal.mtyperep; + top.mUpSubst = monadLocal.mUpSubst; + + monadLocal.monadicallyUsed = false; + --We get the monadic names out of the expressions bound in here and the rest off the fake forward (monadLocal) + top.monadicNames = + foldr(\x::Pair l::[Expr] -> + let a::Decorated Expr with MonadInhs = + decorate x.fst with {env=top.env; mDownSubst=top.mDownSubst; + frame=top.frame; grammarName=top.grammarName; downSubst=top.mDownSubst; + finalSubst=top.mDownSubst; compiledGrammars=top.compiledGrammars; + config=top.config; flowEnv=top.flowEnv;expectedMonad=top.expectedMonad; + isRoot=top.isRoot; originRules=top.originRules;} + in if isMonad(a.mtyperep, top.env) && monadsMatch(a.mtyperep, top.expectedMonad, top.mDownSubst).fst && + !isMonad(performSubstitution(x.snd, top.mDownSubst), top.env) + then decorate x.fst with {env=top.env; mDownSubst=top.mDownSubst; + frame=top.frame; grammarName=top.grammarName; downSubst=top.mDownSubst; + finalSubst=top.mDownSubst; compiledGrammars=top.compiledGrammars; + config=top.config; flowEnv=top.flowEnv; monadicallyUsed=true; + expectedMonad=top.expectedMonad; isRoot=top.isRoot; originRules=top.originRules;}.monadicNames + else [] + end ++ l, + monadLocal.monadicNames, zipWith(\x::Expr y::Type -> pair(x,y), es.rawExprs, ml.patternTypeList)); +} +--find if any of the expressions are being matched as their inner type +--if returns (true, ty), ty will be used to find the correct Fail() +function monadicallyUsedExpr +Boolean ::= elst::[Expr] env::Decorated Env sub::Substitution f::BlockContext gn::String + cg::EnvTree c::Decorated CmdArgs fe::FlowEnv em::Type + iR::Boolean oR::[Decorated Expr] +{ + return case elst of + | [] -> false + | e::etl -> + let etyp::Type = decorate e with {env=env; mDownSubst=sub; frame=f; grammarName=gn; + downSubst=sub; finalSubst=sub; + compiledGrammars=cg; config=c; flowEnv=fe; + expectedMonad=em; isRoot=iR; originRules=oR;}.mtyperep + in + fst(monadsMatch(etyp, em, sub)) || monadicallyUsedExpr(etl, env, sub, f, gn, cg, c, fe, em, iR, oR) + end + end; +} +--make a list of the expression types, rewritten expressions and names for +-- binding them as well as a new list of expressions for the forward to use +--use a name from names when that is not empty; when empty, use a new name +function monadicMatchTypesNames +([(Type, (Expr, String))], [Expr]) ::= + elst::[Decorated Expr with MonadInhs] + tylst::[Type] names::[String] env::Decorated Env sub::Substitution em::Type + loc::Location index::Integer +{ + local attribute subcall::([(Type, Expr, String)], [Expr]); + subcall = case elst, tylst of + | _::etl, _::ttl -> monadicMatchTypesNames(etl, ttl, ntail, env, sub, em, loc, index + 1) + | [], [] -> error("Should not access subcall in monadicMatchTypesNames with empty lists") + | _, _ -> error("Both lists in monadicMatchTypesNames must be the same length") + end; + local ntail::[String] = if null(names) then [] else tail(names); + local newName::String = if null(names) + then "__sv_expression_in_case" ++ toString(index) ++ "_" ++ toString(genInt()) + else head(names); + return case elst, tylst of + | [], _ -> pair([], []) + | _, [] -> pair([], map(new, elst)) + | decE::etl, t::ttl -> + let ety::Type = decE.mtyperep + in + if isMonad(ety, env) && fst(monadsMatch(ety, em, sub)) + then ((ety, decE.monadRewritten, newName) :: subcall.1, + baseExpr(qName(loc, newName), location=loc) :: subcall.2) + else (subcall.1, new(decE)::subcall.2) + end + end; +} + +{-We need to essentially set up our own compilation here for + monadRewritten because Ted doesn't like duplicating generated code. + Putting the monad default fail into a let with a monad type is + turning into a bind over the matching, so everything matching + fails.-} +aspect production caseExpr +top::Expr ::= es::[Expr] ml::[AbstractMatchRule] complete::Boolean failExpr::Expr retType::Type { + local monadLocal::Expr = monadCompileCaseExpr(es, ml, failExpr, retType, top.location, top.env); + monadLocal.mDownSubst = top.mDownSubst; + monadLocal.frame = top.frame; + monadLocal.grammarName = top.grammarName; + monadLocal.compiledGrammars = top.compiledGrammars; + monadLocal.config = top.config; + monadLocal.env = top.env; + monadLocal.flowEnv = top.flowEnv; + monadLocal.downSubst = top.mDownSubst; + monadLocal.finalSubst = top.finalSubst; + monadLocal.expectedMonad = top.expectedMonad; + monadLocal.originRules = top.originRules; + monadLocal.isRoot = top.isRoot; + + top.monadRewritten = monadLocal.monadRewritten; + + top.merrors <- checkOverlappingPatterns(es, ml); +} + + +{- + We need to compile the case expression for monad rewriting to take + advantage of primitive matching's monad rewriting for patterns, but + without ending up rewriting any lets which will turn out poorly with + rewriting. For example, the standard compilation involving + rewriting to "let __fail = in ", which will not + work for us if our failure expression has a monadic type, as it + often does. + + This is essentially the standard compilation, but without inserting + failure lets. + + If there are ever any size problems with the translation here, + introduce a new production (probably named mLet) which would be a + let but with monadRewritten being just a let, no checking of types + and binding them in. That would be safe, but would still require + copying the function format from the patternmatching extension. +-} +function monadCompileCaseExpr +Expr ::= es::[Expr] ml::[AbstractMatchRule] failExpr::Expr retType::Type loc::Location env::Decorated Env +{ + --Split rules into segments of non-forwarding constructors, all same + -- forwarding constructor, and variables based on first pattern + local groups::[[AbstractMatchRule]] = splitPatternGroups(ml, env); + + local compiledGroups::Expr = + monadCompilePatternGroups(es, groups, failExpr, retType, loc, env); + + --Check if there is any match rule with empty patterns + local anyEmptyRules::Boolean = + any(map(\ m::AbstractMatchRule -> + case m of + | matchRule([], _, _) -> true + | _ -> false + end, ml)); + + --Assume all the rules are devoid of patterns + local finalStep::Expr = + foldr(\ mrule::AbstractMatchRule rest::Expr -> + case mrule of + | matchRule(_, nothing(), e) -> e + --cond is a Boolean + | matchRule(_, just((cond, nothing())), e) -> + ifThenElse('if', cond, 'then', e, 'else', rest, location=loc) + --cond is the expression for another match + | matchRule(_, just((cond, just(patt))), e) -> + Silver_Expr { + case $Expr{cond} of + | $Pattern{patt} -> $Expr{e} + | _ -> $Expr{failExpr} + end + } + end, + failExpr, ml); + + return + case ml of + | [] -> failExpr + | _ -> if anyEmptyRules then finalStep else compiledGroups + end; +} + + +--Compile a match where the sets of patterns are grouped based on +--having the same kind of first pattern (can go in the same primitive +--match together), create a series of primitive match expressions to +--implement the match +-- +--The same as the compilePatternGroups function from the patternmatching extension, +-- but without using lets which would end up being problematic in our implicit use +function monadCompilePatternGroups +Expr ::= matchEs::[Expr] ruleGroups::[[AbstractMatchRule]] finalFail::Expr + retType::Type loc::Location env::Decorated Env +{ + local compileRest::Expr = + monadCompilePatternGroups(matchEs, tail(ruleGroups), finalFail, + retType, loc, env); + + local firstGroup::[AbstractMatchRule] = + case ruleGroups of + | [] -> error("Shouldn't access firstGroup with empty ruleGroups") + | []::tl -> + error("Shouldn't have empty list of patterns in monadCompilePatternGroups") + | hd::tl -> hd + end; + local firstPatt::Decorated Pattern = head(firstGroup).headPattern; + local firstMatchExpr::Expr = + case matchEs of + | [] -> + error("Shouldn't call monadCompilePatternGroups with empty match expressions") + | e::tl -> e + end; + + --Modifying the order of rules in the same group (from ruleGroups) is fine, + --since we either have only the same constructor for a forwarding production + --or multiple non-forwarding productions where a value can only match one of them + local constructorGroups::[[AbstractMatchRule]] = groupMRules(firstGroup); + local mappedPatterns::[PrimPattern] = + map(monadAllConCaseTransform(head(matchEs), tail(matchEs), + compileRest, retType, _, env), + constructorGroups); + local currentConCase::Expr = + matchPrimitive(firstMatchExpr, typerepTypeExpr(retType, location=loc), + foldPrimPatterns(mappedPatterns), + compileRest, location=loc); + + -- A quick note about that freshType() hack: putting it here means there's ONE fresh type + -- generated, puching it inside 'bindHeadPattern' would generate multiple fresh types. + -- So don't try that! + local boundVarRules::[AbstractMatchRule] = + map(bindHeadPattern(firstMatchExpr, freshType(), _), firstGroup); + local currentVarCase::Expr = + monadCompileCaseExpr(tail(matchEs), boundVarRules, + compileRest, retType, loc, env); + + return + case ruleGroups of + | [] -> finalFail + | _::_ -> if firstPatt.patternIsVariable + then currentVarCase + else currentConCase + end; +} + + + +{- + Turn matching on all constructors into primitive matching. +-} +function monadAllConCaseTransform +PrimPattern ::= currExpr::Expr restExprs::[Expr] failCase::Expr retType::Type mrs::[AbstractMatchRule] env::Decorated Env +{ + local names :: [Name] = map(patternListVars, head(mrs).headPattern.patternSubPatternList); + + local subcase :: Expr = + monadCompileCaseExpr( + map(exprFromName, names) ++ annoAccesses ++ restExprs, + map(\ mr::AbstractMatchRule -> mr.expandHeadPattern(annos), mrs), + failCase, retType, head(mrs).location, env); + + local annos :: [String] = + nub(map(fst, flatMap((.patternNamedSubPatternList), map((.headPattern), mrs)))); + local annoAccesses :: [Expr] = + map(\ n::String -> access(currExpr, '.', qNameAttrOccur(qName(l, n), location=l), location=l), annos); + + -- Maybe this one is more reasonable? We need to test examples and see what happens... + local l :: Location = head(mrs).headPattern.location; + + return + case head(mrs).headPattern of + | prodAppPattern_named(qn,_,_,_,_,_) -> + prodPattern(qn, '(', convStringsToVarBinders(names, l), ')', terminal(Arrow_kwd, "->", l), subcase, location=l) + | intPattern(it) -> integerPattern(it, terminal(Arrow_kwd, "->", l), subcase, location=l) + | fltPattern(it) -> floatPattern(it, terminal(Arrow_kwd, "->", l), subcase, location=l) + | strPattern(it) -> stringPattern(it, terminal(Arrow_kwd, "->", l), subcase, location=l) + | truePattern(_) -> booleanPattern("true", terminal(Arrow_kwd, "->", l), subcase, location=l) + | falsePattern(_) -> booleanPattern("false", terminal(Arrow_kwd, "->", l), subcase, location=l) + | nilListPattern(_,_) -> nilPattern(subcase, location=l) + | consListPattern(h,_,t) -> conslstPattern(head(names), head(tail(names)), subcase, location=l) + | _ -> error("Can only have constructor patterns in monadAllConCaseTransform") + end; +} + +--case expression that expands, using mplus, to possibly take multiple cases +concrete production mcaseExpr_c +top::Expr ::= 'case_any' es::Exprs 'of' vbar::Opt_Vbar_t ml::MRuleList 'end' +{ + top.unparse = "case_any " ++ es.unparse ++ " of " ++ ml.unparse ++ " end"; + + top.merrors := []; + top.merrors <- if isMonadPlus_instance then [] else [err(top.location, notMonadPlus)]; + + ml.mDownSubst = top.mDownSubst; + local monadInExprs::Boolean = + monadicallyUsedExpr(es.rawExprs, top.env, ml.mUpSubst, top.frame, + top.grammarName, top.compiledGrammars, top.config, top.flowEnv, + top.expectedMonad, false, top.originRules); + + local mplus::Expr = monadPlus(top.location); + local mzero::Expr = monadZero(top.location); + + local isMonadPlus_instance::Boolean = isMonadPlus(top.expectedMonad, top.env); + local notMonadPlus::String = + monadToString(top.expectedMonad) ++ " is not an instance of " ++ + "MonadPlus and cannot be used with case_any"; + + --we need fresh names for the expressions being matched on, which we will use to only evaluate them once + local newNames::[String] = map(\ x::Expr -> "__sv_mcase_var_" ++ toString(genInt()), es.rawExprs); + local params::[Pair] = zipWith(pair, newNames, ml.patternTypeList); + local nameExprs::[Expr] = map(\x::String -> baseExpr(qName(top.location, x), location=top.location), + newNames); + + --Build a separate case expression for each match rule with mzero as the failure + local caseExprs::[Expr] = + map(\ x::AbstractMatchRule -> + caseExpr(nameExprs, [x], false, mzero, top.mtyperep, location=top.location), + ml.matchRuleList); + --Rewrite the case expressions, wrapped in lambdas to provide the names + local rewrittenCaseExprs::[Expr] = + map(\ x::Expr -> + decorate buildMultiLambda(params, x, top.location) with + {mDownSubst = top.mDownSubst; + frame = top.frame; + grammarName = top.grammarName; + compiledGrammars = top.compiledGrammars; + config = top.config; + env = top.env; + flowEnv = top.flowEnv; + downSubst = top.mDownSubst; + finalSubst = top.mDownSubst; + expectedMonad = top.expectedMonad; + isRoot = top.isRoot; + originRules = top.originRules; + }.monadRewritten, + caseExprs); + --Take the rewritten functions and apply them to the names to get expressions of a monad type + local appliedCaseExprs::[Expr] = + map(\ x::Expr -> buildApplication(x, nameExprs, top.location), + rewrittenCaseExprs); + --In some cases, they might not return monadic types, so we need to check and add return + local typecheckedCaseExprs::[Expr] = + map(\ x::Expr -> + let ty::Type = decorate x with + {flowEnv = top.flowEnv; env = top.env; config=top.config; + compiledGrammars=top.compiledGrammars; grammarName=top.grammarName; + frame=top.frame; downSubst=top.mDownSubst; finalSubst=top.mDownSubst; + isRoot=top.isRoot; originRules=top.originRules; + }.typerep + in + if isMonad(ty, top.env) && monadsMatch(ty, top.expectedMonad, top.mDownSubst).fst + then x + else buildApplication(monadReturn(top.location), [x], top.location) + end, + appliedCaseExprs); + --Mplus the rewritten-and-applied case expressions together + local mplused::Expr = foldl(\rest::Expr current::Expr -> + Silver_Expr{ + $Expr{mplus}($Expr{rest}, $Expr{current}) + }, + head(typecheckedCaseExprs), tail(typecheckedCaseExprs)); + --Use bind and lambdas to change the names of everything being matched on to only evaluate it once + local applied::Expr = + mcaseBindsApps(es.rawExprs, newNames, top.location, mplused, + top.env, ml.mUpSubst, top.frame, top.grammarName, + top.compiledGrammars, top.config, top.flowEnv, + top.expectedMonad, top.isRoot, top.originRules); + + top.monadRewritten = applied; + top.mUpSubst = ml.mUpSubst; + top.mtyperep = monadOfType(top.expectedMonad, freshType()); + + --We need to forward to an errorExpr rather than the rewritten version to avoid flow errors + --Because this should only be used in implicit equations, it should be fine + forwards to errorExpr([err(top.location, + "Can only use case_any in implicit equations")], + location=top.location); +} + +{- + We walk down exprs and names to either bind head(exprs) using + head(names) for it if it is monadic, otherwise simply applying it to + a lambda to change the name. +-} +function mcaseBindsApps +Expr ::= exprs::[Expr] names::[String] loc::Location base::Expr + env::Decorated Env sub::Substitution f::BlockContext + gn::String cg::EnvTree c::Decorated CmdArgs + fe::FlowEnv em::Type iR::Boolean oR::[Decorated Expr] +{ + local subcall::Expr = + mcaseBindsApps(tail(exprs), tail(names), loc, base, + env, sub, f, gn, cg, c, fe, em, iR, oR); + return + if null(exprs) + then base + else let ety::Type = decorate head(exprs) with + {env=env; mDownSubst=sub; frame=f; grammarName=gn; + downSubst=sub; finalSubst=sub; + compiledGrammars=cg; config=c; flowEnv=fe; + expectedMonad=em; originRules = oR; + isRoot = iR;}.mtyperep + in + if isMonad(ety, env) && fst(monadsMatch(ety, em, sub)) + then buildApplication( + monadBind(loc), + [if ety.isDecorated + then mkStrFunctionInvocation(loc, "silver:core:new", [head(exprs)]) + else head(exprs), + buildLambda(head(names), monadInnerType(ety, loc), subcall, loc)], + loc) + else buildApplication(buildLambda(head(names), dropDecorated(ety), subcall, loc), + [head(exprs)], loc) + end; +} + + + + +synthesized attribute monadDecExprs::[Decorated Expr with MonadInhs]; +attribute monadDecExprs, mDownSubst, expectedMonad occurs on Exprs; + +aspect production exprsEmpty +top::Exprs ::= +{ + top.monadDecExprs = []; +} +aspect production exprsSingle +top::Exprs ::= e::Expr +{ + e.expectedMonad = top.expectedMonad; + e.mDownSubst = top.mDownSubst; + top.monadDecExprs = [e]; +} +aspect production exprsCons +top::Exprs ::= e1::Expr ',' e2::Exprs +{ + e1.expectedMonad = top.expectedMonad; + e1.mDownSubst = top.mDownSubst; + e2.expectedMonad = top.expectedMonad; + e2.mDownSubst = top.mDownSubst; + top.monadDecExprs = e1::e2.monadDecExprs; +} + + + +--There are several thing we need for mtyperep on e which don't occur on match rules +--Therefore we need to pass them here +inherited attribute temp_flowEnv::FlowEnv; +inherited attribute temp_env::Decorated Env; +inherited attribute temp_config::Decorated CmdArgs; +inherited attribute temp_compiledGrammars::EnvTree; +inherited attribute temp_grammarName::String; +inherited attribute temp_frame::BlockContext; +inherited attribute temp_finalSubst::Substitution; +inherited attribute temp_downSubst::Substitution; +attribute temp_flowEnv, temp_compiledGrammars, temp_grammarName occurs on MatchRule, MRuleList; +attribute mDownSubst occurs on MatchRule; + + +aspect production mRuleList_one +top::MRuleList ::= m::MatchRule +{ + m.temp_compiledGrammars = top.temp_compiledGrammars; + m.temp_flowEnv = top.temp_flowEnv; + m.temp_grammarName = top.temp_grammarName; + m.mDownSubst = top.mDownSubst; + + top.patternTypeList = m.patternTypeList; + top.mUpSubst = top.mDownSubst; +} + +aspect production mRuleList_cons +top::MRuleList ::= h::MatchRule vbar::Vbar_kwd t::MRuleList +{ + h.temp_compiledGrammars = top.temp_compiledGrammars; + h.temp_flowEnv = top.temp_flowEnv; + h.temp_grammarName = top.temp_grammarName; + h.mDownSubst = top.mDownSubst; + + t.temp_compiledGrammars = top.temp_compiledGrammars; + t.temp_flowEnv = top.temp_flowEnv; + t.temp_grammarName = top.temp_grammarName; + t.mDownSubst = top.mDownSubst; + + top.patternTypeList = h.patternTypeList; + --need to unify here with t.patternTypeList so, when we reach the case, if there is a + -- monad pattern farther down where the first one is a wildcard/variable, we'll find + -- it and not incorrectly identify something as being used non-monadically + top.mUpSubst = foldl(\s::Substitution p::Pair -> + decorate check(p.fst, p.snd) with {downSubst=s;}.upSubst, + t.mUpSubst, zipWith(pair, h.patternTypeList, t.patternTypeList)); +} + +aspect production matchRule_c +top::MatchRule ::= pt::PatternList arr::Arrow_kwd e::Expr +{ + local ne::Expr = e; + ne.flowEnv = top.temp_flowEnv; + ne.env = top.env; + ne.config = top.config; + ne.compiledGrammars = top.temp_compiledGrammars; + ne.grammarName = top.temp_grammarName; + ne.frame = top.frame; + ne.finalSubst = top.mDownSubst; + ne.downSubst = top.mDownSubst; + ne.originRules = []; + ne.isRoot = false; + + top.patternTypeList = pt.patternTypeList; + + top.notExplicitAttributes := ne.notExplicitAttributes; +} + +aspect production matchRuleWhen_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr arr::Arrow_kwd e::Expr +{ + local ncond::Expr = cond; + ncond.flowEnv = top.temp_flowEnv; + ncond.env = top.env; + ncond.config = top.config; + ncond.compiledGrammars = top.temp_compiledGrammars; + ncond.grammarName = top.temp_grammarName; + ncond.frame = top.frame; + ncond.finalSubst = top.mDownSubst; + ncond.downSubst = top.mDownSubst; + ncond.originRules = []; + ncond.isRoot = false; + local ne::Expr = e; + ne.flowEnv = top.temp_flowEnv; + ne.env = top.env; + ne.config = top.config; + ne.compiledGrammars = top.temp_compiledGrammars; + ne.grammarName = top.temp_grammarName; + ne.frame = top.frame; + ne.finalSubst = top.mDownSubst; + ne.downSubst = top.mDownSubst; + ne.originRules = []; + ne.isRoot = false; + + top.patternTypeList = pt.patternTypeList; + + top.notExplicitAttributes := ncond.notExplicitAttributes ++ ne.notExplicitAttributes; +} + +aspect production matchRuleWhenMatches_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern arr::Arrow_kwd e::Expr +{ + local ncond::Expr = cond; + ncond.flowEnv = top.temp_flowEnv; + ncond.env = top.env; + ncond.config = top.config; + ncond.compiledGrammars = top.temp_compiledGrammars; + ncond.grammarName = top.temp_grammarName; + ncond.frame = top.frame; + ncond.finalSubst = top.mDownSubst; + ncond.downSubst = top.mDownSubst; + ncond.originRules = []; + ncond.isRoot = false; + local ne::Expr = e; + ne.flowEnv = top.temp_flowEnv; + ne.env = top.env; + ne.config = top.config; + ne.compiledGrammars = top.temp_compiledGrammars; + ne.grammarName = top.temp_grammarName; + ne.frame = top.frame; + ne.finalSubst = top.mDownSubst; + ne.downSubst = top.mDownSubst; + ne.originRules = []; + ne.isRoot = false; + + top.patternTypeList = pt.patternTypeList; + + top.notExplicitAttributes := ncond.notExplicitAttributes ++ ne.notExplicitAttributes; +} + +aspect production patternList_one +top::PatternList ::= p::Pattern +{ +-- top.errors := p.errors; + + top.patternTypeList = [p.patternType]; +} +aspect production patternList_more +top::PatternList ::= p::Pattern ',' ps1::PatternList +{ +-- top.errors := p.errors ++ ps1.errors; + + top.patternTypeList = p.patternType :: ps1.patternTypeList; +} + +aspect production patternList_nil +top::PatternList ::= +{ +-- top.errors := []; + + top.patternTypeList = []; +} + + + +attribute temp_flowEnv, temp_env, temp_config, temp_compiledGrammars, temp_grammarName, + temp_frame, temp_finalSubst, temp_downSubst occurs on AbstractMatchRule; + +attribute mDownSubst, merrors, mtyperep, expectedMonad occurs on AbstractMatchRule; + +aspect production matchRule +top::AbstractMatchRule ::= pl::[Decorated Pattern] cond::Maybe<(Expr, Maybe)> e::Expr +{ + local ne::Expr = e; + ne.flowEnv = top.temp_flowEnv; + ne.env = top.temp_env; + ne.config = top.temp_config; + ne.compiledGrammars = top.temp_compiledGrammars; + ne.grammarName = top.temp_grammarName; + ne.frame = top.temp_frame; + ne.finalSubst = top.temp_finalSubst; + ne.downSubst = top.temp_downSubst; + ne.originRules = []; + ne.isRoot = false; + + ne.mDownSubst = top.mDownSubst; + ne.expectedMonad = top.expectedMonad; + top.merrors := []; --merrors from e should be picked up in primitive matching + top.mtyperep = ne.mtyperep; + + top.notExplicitAttributes := ne.notExplicitAttributes; +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/CopperExpr.sv b/grammars/silver/compiler/extension/implicit_monads/CopperExpr.sv new file mode 100644 index 000000000..08908055a --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/CopperExpr.sv @@ -0,0 +1,68 @@ +grammar silver:compiler:extension:implicit_monads; + +{- + I honestly have no idea if these can occur in a production. I kind of doubt + it with them being Copper expressions. +-} + +aspect production actionChildReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + top.mUpSubst = top.mDownSubst; + top.mtyperep = q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production pluckTerminalReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + top.mUpSubst = top.mDownSubst; + top.mtyperep = terminalIdType(); + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production terminalIdReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + top.mUpSubst = top.mDownSubst; + top.mtyperep = terminalIdType(); + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production parserAttributeReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + top.mUpSubst = top.mDownSubst; + top.mtyperep = q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production termAttrValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + top.mUpSubst = top.mDownSubst; + top.mtyperep = q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + --this is just a Silver name (e.g. lexeme), not a user-defined name, + -- so it should be fine to leave it decorated + top.monadRewritten = termAttrValueReference(q, location=top.location); +} diff --git a/grammars/silver/compiler/extension/implicit_monads/DclInfo.sv b/grammars/silver/compiler/extension/implicit_monads/DclInfo.sv new file mode 100644 index 000000000..6cd6dc517 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/DclInfo.sv @@ -0,0 +1,118 @@ +grammar silver:compiler:extension:implicit_monads; + + +abstract production restrictedSynDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.attrDefDispatcher = restrictedSynAttributeDef(_, _, _, location=_); + + --copied from synDcl + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.fullName = fn; + propagate compareKey; + + top.typeScheme = polyType(bound, ty); + + top.isSynthesized = true; + top.isInherited = false; +} + + +abstract production restrictedInhDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.attrDefDispatcher = restrictedInhAttributeDef(_, _, _, location=_); + + --copied from inhDcl + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.fullName = fn; + propagate compareKey; + + top.typeScheme = polyType(bound, ty); + + top.isSynthesized = false; + top.isInherited = true; +} + + + + +abstract production implicitSynDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.attrDefDispatcher = implicitSynAttributeDef(_, _, _, location=_); + + --copied from synDcl + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.fullName = fn; + propagate compareKey; + + top.typeScheme = polyType(bound, ty); + + top.isSynthesized = true; + top.isInherited = false; +} + + +abstract production implicitInhDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.attrDefDispatcher = implicitInhAttributeDef(_, _, _, location=_); + + --copied from inhDcl + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.fullName = fn; + propagate compareKey; + + top.typeScheme = polyType(bound, ty); + + top.isSynthesized = false; + top.isInherited = true; +} + + + + + + +function restrictedSynDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(restrictedSynDcl(fn, bound, ty, sourceGrammar=sg, sourceLocation=sl))); +} + + +function restrictedInhDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(restrictedInhDcl(fn, bound, ty, sourceGrammar=sg, sourceLocation=sl))); +} + + + + +function implicitSynDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(implicitSynDcl(fn, bound, ty, sourceGrammar=sg, sourceLocation=sl))); +} + + +function implicitInhDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(implicitInhDcl(fn, bound, ty, sourceGrammar=sg, sourceLocation=sl))); +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/Expr.sv b/grammars/silver/compiler/extension/implicit_monads/Expr.sv new file mode 100644 index 000000000..543e44e91 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Expr.sv @@ -0,0 +1,2077 @@ +grammar silver:compiler:extension:implicit_monads; + +--whether an expression needs to be bound into its immediate parent +--I think this is for let insertion, but I'll leave it here anyway +inherited attribute monadicallyUsed::Boolean occurs on Expr; +--a collection of names/attribute accesses that are monadically used +--it's a list of expressions for attribute accesses +--I think this is for let insertion too +synthesized attribute monadicNames::[Expr] occurs on Expr, AppExpr, AppExprs; + +attribute monadRewritten, merrors, mtyperep, mDownSubst, mUpSubst, expectedMonad occurs on Expr; +propagate expectedMonad on Expr; + + +type MonadInhs = { + downSubst, finalSubst, frame, grammarName, isRoot, originRules, + compiledGrammars, config, env, flowEnv, expectedMonad, mDownSubst +}; + + +--list of the attributes accessed in an explicit expression not allowed there +--this is turned into a list of appropriate error messages at the equation +monoid attribute notExplicitAttributes::[Pair]; +attribute notExplicitAttributes occurs on Expr, AppExprs, AnnoAppExprs, MRuleList, Exprs, MatchRule, AbstractMatchRule, AssignExpr; +propagate notExplicitAttributes on Expr, AppExprs, AnnoAppExprs, MRuleList, Exprs, AssignExpr excluding forwardAccess; + + +aspect default production +top::Expr ::= +{ + top.merrors := []; +} + + +aspect production errorExpr +top::Expr ::= e::[Message] +{ + top.merrors := e; + propagate mDownSubst, mUpSubst; + top.mtyperep = errorType(); + top.monadicNames = []; + top.monadRewritten = errorExpr(e, location=top.location); +} + +aspect production errorReference +top::Expr ::= msg::[Message] q::PartiallyDecorated QName +{ + top.merrors := msg; + propagate mDownSubst, mUpSubst; + top.mtyperep = errorType(); + top.monadicNames = []; + top.monadRewritten = errorReference(msg, q, location=top.location); +} + +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then q.lookupValue.typeScheme.asNtOrDecType + else q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.asNtOrDecType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then q.lookupValue.typeScheme.asNtOrDecType + else q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + -- An LHS (and thus, forward) is *always* a decorable (nonterminal) type. + top.mtyperep = q.lookupValue.typeScheme.asNtOrDecType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production productionReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.typerep; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production functionReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.typerep; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production classMemberReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.typerep; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production globalValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.typerep; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} + +aspect production application +top::Expr ::= e::Expr '(' es::AppExprs ',' anns::AnnoAppExprs ')' +{ + {- + We bind e in here because this would otherwise forward to an error. + Everything else will work out fine by rewriting in a forward other than this. + Errors might not be great if we have different monads here and in arguments; + once partial application works, we could just do the whole rewriting here + -} + local ne::Expr = new(e); + ne.mDownSubst = top.mDownSubst; + ne.env = top.env; + ne.flowEnv = top.flowEnv; + ne.config = top.config; + ne.compiledGrammars = top.compiledGrammars; + ne.grammarName = top.grammarName; + ne.frame = top.frame; + ne.finalSubst = top.finalSubst; + ne.downSubst = top.downSubst; + ne.originRules = top.originRules; + ne.isRoot = false; + local nes::AppExprs = new(es); + nes.mDownSubst = ne.mUpSubst; + nes.flowEnv = top.flowEnv; + nes.env = top.env; + nes.config = top.config; + nes.compiledGrammars = top.compiledGrammars; + nes.grammarName = top.grammarName; + nes.frame = top.frame; + nes.finalSubst = top.finalSubst; + nes.downSubst = top.downSubst; + nes.originRules = top.originRules; + nes.isRoot = false; + nes.appExprTypereps = reverse(performSubstitution(ne.mtyperep, ne.mUpSubst).inputTypes); + nes.appExprApplied = ne.unparse; + nes.monadArgumentsAllowed = acceptableMonadFunction(e); + + ne.expectedMonad = top.expectedMonad; + nes.expectedMonad = top.expectedMonad; + + top.merrors := ne.merrors ++ nes.merrors; + top.mUpSubst = nes.mUpSubst; + + top.merrors <- + case anns of + | emptyAnnoAppExprs() -> + [] + | _ -> + if null(nes.monadTypesLocations) + then [] + else [err(top.location, "Monad Rewriting not defined with annotated " ++ + "expressions in a function application")] + end; + + local substTy::Type = performSubstitution(ne.mtyperep, top.mUpSubst); + local ety :: Type = + if isMonad(substTy, top.env) && + monadsMatch(top.expectedMonad, substTy, top.mDownSubst).fst + then monadInnerType(substTy, top.location) + else substTy; + + --needs to add a monad to the result if there are monadic args or the function is monadic + top.mtyperep = + if null(nes.monadTypesLocations) + then if isMonad(substTy, top.env) && monadsMatch(top.expectedMonad, substTy, top.mDownSubst).fst + then monadOfType(top.expectedMonad, ety.outputType) + else ety.outputType + else if isMonad(ety.outputType, top.env) && fst(monadsMatch(ety.outputType, top.expectedMonad, top.mUpSubst)) + then ety.outputType + else monadOfType(top.expectedMonad, ety.outputType); + + ne.monadicallyUsed = isMonad(ne.mtyperep, top.env) && fst(monadsMatch(ne.mtyperep, top.expectedMonad, top.mUpSubst)); + top.monadicNames = ne.monadicNames ++ nes.monadicNames; + + --whether we need to wrap the ultimate function call in monadRewritten in a Return + local wrapReturn::Boolean = + --monadic args or monadic function + (!null(nes.monadTypesLocations) || (isMonad(substTy, top.env) && monadsMatch(substTy, top.expectedMonad, top.mUpSubst).fst)) && + --not monadic result or not the right monad + (!isMonad(ety.outputType, top.env) || !fst(monadsMatch(ety.outputType, top.expectedMonad, top.mUpSubst))); + + {- + Monad translation creates a lambda to apply to all the arguments + plus the function (to get fresh names for everything), then + creates a body that binds all the monadic arguments into the final + function application. + + For example, if we have + fun(a, b, c, d) + where a and d are monadic, then we translate into + (\a1 a2 a3 a4 f. a1 >>= (\a1. a4 >>= (\a4. f(a1, a2, a3, a4))))(a, b, c, d, fun) + Reusing ai in the bind for the ith argument simplifies doing the + application inside all the binds. + -} + local lambda_fun::Expr = buildMonadApplicationLambda(nes.realTypes, nes.monadTypesLocations, ety, wrapReturn, top.location); + local expanded_args::AppExprs = snocAppExprs(nes.monadRewritten, ',', presentAppExpr(ne.monadRewritten, location=top.location), + location=top.location); + local bind_name::String = "__bindFun_" ++ toString(genInt()); + -- fun >>= \ bind_name -> lambda_fun(args, bind_name) + local bind_fun_in::Expr = + Silver_Expr { + bind($Expr {if ne.mtyperep.isDecorated then mkStrFunctionInvocation(top.location, "silver:core:new", [ne.monadRewritten]) else ne.monadRewritten}, + $Expr {buildLambda(bind_name, monadInnerType(ne.mtyperep, top.location), applicationExpr(lambda_fun, '(', expanded_name_args, ')', location=top.location), top.location) }) + }; + local expanded_name_args::AppExprs = + snocAppExprs(nes.monadRewritten, ',', presentAppExpr(baseExpr(qNameId(name(bind_name, top.location), location=top.location), + location=top.location), location=top.location), location=top.location); + --haven't done monadRewritten on annotated ones, so ignore them + top.monadRewritten = + if isMonad(substTy, top.env) && monadsMatch(top.expectedMonad, substTy, top.mDownSubst).fst + then bind_fun_in + else if null(nes.monadTypesLocations) + then applicationExpr(ne.monadRewritten, '(', nes.monadRewritten, ')', location=top.location) + else applicationExpr(lambda_fun, '(', expanded_args, ')', location=top.location); +} + +aspect production functionInvocation +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + local t::Expr = application(e, '(', es, ',', anns, ')', location=top.location); + t.mDownSubst = top.mDownSubst; + t.env = top.env; + t.flowEnv = top.flowEnv; + t.config = top.config; + t.compiledGrammars = top.compiledGrammars; + t.grammarName = top.grammarName; + t.frame = top.frame; + t.finalSubst = top.finalSubst; + t.downSubst = top.downSubst; + t.isRoot = top.isRoot; + t.originRules = top.originRules; + t.expectedMonad = top.expectedMonad; + + t.monadicallyUsed = top.monadicallyUsed; + + top.merrors := t.merrors; + top.mUpSubst = t.mUpSubst; + top.mtyperep = t.mtyperep; + top.monadRewritten = t.monadRewritten; + + top.monadicNames = t.monadicNames; +} +--build the lambda to apply to all the original arguments plus the function +--we're going to assume this is only called if monadTysLocs is non-empty +function buildMonadApplicationLambda +Expr ::= realtys::[Type] monadTysLocs::[Pair] funType::Type wrapReturn::Boolean loc::Location +{ + local funargs::AppExprs = buildFunArgs(length(realtys), loc); + local params::ProductionRHS = buildMonadApplicationParams(realtys, 1, funType, loc); + local body::Expr = buildMonadApplicationBody(monadTysLocs, funargs, head(monadTysLocs).fst, wrapReturn, loc); + return lambdap(params, body, location=loc); +} +--build the parameters for the lambda applied to all the original arguments plus the function +function buildMonadApplicationParams +ProductionRHS ::= realtys::[Type] currentLoc::Integer funType::Type loc::Location +{ + return if null(realtys) + then productionRHSCons(productionRHSElem(name("f", loc), + '::', + typerepTypeExpr(funType, location=loc), + location=loc), + productionRHSNil(location=loc), + location=loc) + else productionRHSCons(productionRHSElem(name("a"++toString(currentLoc), loc), + '::', + typerepTypeExpr(dropDecorated(head(realtys)), location=loc), + --typerepTypeExpr(head(realtys), location=loc), + location=loc), + buildMonadApplicationParams(tail(realtys), currentLoc+1, funType, loc), + location=loc); +} +--build the arguments for the application inside all the binds +function buildFunArgs +AppExprs ::= currentIndex::Integer loc::Location +{ + return if currentIndex == 0 + then emptyAppExprs(location=loc) + else snocAppExprs(buildFunArgs(currentIndex - 1, loc), ',', + presentAppExpr(baseExpr(qName(loc, + "a"++toString(currentIndex)), + location=loc), + location=loc), location=loc); +} +--build the body of the lambda which includes all the binds +function buildMonadApplicationBody +Expr ::= monadTysLocs::[Pair] funargs::AppExprs monadType::Type wrapReturn::Boolean loc::Location +{ + local sub::Expr = buildMonadApplicationBody(tail(monadTysLocs), funargs, monadType, wrapReturn, loc); + local argty::Type = head(monadTysLocs).fst; + local bind::Expr = monadBind(loc); + local binding::ProductionRHS = + productionRHSCons(productionRHSElem(name("a"++toString(head(monadTysLocs).snd), + loc), + '::', + typerepTypeExpr(monadInnerType(argty, loc), + location=loc), + location=loc), + productionRHSNil(location=loc), + location=loc); + local bindargs::AppExprs = + snocAppExprs( + oneAppExprs(presentAppExpr( + baseExpr(qName(loc,"a"++toString(head(monadTysLocs).snd)), + location=loc), + location=loc), + location=loc), + ',', + presentAppExpr(lambdap(binding, sub, location=loc), + location=loc), + location=loc); + + local step::Expr = applicationExpr(bind, '(', bindargs, ')', location=loc); + + --the function is always going to be bound into the name "f", so we hard code that here + local baseapp::Expr = applicationExpr(baseExpr(qName(loc, "f"), location=loc), + '(', funargs, ')', location=loc); + local funapp::Expr = if wrapReturn + then Silver_Expr { $Expr {monadReturn(loc)}($Expr {baseapp}) } + else baseapp; + + return if null(monadTysLocs) + then funapp + else step; +} + + +aspect production partialApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.merrors := error("merrors not defined on partial applications"); + top.mUpSubst = error("mUpSubst not defined on partial applications"); + + top.monadicNames = error("monadicNames not defined on partial applications"); + + top.mtyperep = error("mtyperep not defined on partial applications, but sholud be in the future"); + top.monadRewritten = error("monadRewritten not defined on partial applications, but should be in the future"); +} + +aspect production errorApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.merrors := []; + + top.monadicNames = []; + + top.mUpSubst = top.mDownSubst; + top.mtyperep = errorType(); + top.monadRewritten = top; +} + +aspect production noteAttachment +top::Expr ::= 'attachNote' note::Expr 'on' e::Expr 'end' +{ + top.merrors := e.merrors; + + e.mDownSubst = top.mDownSubst; + top.mUpSubst = e.mUpSubst; + + top.mtyperep = e.mtyperep; + + e.monadicallyUsed = top.monadicallyUsed; + top.monadicNames = e.monadicNames; + + top.monadRewritten = noteAttachment('attachNote', note, 'on', e.monadRewritten, 'end', location=top.location); +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + local ne::Expr = e; + ne.downSubst = top.mDownSubst; + ne.mDownSubst = top.mDownSubst; + top.mUpSubst = ne.mUpSubst; + ne.finalSubst = top.finalSubst; + ne.expectedMonad = top.expectedMonad; + ne.frame = top.frame; + ne.grammarName = top.grammarName; + ne.compiledGrammars = top.compiledGrammars; + ne.config = top.config; + ne.env = top.env; + ne.flowEnv = top.flowEnv; + ne.originRules = top.originRules; + ne.isRoot = false; + ne.monadicallyUsed = false; --this needs to change when we decorated monadic trees + + --apparently there isn't a downSubst equation normally? + local res_e::Expr = e; + res_e.downSubst = top.downSubst; + res_e.finalSubst = top.finalSubst; + res_e.frame = top.frame; + res_e.grammarName = top.grammarName; + res_e.compiledGrammars = top.compiledGrammars; + res_e.config = top.config; + res_e.env = top.env; + res_e.flowEnv = top.flowEnv; + res_e.isRoot = false; + res_e.originRules = top.originRules; + top.notExplicitAttributes := res_e.notExplicitAttributes; + + top.merrors := ne.errors; + top.mtyperep = ne.mtyperep; + + top.monadicNames = ne.monadicNames; + top.monadRewritten = forwardAccess(ne.monadRewritten, '.', 'forward', location=top.location); +} + +aspect production errorAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + e.monadicallyUsed = false; --this needs to change when we decorate monadic trees + top.monadicNames = if top.monadicallyUsed + then [access(e, '.', q, location=top.location)] ++ e.monadicNames + else e.monadicNames; + + propagate mDownSubst, mUpSubst; + top.merrors := []; + top.merrors <- case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Attributes accessed in implicit equations must " ++ + "be either implicit or restricted; " ++ q.unparse ++ + " is neither")] + end; + + --Why do we rewrite here, in an error production? We can get here from the basic access + -- production based on normal typechecking failing even though our typechecking will + -- succeed, and we then need to be able to go back. + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + local isBothMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then isBothMonad + else isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then q.typerep + else monadOfType(top.expectedMonad, q.typerep) + else q.typerep; + + top.notExplicitAttributes <- e.notExplicitAttributes ++ + if q.found + then case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [pair(q.unparse, top.location)] + end + else []; +} + +aspect production annoAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + e.monadicallyUsed = false; --this needs to change when we decorate monadic trees + top.monadicNames = if top.monadicallyUsed + then [access(e, '.', q, location=top.location)] ++ e.monadicNames + else e.monadicNames; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + local isBothMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then isBothMonad + else isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then q.typerep + else monadOfType(top.expectedMonad, q.typerep) + else q.typerep; + + top.mUpSubst = top.mDownSubst; + top.merrors := []; + top.merrors <- case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Attributes accessed in implicit equations must " ++ + "be either implicit or restricted; " ++ q.unparse ++ + " is neither")] + end; + + top.notExplicitAttributes <- e.notExplicitAttributes ++ + if q.found + then case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [pair(q.unparse, top.location)] + end + else []; +} + +aspect production terminalAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + + top.merrors := e.merrors; + top.mUpSubst = top.mDownSubst; + + e.monadicallyUsed = false; --this needs to change when we decorate monadic trees + top.monadicNames = if top.monadicallyUsed + then [access(e, '.', q, location=top.location)] ++ e.monadicNames + else e.monadicNames; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then monadOfType(top.expectedMonad, baseType) + else baseType; + + local baseType::Type = + if q.name == "lexeme" || q.name == "filename" + then stringType() + else if q.name == "line" || q.name == "column" + then intType() + else if q.name == "location" + then nonterminalType("silver:core:Location", [], false) + else errorType(); +} + +aspect production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + e.monadicallyUsed = false; --this needs to change when we decorate monadic trees + top.monadicNames = if top.monadicallyUsed + then [access(e, '.', q, location=top.location)] ++ e.monadicNames + else e.monadicNames; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + local isBothMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then isBothMonad + else isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then q.typerep + else monadOfType(top.expectedMonad, q.typerep) + else q.typerep; + + top.mUpSubst = top.mDownSubst; + top.merrors := e.merrors; + top.merrors <- case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Attributes accessed in implicit equations must " ++ + "be either implicit or restricted; " ++ q.unparse ++ + " is neither")] + end; + + top.notExplicitAttributes <- e.notExplicitAttributes ++ + if q.found + then case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [pair(q.unparse, top.location)] + end + else []; +} + +aspect production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + e.monadicallyUsed = false; --this needs to change when we decorate monadic trees + top.monadicNames = if top.monadicallyUsed + then [access(e, '.', q, location=top.location)] ++ e.monadicNames + else e.monadicNames; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + local isBothMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then isBothMonad + else isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then q.typerep + else monadOfType(top.expectedMonad, q.typerep) + else q.typerep; + + top.mUpSubst = top.mDownSubst; + top.merrors := e.merrors; + top.merrors <- case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Attributes accessed in implicit equations must " ++ + "be either implicit or restricted; " ++ q.unparse ++ + " is neither")] + end; + + top.notExplicitAttributes <- e.notExplicitAttributes ++ + if q.found + then case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [pair(q.unparse, top.location)] + end + else []; +} + +aspect production errorDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + + top.monadicNames = []; + + --Why do we rewrite here, in an error production? We can get here from the basic access + -- production based on normal typechecking failing even though our typechecking will + -- succeed, and we then need to be able to go back. + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr{ silver:core:new($Expr {e.monadRewritten}) } + else e.monadRewritten; + local noMonad::Expr = access(e.monadRewritten, '.', q, location=top.location); + local isEMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + local isBothMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + (x.$QName {qName(q.location, q.name)}) + ) + ) + }; + top.monadRewritten = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then isBothMonad + else isEMonad + else noMonad; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then if isMonad(q.typerep, top.env) && + fst(monadsMatch(q.typerep, top.expectedMonad, top.mUpSubst)) + then q.typerep + else monadOfType(top.expectedMonad, q.typerep) + else q.typerep; + + top.merrors := e.merrors; + top.merrors <- case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Attributes accessed in implicit equations must " ++ + "be either implicit or restricted; " ++ q.unparse ++ + " is neither")] + end; + top.mUpSubst = top.mDownSubst; + + top.notExplicitAttributes <- e.notExplicitAttributes ++ + if q.found + then case q.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [pair(q.unparse, top.location)] + end + else []; +} + + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + {- + We assume no one is both using monadic stuff and explicitly decorating + monads, so anything that is a monad gets bound in to have its insides + decorated. + -} + propagate mDownSubst, mUpSubst; + top.merrors := e.merrors; + + e.monadicallyUsed = if isMonad(e.mtyperep, top.env) && monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst).fst + then true + else false; + top.monadicNames = e.monadicNames ++ inh.monadicNames; + + top.mtyperep = if isMonad(e.mtyperep, top.env) && monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst).fst + then monadOfType( + e.mtyperep, + decoratedType( + performSubstitution(monadInnerType(e.mtyperep, top.location), e.mUpSubst), + inhSetType(sort(nub(inh.suppliedInhs))))) + else decoratedType(performSubstitution(e.mtyperep, e.mUpSubst), inhSetType(sort(nub(inh.suppliedInhs)))); + + local newname::String = "__sv_bind_" ++ toString(genInt()); + local params::ProductionRHS = + productionRHSCons(productionRHSElem(name(newname, top.location), + '::', + typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location), + location=top.location), + productionRHSNil(location=top.location), + location=top.location); + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr {new($Expr {e.monadRewritten})} + else e.monadRewritten; + top.monadRewritten = + if isMonad(e.mtyperep, top.env) && monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst).fst + then Silver_Expr { + $Expr{monadBind(top.location)} + ($Expr{eUnDec}, + $Expr{lambdap(params, + Silver_Expr{ + $Expr{monadReturn(top.location)} + ($Expr{decorateExprWith('decorate', + baseExpr(qName(top.location, newname), location=top.location), + 'with', '{', inh.monadRewritten, '}', location=top.location)}) + }, location=top.location)}) + } + else decorateExprWith('decorate', e.monadRewritten, 'with', + '{', inh.monadRewritten, '}', location=top.location); +} + +attribute monadRewritten, merrors, mDownSubst, mUpSubst, monadicNames, expectedMonad occurs on ExprInhs; +attribute monadRewritten, merrors, mDownSubst, mUpSubst, monadicNames, expectedMonad occurs on ExprInh; + +propagate mDownSubst, mUpSubst, expectedMonad on ExprInhs, ExprInh; + +aspect production exprInhsEmpty +top::ExprInhs ::= +{ + top.merrors := []; + + top.monadicNames = []; + + top.monadRewritten = exprInhsEmpty(location=top.location); +} + +aspect production exprInhsOne +top::ExprInhs ::= lhs::ExprInh +{ + top.merrors := lhs.merrors; + + top.monadicNames = lhs.monadicNames; + + top.monadRewritten = exprInhsOne(lhs.monadRewritten, location=top.location); +} + +aspect production exprInhsCons +top::ExprInhs ::= lhs::ExprInh inh::ExprInhs +{ + top.merrors := lhs.merrors ++ inh.merrors; + + top.monadicNames = lhs.monadicNames ++ inh.monadicNames; + + top.monadRewritten = exprInhsCons(lhs.monadRewritten, inh.monadRewritten, location=top.location); +} + +aspect production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' +{ + top.merrors := e.merrors; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.monadRewritten = exprInh(lhs, '=', e.monadRewritten, ';', location=top.location); +} + + + + +aspect production trueConst +top::Expr ::= 'true' +{ + propagate mDownSubst, mUpSubst; + top.mtyperep = boolType(); + top.merrors := []; + top.monadicNames = []; + top.monadRewritten = trueConst('true', location=top.location); +} + +aspect production falseConst +top::Expr ::= 'false' +{ + propagate mDownSubst, mUpSubst; + top.mtyperep = boolType(); + top.merrors := []; + top.monadicNames = []; + top.monadRewritten = falseConst('false', location=top.location); +} + +aspect production and +top::Expr ::= e1::Expr '&&' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '&&', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '&&', not " ++ monadToString(e2.mtyperep))] + else []; + + local ec1::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e1.mtyperep, top.location), boolType()) + else check(e1.mtyperep, boolType()); + local ec2::TypeCheck = if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e2.mtyperep, top.location), boolType()) + else check(e2.mtyperep, boolType()); + ec1.finalSubst = top.finalSubst; + ec2.finalSubst = top.finalSubst; + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec1.downSubst = e2.mUpSubst; + ec2.downSubst = ec1.upSubst; + top.mUpSubst = ec2.upSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep --assume it will be well-typed + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then e2.mtyperep + else boolType(); + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> if x then y else Return(false))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + if x then y else $Expr {monadReturn(top.location)}(false)) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x && y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x && y))(_, $Expr {e2UnDec})) + }; + --if e1 then e2 else Return(false) + local bind2::Expr = + Silver_Expr { + if $Expr {e1UnDec} then $Expr {e2UnDec} else $Expr {monadReturn(top.location)}(false) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else and(e1.monadRewritten, '&&', e2.monadRewritten, location=top.location); +} + +aspect production or +top::Expr ::= e1::Expr '||' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '||', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '||', not " ++ monadToString(e2.mtyperep))] + else []; + + local ec1::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e1.mtyperep, top.location), boolType()) + else check(e1.mtyperep, boolType()); + local ec2::TypeCheck = if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e2.mtyperep, top.location), boolType()) + else check(e2.mtyperep, boolType()); + ec1.finalSubst = top.finalSubst; + ec2.finalSubst = top.finalSubst; + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec1.downSubst = e2.mUpSubst; + ec2.downSubst = ec1.upSubst; + top.mUpSubst = ec2.upSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep --assume it will be well-typed + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then e2.mtyperep + else boolType(); + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> if x then Return(true) else y)(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + if x then $Expr {monadReturn(top.location)}(true) else y) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x || y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x || y))(_, $Expr {e2UnDec})) + }; + --if e1 then Return(true) else e2 + local bind2::Expr = + Silver_Expr { + if $Expr {e1UnDec} then $Expr {monadReturn(top.location)}(true) else $Expr {e2UnDec} + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else or(e1.monadRewritten, '||', e2.monadRewritten, location=top.location); +} + +aspect production notOp +top::Expr ::= '!' e::Expr +{ + top.merrors := e.merrors; + top.merrors <- + if isMonad(e.mtyperep, top.env) && monadsMatch(top.expectedMonad, e.mtyperep, top.mDownSubst).fst + then if monadsMatch(top.expectedMonad, e.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '!', not " ++ monadToString(e.mtyperep))] + else []; + + local ec::TypeCheck = if isMonad(e.mtyperep, top.env) + then check(monadInnerType(e.mtyperep, top.location), boolType()) + else check(e.mtyperep, boolType()); + e.mDownSubst = top.mDownSubst; + ec.downSubst = e.mUpSubst; + top.mUpSubst = ec.upSubst; + ec.finalSubst = top.finalSubst; + top.mtyperep = e.mtyperep; --assume it will be well-typed + + e.monadicallyUsed = isMonad(e.mtyperep, top.env) && monadsMatch(top.expectedMonad, e.mtyperep, top.mDownSubst).fst; + top.monadicNames = e.monadicNames; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr {silver:core:new($Expr {e.monadRewritten})} + else e.monadRewritten; + top.monadRewritten = + if isMonad(e.mtyperep, top.env) && monadsMatch(top.expectedMonad, e.mtyperep, top.mDownSubst).fst + then Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + \x::Boolean -> + $Expr {monadReturn(top.location)}(!x)) + } + else notOp('!', e.monadRewritten, location=top.location); +} + +concrete production ifThen +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'end' --this is easier than anything else to do +{ + top.unparse = "if " ++ e1.unparse ++ " then " ++ e2.unparse ++ " end"; + top.merrors <- + if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this 'if-then', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonadFail(top.expectedMonad, top.env) + then [] + else [err(top.location, monadToString(top.expectedMonad) ++ + " is not an instance of MonadFail and cannot be used with if-then")]; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e1.mtyperep, top.location), boolType()) + else check(e1.mtyperep, boolType()); + ec.finalSubst = top.finalSubst; + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + e1.downSubst = top.downSubst; + e2.downSubst = e1.upSubst; + top.upSubst = e2.upSubst; + + top.mtyperep = if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then e2.mtyperep + else if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then monadOfType(e1.mtyperep, e2.mtyperep) + else monadOfType(top.expectedMonad, e2.mtyperep); + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = false; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + e1.expectedMonad = top.expectedMonad; + e2.expectedMonad = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else top.expectedMonad; + + forwards to ifThenElse('if', e1, 'then', e2, 'else', monadFail(top.location), location=top.location); +} + +aspect production ifThenElse +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr +{ + top.merrors := e1.merrors ++ e2.merrors ++ e3.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this 'if-then-els', not " ++ monadToString(e1.mtyperep))] + else []; + + local ec1::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then check(monadInnerType(e1.mtyperep, top.location), boolType()) + else check(e1.mtyperep, boolType()); + local ec2::TypeCheck = if isMonad(e3.mtyperep, top.env) && monadsMatch(top.expectedMonad, e3.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e3.mtyperep, e2.mtyperep) + else check(monadInnerType(e3.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e3.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e3.mtyperep, e2.mtyperep); + ec1.finalSubst = top.finalSubst; + ec2.finalSubst = top.finalSubst; + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + e3.mDownSubst = e2.mUpSubst; + ec1.downSubst = e3.mUpSubst; + ec2.downSubst = ec1.upSubst; + top.mUpSubst = ec2.upSubst; + + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then e2.mtyperep + else if isMonad(e3.mtyperep, top.env) && monadsMatch(top.expectedMonad, e3.mtyperep, top.mDownSubst).fst + then e3.mtyperep + else monadOfType(top.expectedMonad, e3.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then e2.mtyperep + else e3.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = false; + e3.monadicallyUsed = false; + top.monadicNames = e1.monadicNames ++ e2.monadicNames ++ e3.monadicNames; + + --To deal with the case where one type or the other might be "generic" (e.g. Maybe
), + -- we want to do substitution on the types before putting them into the monadRewritten + local e2Type::Type = performSubstitution(e2.mtyperep, top.finalSubst); + local e3Type::Type = performSubstitution(e3.mtyperep, top.finalSubst); + -- + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new($Expr {e1.monadRewritten})} + else e1.monadRewritten; + --We assume that if e2 or e3 are monads, they are the same as e1 if that is a + -- monad and we don't allow monads to become nested. + local cMonad::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\c::Boolean + x::$TypeExpr {typerepTypeExpr(dropDecorated(e2Type), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e3Type), location=top.location)} -> + --x::$TypeExpr {typerepTypeExpr(e2Type, location=top.location)} + --y::$TypeExpr {typerepTypeExpr(e3Type, location=top.location)} -> + if c + then $Expr { if isMonad(e2.mtyperep, top.env) + then Silver_Expr {x} + else Silver_Expr {$Expr {monadReturn(top.location)}(x)} } + else $Expr { if isMonad(e3.mtyperep, top.env) + then Silver_Expr {y} + else Silver_Expr {$Expr {monadReturn(top.location)}(y)} }) + (_, $Expr {e2.monadRewritten}, $Expr {e3.monadRewritten})) + }; + local cBool::Expr = + Silver_Expr { + if $Expr {e1.monadRewritten} + then $Expr {if isMonad(e2.mtyperep, top.env) + then e2.monadRewritten + else if isMonad(e3.mtyperep, top.env) + then Silver_Expr { $Expr {monadReturn(top.location)}($Expr {e2.monadRewritten}) } + else e2.monadRewritten} + else $Expr {if isMonad(e3.mtyperep, top.env) + then e3.monadRewritten + else if isMonad(e2.mtyperep, top.env) + then Silver_Expr { $Expr {monadReturn(top.location)}($Expr {e3.monadRewritten}) } + else e3.monadRewritten} + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) + then cMonad + else cBool; +} + +aspect production intConst +top::Expr ::= i::Int_t +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = intType(); + top.monadicNames = []; + top.monadRewritten = intConst(i, location=top.location); +} + +aspect production floatConst +top::Expr ::= f::Float_t +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = floatType(); + top.monadicNames = []; + top.monadRewritten = floatConst(f, location=top.location); +} + +aspect production plus +top::Expr ::= e1::Expr '+' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '+', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '+', not " ++ monadToString(e2.mtyperep))] + else []; + + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, e2.mtyperep) + else check(monadInnerType(e1.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e1.mtyperep, e2.mtyperep); + ec.finalSubst = top.mUpSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else e2.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> y >>= \z -> Return(x + z))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadBind(top.location)} + (y, + \z::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x + z))) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x + y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x + y))(_, $Expr {e2UnDec})) + }; + --e2 >>= ( (\x y -> Return(x + y))(e1, _) ) + local bind2::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e2UnDec}, + (\x::$TypeExpr {typerepTypeExpr(e1.mtyperep, location=top.location)} + y::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x + y))($Expr {e1UnDec}, _)) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else plus(e1.monadRewritten, '+', e2.monadRewritten, location=top.location); +} + +aspect production minus +top::Expr ::= e1::Expr '-' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '-', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '-', not " ++ monadToString(e2.mtyperep))] + else []; + + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, e2.mtyperep) + else check(monadInnerType(e1.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e1.mtyperep, e2.mtyperep); + ec.finalSubst = top.mUpSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else e2.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> y >>= \z -> Return(x - z))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadBind(top.location)} + (y, + \z::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x - z))) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x - y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x - y))(_, $Expr {e2UnDec})) + }; + --e2 >>= ( (\x y -> Return(x - y))(e1, _) ) + local bind2::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e2UnDec}, + (\x::$TypeExpr {typerepTypeExpr(e1.mtyperep, location=top.location)} + y::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x - y))($Expr {e1UnDec}, _)) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else minus(e1.monadRewritten, '-', e2.monadRewritten, location=top.location); +} + +aspect production multiply +top::Expr ::= e1::Expr '*' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '*', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '*', not " ++ monadToString(e2.mtyperep))] + else []; + + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, e2.mtyperep) + else check(monadInnerType(e1.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e1.mtyperep, e2.mtyperep); + ec.finalSubst = top.mUpSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else e2.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> y >>= \z -> Return(x * z))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadBind(top.location)} + (y, + \z::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x * z))) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x * y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x * y))(_, $Expr {e2UnDec})) + }; + --e2 >>= ( (\x y -> Return(x * y))(e1, _) ) + local bind2::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e2UnDec}, + (\x::$TypeExpr {typerepTypeExpr(e1.mtyperep, location=top.location)} + y::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x * y))($Expr {e1UnDec}, _)) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else multiply(e1.monadRewritten, '*', e2.monadRewritten, location=top.location); +} + +aspect production divide +top::Expr ::= e1::Expr '/' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '/', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '/', not " ++ monadToString(e2.mtyperep))] + else []; + + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, e2.mtyperep) + else check(monadInnerType(e1.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e1.mtyperep, e2.mtyperep); + ec.finalSubst = top.mUpSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else e2.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env); + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env); + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> y >>= \z -> Return(x / z))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadBind(top.location)} + (y, + \z::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x / z))) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x / y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x / y))(_, $Expr {e2UnDec})) + }; + --e2 >>= ( (\x y -> Return(x / y))(e1, _) ) + local bind2::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e2UnDec}, + (\x::$TypeExpr {typerepTypeExpr(e1.mtyperep, location=top.location)} + y::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x / y))($Expr {e1UnDec}, _)) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else divide(e1.monadRewritten, '/', e2.monadRewritten, location=top.location); +} + +aspect production modulus +top::Expr ::= e1::Expr '%' e2::Expr +{ + top.merrors := e1.merrors ++ e2.merrors; + top.merrors <- + if isMonad(e1.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '%', not " ++ monadToString(e1.mtyperep))] + else []; + top.merrors <- + if isMonad(e2.mtyperep, top.env) + then if monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then [] + else [err(top.location, "Can only use " ++ monadToString(top.expectedMonad) ++ + " implicitly in this '%', not " ++ monadToString(e2.mtyperep))] + else []; + + e1.mDownSubst = top.mDownSubst; + e2.mDownSubst = e1.mUpSubst; + ec.downSubst = e2.mUpSubst; + top.mUpSubst = ec.upSubst; + + local ec::TypeCheck = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, e2.mtyperep) + else check(monadInnerType(e1.mtyperep, top.location), e2.mtyperep) + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then check(e1.mtyperep, monadInnerType(e2.mtyperep, top.location)) + else check(e1.mtyperep, e2.mtyperep); + ec.finalSubst = top.mUpSubst; + top.mtyperep = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then e1.mtyperep + else e2.mtyperep; + + e1.monadicallyUsed = isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst; + e2.monadicallyUsed = isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst; + top.monadicNames = e1.monadicNames ++ e2.monadicNames; + + local e1UnDec::Expr = + if e1.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e1.monadRewritten})} + else e1.monadRewritten; + local e2UnDec::Expr = + if e2.mtyperep.isDecorated + then Silver_Expr {silver:core:new( $Expr {e2.monadRewritten})} + else e2.monadRewritten; + --e1 >>= ( (\x y -> y >>= \z -> Return(x % z))(_, e2) ) + local bindBoth::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadBind(top.location)} + (y, + \z::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x % z))) (_, $Expr {e2UnDec})) + }; + --e1 >>= ( (\x y -> Return(x % y))(_, e2) ) + local bind1::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e1UnDec}, + (\x::$TypeExpr {typerepTypeExpr(monadInnerType(e1.mtyperep, top.location), location=top.location)} + y::$TypeExpr {typerepTypeExpr(dropDecorated(e2.mtyperep), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x % y))(_, $Expr {e2UnDec})) + }; + --e2 >>= ( (\x y -> Return(x % y))(e1, _) ) + local bind2::Expr = + Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {e2UnDec}, + (\x::$TypeExpr {typerepTypeExpr(e1.mtyperep, location=top.location)} + y::$TypeExpr {typerepTypeExpr(monadInnerType(e2.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)} + (x % y))($Expr {e1UnDec}, _)) + }; + top.monadRewritten = if isMonad(e1.mtyperep, top.env) && monadsMatch(top.expectedMonad, e1.mtyperep, top.mDownSubst).fst + then if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bindBoth + else bind1 + else if isMonad(e2.mtyperep, top.env) && monadsMatch(top.expectedMonad, e2.mtyperep, top.mDownSubst).fst + then bind2 + else modulus(e1.monadRewritten, '%', e2.monadRewritten, location=top.location); +} + +aspect production neg +top::Expr ::= '-' e::Expr +{ + top.merrors := e.merrors; + + top.mtyperep = e.mtyperep; + + e.monadicallyUsed = isMonad(e.mtyperep, top.env) && monadsMatch(top.expectedMonad, e.mtyperep, top.mDownSubst).fst; + top.monadicNames = e.monadicNames; + + propagate mDownSubst, mUpSubst; + + local eUnDec::Expr = + if e.mtyperep.isDecorated + then Silver_Expr {silver:core:new($Expr {e.monadRewritten})} + else e.monadRewritten; + top.monadRewritten = + if isMonad(e.mtyperep, top.env) + then Silver_Expr { + $Expr {monadBind(top.location)} + ($Expr {eUnDec}, + \x::$TypeExpr {typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location)} -> + $Expr {monadReturn(top.location)}(-x)) + } + else neg('-', e.monadRewritten, location=top.location); +} + +aspect production terminalConstructor +top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' +{ + es.mDownSubst = top.mDownSubst; + el.mDownSubst = es.mUpSubst; + top.merrors := es.merrors ++ el.merrors; + top.mUpSubst = el.mUpSubst; + top.mtyperep = + if ( isMonad(es.mtyperep, top.env) && monadsMatch(es.mtyperep, top.expectedMonad, top.mUpSubst).fst ) || + ( isMonad(el.mtyperep, top.env) && monadsMatch(el.mtyperep, top.expectedMonad, top.mUpSubst).fst ) + then monadOfType(top.expectedMonad, t.typerep) + else t.typerep; + top.monadicNames = []; + + local bind::Expr = monadBind(top.location); + local ret::Expr = monadReturn(top.location); + local esty::TypeExpr = + typerepTypeExpr(if isMonad(es.mtyperep, top.env) then es.mtyperep + else monadInnerType(es.mtyperep, top.location), location=top.location); + local elty::TypeExpr = + typerepTypeExpr(if isMonad(es.mtyperep, top.env) then es.mtyperep + else monadInnerType(es.mtyperep, top.location), location=top.location); + local bindes::Expr = + Silver_Expr { + $Expr {bind} + ($Expr {es.monadRewritten}, + (\x::$TypeExpr {esty} + y::$TypeExpr {elty} -> + $Expr {ret} + (terminal($TypeExpr {t}, x, y))) (_, $Expr {el.monadRewritten})) + }; + local bindel::Expr = + Silver_Expr { + $Expr {bind} + ($Expr {el.monadRewritten}, + (\x::$TypeExpr {esty} + y::$TypeExpr {elty} -> + $Expr {ret} + (terminal($TypeExpr {t}, x, y))) ($Expr {es.monadRewritten}, _)) + }; + local bindBoth::Expr = + Silver_Expr { + $Expr {bind} + ($Expr {es.monadRewritten}, + (\x::$TypeExpr {elty} + y::$TypeExpr {typerepTypeExpr(es.mtyperep, location=top.location)} -> + $Expr {bind} + (y, + \z::$TypeExpr {elty} -> + $Expr {ret} + (terminal($TypeExpr {t}, x, z))) (_, $Expr {el.monadRewritten}))) + }; + top.monadRewritten = + if isMonad(es.mtyperep, top.env) && monadsMatch(es.mtyperep, top.expectedMonad, top.mUpSubst).fst + then if isMonad(el.mtyperep, top.env) && monadsMatch(el.mtyperep, top.expectedMonad, top.mUpSubst).fst + then bindBoth + else bindes + else if isMonad(el.mtyperep, top.env) && monadsMatch(el.mtyperep, top.expectedMonad, top.mUpSubst).fst + then bindel + else top; +} + +aspect production stringConst +top::Expr ::= s::String_t +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = stringType(); + top.monadicNames = []; + + top.monadRewritten = stringConst(s, location=top.location); +} + + +--A list of the locations where arguments are monads used implicitly +synthesized attribute monadTypesLocations::[Pair] occurs on AppExpr, AppExprs; +--A list of the actual types of arguments +synthesized attribute realTypes::[Type] occurs on AppExpr, AppExprs; +--The only monad banned from being used as an actual argument +attribute expectedMonad occurs on AppExpr, AppExprs; +propagate expectedMonad on AppExpr, AppExprs; +--Whether we're in a special case where monad arguments are allowed, despite the normal prohibition +autocopy attribute monadArgumentsAllowed::Boolean occurs on AppExpr, AppExprs; + +attribute monadRewritten, merrors, mDownSubst, mUpSubst occurs on AppExpr; +attribute monadRewritten, merrors, mDownSubst, mUpSubst occurs on AppExprs; + +aspect production missingAppExpr +top::AppExpr ::= '_' +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.monadRewritten = missingAppExpr('_', location=top.location); + top.realTypes = []; + top.monadTypesLocations = []; + top.monadicNames = []; +} +aspect production presentAppExpr +top::AppExpr ::= e::Expr +{ + top.merrors := e.merrors; + + top.realTypes = [e.mtyperep]; + top.monadTypesLocations = if isMonadic + then [pair(e.mtyperep, top.appExprIndex+1)] + else []; + e.monadicallyUsed = isMonadic; + top.monadicNames = e.monadicNames; + + --these have an 'a' at the end of their names because of a bug where local names are not local to their grammars + local attribute errCheck1a::TypeCheck; errCheck1a.finalSubst = top.mUpSubst; + local attribute errCheck2a::TypeCheck; errCheck2a.finalSubst = top.mUpSubst; + + e.mDownSubst = top.mDownSubst; + errCheck1a.downSubst = e.mUpSubst; + errCheck2a.downSubst = e.mUpSubst; + top.mUpSubst = if isMonadic + then errCheck2a.upSubst + else errCheck1a.upSubst; + --determine whether it appears that this is supposed to take + -- advantage of implicit monads based on types matching the + -- expected and being monads + local isMonadic::Boolean = + isMonad(e.mtyperep, top.env) && + fst(monadsMatch(e.mtyperep, top.expectedMonad, e.mUpSubst)) && + !fst(monadsMatch(e.mtyperep, top.appExprTyperep, e.mUpSubst)); + + errCheck1a = check(if top.appExprTyperep.isDecorated then e.mtyperep else dropDecorated(e.mtyperep), top.appExprTyperep); + errCheck2a = check(monadInnerType(e.mtyperep, top.location), top.appExprTyperep); + top.merrors <- + if isMonadic + then if !errCheck2a.typeerror + then [] + else [err(top.location, "Argument " ++ toString(top.appExprIndex+1) ++ " of function '" ++ + top.appExprApplied ++ "' expected " ++ errCheck1a.rightpp ++ + " or a monad of " ++ errCheck1a.rightpp ++ + " but argument is of type " ++ errCheck1a.leftpp)] + else + if !errCheck1a.typeerror + then [] + else [err(top.location, "Argument " ++ toString(top.appExprIndex+1) ++ " of function '" ++ + top.appExprApplied ++ "' expected " ++ errCheck1a.rightpp ++ + " or a monad of " ++ errCheck1a.rightpp ++ + " but argument is of type " ++ errCheck1a.leftpp)]; + --Functions are not allowed to take monad-typed arguments + top.merrors <- + if fst(monadsMatch(top.appExprTyperep, top.expectedMonad, top.mDownSubst)) && !top.monadArgumentsAllowed + then [err(top.location, "Implicit equations may not use functions with " ++ + "monad-typed arguments, specifically " ++ errCheck2a.rightpp)] + else []; + + top.monadRewritten = presentAppExpr(e.monadRewritten, location=top.location); +} + +propagate mDownSubst, mUpSubst on AppExprs; + +aspect production snocAppExprs +top::AppExprs ::= es::AppExprs ',' e::AppExpr +{ + top.merrors := es.merrors ++ e.merrors; + + top.realTypes = es.realTypes ++ e.realTypes; + + top.monadTypesLocations = es.monadTypesLocations ++ e.monadTypesLocations; + + top.monadicNames = es.monadicNames ++ e.monadicNames; + + top.monadRewritten = snocAppExprs(es.monadRewritten, ',', e.monadRewritten, location=top.location); +} +aspect production oneAppExprs +top::AppExprs ::= e::AppExpr +{ + top.merrors := e.merrors; + + top.realTypes = e.realTypes; + + top.monadTypesLocations = e.monadTypesLocations; + + top.monadicNames = e.monadicNames; + + top.monadRewritten = oneAppExprs(e.monadRewritten, location=top.location); +} +aspect production emptyAppExprs +top::AppExprs ::= +{ + top.merrors := []; + + top.realTypes = []; + + top.monadTypesLocations = []; + + top.monadicNames = []; + + top.monadRewritten = emptyAppExprs(location=top.location); +} + + +aspect production exprRef +top::Expr ::= e::PartiallyDecorated Expr +{ + e.mDownSubst = top.mDownSubst; + e.expectedMonad = top.expectedMonad; + e.monadicallyUsed = top.monadicallyUsed; + + top.merrors := e.merrors; + top.mUpSubst = e.mUpSubst; + top.mtyperep = e.mtyperep; + top.monadicNames = e.monadicNames; + top.monadRewritten = e.monadRewritten; +} + + + + +--Copper Expressions +aspect production failureTerminalIdExpr +top::Expr ::= 'disambiguationFailure' +{ + top.mUpSubst = top.mDownSubst; + top.mtyperep = terminalIdType(); + top.monadRewritten = top; + + top.monadicNames = []; +} + + +aspect production lexerClassReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.mUpSubst = top.mDownSubst; + top.mtyperep = q.lookupValue.typeScheme.typerep; + top.monadRewritten = top; + + top.monadicNames = []; +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/Lambda.sv b/grammars/silver/compiler/extension/implicit_monads/Lambda.sv new file mode 100644 index 000000000..6fca47f21 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Lambda.sv @@ -0,0 +1,29 @@ +grammar silver:compiler:extension:implicit_monads; + +aspect production lambdap +top::Expr ::= params::ProductionRHS e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + top.mtyperep = appTypes(functionType(length(params.inputElements), []), map((.typerep), params.inputElements) ++ [e.typerep]); + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.monadRewritten = lambdap(params, e.monadRewritten, location=top.location); +} + + + +aspect production lambdaParamReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} diff --git a/grammars/silver/compiler/extension/implicit_monads/Let.sv b/grammars/silver/compiler/extension/implicit_monads/Let.sv new file mode 100644 index 000000000..975715858 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Let.sv @@ -0,0 +1,146 @@ +grammar silver:compiler:extension:implicit_monads; + + + +aspect production letp +top::Expr ::= la::AssignExpr e::Expr +{ + top.merrors := la.merrors ++ ne.merrors; + + --We needed to provide our own environment. + local ne::Expr = e; + ne.config = top.config; + ne.grammarName = top.grammarName; + ne.compiledGrammars = top.compiledGrammars; + ne.flowEnv = top.flowEnv; + ne.frame = top.frame; + ne.mDownSubst = la.mUpSubst; + ne.downSubst = la.mUpSubst; + ne.finalSubst = top.mUpSubst; + ne.env = newScopeEnv(la.mdefs, top.env); + ne.expectedMonad = top.expectedMonad; + ne.originRules = top.originRules; + ne.isRoot = top.isRoot; + + la.mDownSubst = top.mDownSubst; + top.mUpSubst = ne.mUpSubst; + + top.mtyperep = if null(la.bindInList) || fst(monadsMatch(ne.mtyperep, top.expectedMonad, top.mUpSubst)) + then ne.mtyperep + else monadOfType(top.expectedMonad, ne.mtyperep); + + --I'm not entirely sure if this should be false. It might be that it should + --be based on top.monadicallyUsed and whether other things become binds or + --something. + ne.monadicallyUsed = false; + top.monadicNames = la.monadicNames ++ ne.monadicNames; + + local mreturn::Expr = monadReturn(top.location); + local mbind::Expr = monadBind(top.location); + + {- + Our rewriting here binds in anything after the let to keep names from + interfering with each other. For example, if we have + let a::Ta = ea; b::Tb = eb; c::Tc = ec in d + where a and c are monadic, we rewrite to + let a::M = ea; b::Tb = eb; c::M = ec in a >>= \a::Ta. c >>= \c::Tc. d + This ensures our names do not interfere with prevous ones. For example, ec + might reference an a that existed before the let, so we need to bind all the + expressions to names at once in a let; after that, we are free to use the + names to create binds. + -} + top.monadRewritten = + letp(la.fixedAssigns, + boundIn, + location=top.location); + local inside::Expr = if isMonad(ne.mtyperep, top.env) || null(la.bindInList) + then ne.monadRewritten + else Silver_Expr { $Expr{mreturn}($Expr{ne.monadRewritten}) }; + local boundIn::Expr = + foldr(\x::Pair y::Expr -> + buildApplication(mbind, + [baseExpr(qName(top.location, x.fst.name), location=top.location), + buildLambda(x.fst.name, + decorate x.snd with + {env=top.env; grammarName=top.grammarName; config=top.config; flowEnv=top.flowEnv;}.typerep, + y, top.location)], top.location), + inside, la.bindInList); +} + + +synthesized attribute fixedAssigns::AssignExpr occurs on AssignExpr; +synthesized attribute bindInList::[Pair] occurs on AssignExpr; +--definitions, but ones that won't cause errors with monad type mismatches in let definitions +synthesized attribute mdefs::[Def] occurs on AssignExpr; + +attribute merrors, mDownSubst, mUpSubst, monadicNames, expectedMonad occurs on AssignExpr; +propagate expectedMonad on AssignExpr; + +aspect production appendAssignExpr +top::AssignExpr ::= a1::AssignExpr a2::AssignExpr +{ + top.merrors := a1.merrors ++ a2.merrors; + + propagate mDownSubst, mUpSubst; + + top.monadicNames = a1.monadicNames ++ a2.monadicNames; + + top.mdefs = a1.mdefs ++ a2.mdefs; + + top.bindInList = a1.bindInList ++ a2.bindInList; + + top.fixedAssigns = appendAssignExpr(a1.fixedAssigns, a2.fixedAssigns, location=top.location); +} + +aspect production assignExpr +top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr +{ + top.merrors := e.merrors; + top.merrors <- if isMonad(t.typerep, top.env) && fst(monadsMatch(top.expectedMonad, t.typerep, top.mDownSubst)) + then [err(top.location, "Let bindings may not use a monad type")] + else []; + local errCheck::TypeCheck = if isMonad(e.mtyperep, top.env) && fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst)) + then check(t.typerep, monadInnerType(e.mtyperep, top.location)) + else check(t.typerep, e.mtyperep); + e.mDownSubst = top.mDownSubst; + errCheck.downSubst = e.mUpSubst; + top.mUpSubst = errCheck.upSubst; + + --I'm not entirely sure about this vs. false--it should only matter if we are + --directly redefining a name (doing x::T=y), which would be weird for a person + --to write (?), and redfining it monadically. This would happen if the person + --was putting in a let after let insertion failed, but then this should be the + --only place where the name occurs, so it wouldn't affect anything then either. + e.monadicallyUsed = isMonad(e.mtyperep, top.env) && fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst)) && !isMonad(t.typerep, top.env); + top.monadicNames = e.monadicNames; + + top.mdefs = [lexicalLocalDef(top.grammarName, id.location, fName, + performSubstitution(t.typerep, top.mUpSubst), + e.flowVertexInfo, e.flowDeps)]; + + top.bindInList = if isMonad(e.mtyperep, top.env) && fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + then [pair(id, t)] + else []; + + top.fixedAssigns = if isMonad(e.mtyperep, top.env) && fst(monadsMatch(e.mtyperep, top.expectedMonad, top.mUpSubst)) + --use t.typerep to get typechecking when we create the ultimate monadRewritten + then assignExpr(id, '::', typerepTypeExpr(monadOfType(top.expectedMonad, t.typerep), + location=top.location), + '=', e.monadRewritten, location=top.location) + else assignExpr(id, '::', t, '=', e.monadRewritten, location=top.location); +} + + + + +aspect production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName fi::ExprVertexInfo fd::[FlowVertex] +{ + top.merrors := []; + propagate mDownSubst, mUpSubst; + top.mtyperep = q.lookupValue.typeScheme.monoType; + top.monadicNames = if top.monadicallyUsed + then [baseExpr(new(q), location=top.location)] + else []; + top.monadRewritten = baseExpr(new(q), location=top.location); +} diff --git a/grammars/silver/compiler/extension/implicit_monads/PatternTypes.sv b/grammars/silver/compiler/extension/implicit_monads/PatternTypes.sv new file mode 100644 index 000000000..a8bbd0c76 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/PatternTypes.sv @@ -0,0 +1,74 @@ +grammar silver:compiler:extension:implicit_monads; + +aspect production prodAppPattern +top::Pattern ::= prod::QName '(' ps::PatternList ')' +{ + -- TODO: is this right? Seems like we should unify with ps pattern types? + top.patternType = prod.lookupValue.typeScheme.typerep.outputType; +} + +aspect production prodAppPattern_named +top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' +{ + top.patternType = prod.lookupValue.typeScheme.typerep.outputType; +} + +aspect production wildcPattern +top::Pattern ::= '_' +{ + top.patternType = freshType(); +} + +aspect production varPattern +top::Pattern ::= v::Name +{ + top.patternType = freshType(); +} + +aspect production errorPattern +top::Pattern ::= msg::[Message] +{ + top.patternType = errorType(); +} + +aspect production intPattern +top::Pattern ::= num::Int_t +{ + top.patternType = intType(); +} + +aspect production fltPattern +top::Pattern ::= num::Float_t +{ + top.patternType = floatType(); +} + +aspect production strPattern +top::Pattern ::= str::String_t +{ + top.patternType = stringType(); +} + +aspect production truePattern +top::Pattern ::= 'true' +{ + top.patternType = boolType(); +} + +aspect production falsePattern +top::Pattern ::= 'false' +{ + top.patternType = boolType(); +} + +aspect production nilListPattern +top::Pattern ::= '[' ']' +{ + top.patternType = listType(freshType()); +} + +aspect production consListPattern +top::Pattern ::= hp::Pattern '::' tp::Pattern +{ + top.patternType = listType(hp.patternType); +} diff --git a/grammars/silver/compiler/extension/implicit_monads/PrimitiveMatch.sv b/grammars/silver/compiler/extension/implicit_monads/PrimitiveMatch.sv new file mode 100644 index 000000000..aa0076065 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/PrimitiveMatch.sv @@ -0,0 +1,421 @@ +grammar silver:compiler:extension:implicit_monads; + +attribute mtyperep, merrors, patternType, monadRewritten, + mDownSubst, mUpSubst, monadicNames, expectedMonad, + returnFun, returnify occurs on PrimPatterns; +attribute mtyperep, merrors, patternType, monadRewritten, + mDownSubst, mUpSubst, monadicNames, expectedMonad, + returnFun, returnify occurs on PrimPattern; +propagate expectedMonad on PrimPatterns, PrimPattern; + +--returnFun is the monad's defined Return for returnify +inherited attribute returnFun::Expr; +synthesized attribute returnify::a; +--type matched by patterns; provided by Case.sv +--synthesized attribute patternType::Type; + + +aspect production matchPrimitiveConcrete +top::Expr ::= 'match' e::Expr 'return' t::TypeExpr 'with' pr::PrimPatterns 'else' arr::Arrow_kwd f::Expr 'end' +{ +} +aspect production matchPrimitive +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ +} + +aspect production matchPrimitiveReal +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + --if e is the implicit monad + local eIsMonadic::Boolean = isMonad(e.mtyperep, top.env) && monadsMatch(e.mtyperep, top.expectedMonad, top.mDownSubst).fst; + --if the pattern type is the implicit monad + local prPattIsMonadic::Boolean = isMonad(pr.patternType, top.env) && monadsMatch(pr.patternType, top.expectedMonad, top.mDownSubst).fst; + --the return type of the patterns is the implicit monad + local prRetIsMonadic::Boolean = isMonad(pr.mtyperep, top.env) && monadsMatch(pr.mtyperep, top.expectedMonad, top.mDownSubst).fst; + --the type of f is the implicit monad + local fIsMonadic::Boolean = isMonad(f.mtyperep, top.env) && monadsMatch(f.mtyperep, top.expectedMonad, top.mDownSubst).fst; + --the type t is the implicit monad + local tIsMonadic::Boolean = isMonad(t.typerep, top.env) && monadsMatch(t.typerep, top.expectedMonad, top.mDownSubst).fst; + + top.mtyperep = if eIsMonadic + then if tIsMonadic + then t.typerep + else monadOfType(t.typerep, top.expectedMonad) + else if prRetIsMonadic + then pr.mtyperep + else if tIsMonadic + then t.typerep + else f.mtyperep; + + top.merrors := e.merrors ++ pr.merrors ++ f.merrors; + top.merrors <- if prPattIsMonadic + then [err(top.location, "Cannot match on implicit monadic type " ++ prettyType(pr.patternType))] + else []; + + e.mDownSubst = top.mDownSubst; + pr.mDownSubst = e.mUpSubst; + f.mDownSubst = pr.mUpSubst; + top.mUpSubst = f.mUpSubst; + + e.monadicallyUsed = eIsMonadic; + f.monadicallyUsed = false; + top.monadicNames = e.monadicNames ++ pr.monadicNames ++ f.monadicNames; + + local freshname::String = "__sv_bindingInAMatchExpression_" ++ toString(genInt()); + local eBind::Expr = monadBind(top.location); + local eInnerType::TypeExpr = typerepTypeExpr(monadInnerType(e.mtyperep, top.location), location=top.location); + local binde_lambdaparams::ProductionRHS = + productionRHSCons(productionRHSElem(name(freshname, top.location), '::', + eInnerType, location=top.location), + productionRHSNil(location=top.location), location=top.location); + --Since we sometimes need to just use pure() over the top of everything to get a + -- monad out, we use a fresh type rather than the top.mtyperep + local outty::TypeExpr = typerepTypeExpr(freshType(), location=top.location); + + {-We need to make sure that, if we are matching on a decorable type, + it is decorated. We need to check both whether the type is + decorable, or, in case that is a variable because we do as little + typechecking as possible, whether the pattern type is decorable + (and to avoid double-decoration, we check it isn't decorated + already).-} + local eMTyDecorable::Boolean = + if eIsMonadic + then isDecorable(performSubstitution(monadInnerType(e.mtyperep, top.location), e.mUpSubst), top.env) || + (!performSubstitution(monadInnerType(e.mtyperep, top.location), e.mUpSubst).isDecorated && isDecorable(pr.patternType, top.env)) + else isDecorable(performSubstitution(e.mtyperep, e.mUpSubst), top.env) || + (!performSubstitution(e.mtyperep, e.mUpSubst).isDecorated && isDecorable(pr.patternType, top.env)); + local decName::Expr = + if eMTyDecorable + then decorateExprWithEmpty('decorate', baseExpr(qName(top.location, freshname), location=top.location), + 'with', '{', '}', location=top.location) + else baseExpr(qName(top.location, freshname), location=top.location); + local decE::Expr = + if eMTyDecorable + then decorateExprWithEmpty('decorate', e.monadRewritten, 'with', '{', '}', location=top.location) + else e.monadRewritten; + + --bind e, just do the rest + local justBind_e::Expr = + buildApplication(eBind, + [e.monadRewritten, lambdap(binde_lambdaparams, + matchPrimitiveReal(decName, + outty, pr.monadRewritten, f.monadRewritten, + location=top.location), + location=top.location)], + top.location); + --bind e, return f based on e's type + local bind_e_return_f::Expr = + buildApplication(eBind, + [e.monadRewritten, lambdap(binde_lambdaparams, + matchPrimitiveReal(decName, + outty, pr.monadRewritten, + buildApplication(monadReturn(top.location), + [f.monadRewritten], top.location), + location=top.location), + location=top.location)], + top.location); + --bind e, returnify pr based on e's type + local prReturnify::PrimPatterns = pr.monadRewritten; + prReturnify.returnFun = monadReturn(top.location); + prReturnify.grammarName = top.grammarName; + prReturnify.env = top.env; + prReturnify.config = top.config; + local bind_e_returnify_pr::Expr = + buildApplication(eBind, + [e.monadRewritten, lambdap(binde_lambdaparams, + matchPrimitiveReal(decName, + outty, prReturnify.returnify, + f.monadRewritten, location=top.location), + location=top.location)], + top.location); + --bind e, returnify pr, return f based on e's type + local bind_e_returnify_pr_return_f::Expr = + buildApplication(eBind, + [e.monadRewritten, lambdap(binde_lambdaparams, + matchPrimitiveReal(decName, + outty, prReturnify.returnify, + buildApplication(monadReturn(top.location), + [f.monadRewritten], top.location), + location=top.location), + location=top.location)], + top.location); + --return f from pr's return type + local return_f::Expr = + matchPrimitiveReal(decE, outty, pr.monadRewritten, + buildApplication(monadReturn(top.location), [f.monadRewritten], top.location), + location=top.location); + --returnify pr from f's type + local ret_pr_from_f::PrimPatterns = pr.monadRewritten; + ret_pr_from_f.returnFun = monadReturn(top.location); + ret_pr_from_f.grammarName = top.grammarName; + ret_pr_from_f.env = top.env; + ret_pr_from_f.config = top.config; + local returnify_pr::Expr = matchPrimitiveReal(decE, outty, ret_pr_from_f.returnify, + f.monadRewritten, location=top.location); + --just use monadRewritten + local just_rewrite::Expr = matchPrimitiveReal(decE, outty, pr.monadRewritten, + f.monadRewritten, location=top.location); + --t is monadic and nothing else is, so return over whole thing + local return_whole_thing::Expr = + buildApplication(monadReturn(top.location), + [matchPrimitiveReal(decE, outty, pr.monadRewritten, + f.monadRewritten, location=top.location)], + top.location); + --pick the right rewriting + local mRw::Expr = + if eIsMonadic + then if prRetIsMonadic + then if fIsMonadic + then justBind_e + else bind_e_return_f + else if fIsMonadic + then bind_e_returnify_pr + else bind_e_returnify_pr_return_f + else if prRetIsMonadic + then if fIsMonadic + then just_rewrite + else return_f + else if fIsMonadic + then returnify_pr + else if tIsMonadic + then return_whole_thing + else if tIsMonadic --and nothing else is, so we need to add return + then Silver_Expr {$Expr {monadReturn(top.location)}($Expr {just_rewrite})} + else just_rewrite; + top.monadRewritten = mRw; +} + +aspect production onePattern +top::PrimPatterns ::= p::PrimPattern +{ + top.merrors := p.merrors; + + propagate mDownSubst, mUpSubst; + + top.mtyperep = p.mtyperep; + top.patternType = p.patternType; + + top.monadicNames = p.monadicNames; + + p.returnFun = top.returnFun; + top.returnify = onePattern(p.returnify, location=top.location); + top.monadRewritten = onePattern(p.monadRewritten, location=top.location); +} +aspect production consPattern +top::PrimPatterns ::= p::PrimPattern vbar::Vbar_kwd ps::PrimPatterns +{ + top.merrors := p.merrors ++ ps.merrors; + + top.monadicNames = p.monadicNames ++ ps.monadicNames; + + p.mDownSubst = top.mDownSubst; + ps.mDownSubst = p.mUpSubst; + top.mUpSubst = ps.mUpSubst; + + top.mtyperep = if isMonad(p.mtyperep, top.env) && monadsMatch(p.mtyperep, top.expectedMonad, top.mDownSubst).fst + then if isMonad(ps.mtyperep, top.env) && monadsMatch(ps.mtyperep, top.expectedMonad, top.mDownSubst).fst + then ps.mtyperep + else p.mtyperep + else ps.mtyperep; + top.patternType = p.patternType; --go with the "earlier" type--mismatch handled by merrors + + p.returnFun = top.returnFun; + ps.returnFun = top.returnFun; + top.returnify = consPattern(p.returnify, terminal(Vbar_kwd, "|"), ps.returnify, location=top.location); + + --when both are monads or both aren't, so we don't need to change anything + local basicRewritten::PrimPatterns = consPattern(p.monadRewritten, terminal(Vbar_kwd, "|"), ps.monadRewritten, + location=top.location); + --when the current clause is a monad but the rest aren't, wrap all of them in Return() + local psReturnify::PrimPatterns = ps.monadRewritten; + psReturnify.returnFun = monadReturn(top.location); + psReturnify.env = top.env; + psReturnify.config = top.config; + psReturnify.grammarName = top.grammarName; + local returnifyRewritten::PrimPatterns = consPattern(p.monadRewritten, terminal(Vbar_kwd, "|"), + psReturnify.returnify, + location=top.location); + --when the current clause is not a monad but the rest are, wrap the current one in Return() + local pReturnify::PrimPattern = p.monadRewritten; + pReturnify.returnFun = monadReturn(top.location); + pReturnify.grammarName = top.grammarName; + pReturnify.config = top.config; + pReturnify.env = top.env; + local returnRewritten::PrimPatterns = consPattern(pReturnify.returnify, terminal(Vbar_kwd, "|"), + ps.monadRewritten, + location=top.location); + top.monadRewritten = if isMonad(p.mtyperep, top.env) && monadsMatch(p.mtyperep, top.expectedMonad, top.mDownSubst).fst + then if isMonad(ps.mtyperep, top.env) && monadsMatch(ps.mtyperep, top.expectedMonad, top.mDownSubst).fst + then basicRewritten --both monads + else returnifyRewritten --current monad, rest not + else if isMonad(ps.mtyperep, top.env) && monadsMatch(ps.mtyperep, top.expectedMonad, top.mDownSubst).fst + then returnRewritten --rest monad, current not + else basicRewritten; --neither monads +} + +aspect production prodPattern +top::PrimPattern ::= qn::QName '(' ns::VarBinders ')' arr::Arrow_kwd e::Expr +{ + local ne::Expr = e; + ne.env = e.env; + ne.frame = top.frame; + ne.compiledGrammars = top.compiledGrammars; + ne.grammarName = top.grammarName; + ne.config = top.config; + ne.flowEnv = top.flowEnv; + ne.originRules = top.originRules; + ne.isRoot = top.isRoot; + + ne.finalSubst = top.finalSubst; + ne.downSubst = top.mDownSubst; + ne.mDownSubst = top.mDownSubst; + top.mUpSubst = ne.mUpSubst; + + ne.expectedMonad = top.expectedMonad; + + ne.monadicallyUsed = false; + top.monadicNames = ne.monadicNames; +} +aspect production prodPatternNormal +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = prod_type.outputType; + + top.returnify = prodPatternNormal(qn, ns, + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = prodPatternNormal(qn, ns, e.monadRewritten, location=top.location); +} + +aspect production prodPatternGadt +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = prod_type.outputType; + + top.returnify = prodPatternGadt(qn, ns, + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = prodPatternGadt(qn, ns, e.monadRewritten, location=top.location); +} + + +aspect production integerPattern +top::PrimPattern ::= i::Int_t arr::Arrow_kwd e::Expr +{ + top.merrors := e.merrors; + e.mDownSubst = top.mDownSubst; + top.mUpSubst = e.mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = intType(); + + top.returnify = integerPattern(i, terminal(Arrow_kwd, "->"), + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = integerPattern(i, terminal(Arrow_kwd, "->"), e.monadRewritten, location=top.location); +} +aspect production floatPattern +top::PrimPattern ::= f::Float_t arr::Arrow_kwd e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = floatType(); + + top.returnify = floatPattern(f, terminal(Arrow_kwd, "->"), + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = floatPattern(f, terminal(Arrow_kwd, "->"), e.monadRewritten, location=top.location); +} +aspect production stringPattern +top::PrimPattern ::= i::String_t arr::Arrow_kwd e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = stringType(); + + top.returnify = stringPattern(i, terminal(Arrow_kwd, "->"), + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = stringPattern(i, terminal(Arrow_kwd, "->"), e.monadRewritten, location=top.location); +} +aspect production booleanPattern +top::PrimPattern ::= i::String arr::Arrow_kwd e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + top.patternType = stringType(); + + top.returnify = booleanPattern(i, terminal(Arrow_kwd, "->"), + Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = booleanPattern(i, terminal(Arrow_kwd, "->"), e.monadRewritten, location=top.location); +} +aspect production nilPattern +top::PrimPattern ::= e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + local attribute thisListType::Type = listType(freshType()); + top.patternType = thisListType; + + top.returnify = nilPattern(Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = nilPattern(e.monadRewritten, location=top.location); +} +aspect production conslstPattern +top::PrimPattern ::= h::Name t::Name e::Expr +{ + top.merrors := e.merrors; + propagate mDownSubst, mUpSubst; + + e.monadicallyUsed = false; + top.monadicNames = e.monadicNames; + + top.mtyperep = e.mtyperep; + local elemType :: Type = freshType(); + top.patternType = listType(elemType); + + top.returnify = conslstPattern(h, t, Silver_Expr { $Expr{top.returnFun}($Expr{e}) }, + location=top.location); + top.monadRewritten = conslstPattern(h, t, e.monadRewritten, location=top.location); +} + + diff --git a/grammars/silver/compiler/extension/implicit_monads/ProductionBody.sv b/grammars/silver/compiler/extension/implicit_monads/ProductionBody.sv new file mode 100644 index 000000000..5d4a7654a --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/ProductionBody.sv @@ -0,0 +1,296 @@ +grammar silver:compiler:extension:implicit_monads; + + +terminal Implicit_kwd 'implicit' lexer classes {KEYWORD,RESERVED}; +terminal Restricted_kwd 'restricted' lexer classes {KEYWORD,RESERVED}; +terminal Unrestricted_kwd 'unrestricted' lexer classes {KEYWORD,RESERVED}; + + + +--Write an empty equation filled in by an appropriate fail +--We want to keep the 'implicit' keyword here so people don't accidentally write empty equations +concrete production emptyAttributeDef +top::ProductionStmt ::= 'implicit' dl::DefLHS '.' attr::QNameAttrOccur '=' ';' +{ + top.unparse = "\timplicit " ++ dl.unparse ++ "." ++ attr.unparse ++ " = ;"; + + top.productionAttributes := []; + top.defs := []; + top.uniqueSignificantExpression := []; + + top.containsPluck = false; + + local merrors::[Message] = + (if isMonadFail(attr.typerep, top.env) + then [] + else [err(top.location, monadToString(attr.typerep) ++ + " is not an instance of MonadFail and cannot " ++ + "be used in an empty equation")]) ++ + ( if attr.found && dl.found + then case attr.attrDcl of + | implicitInhDcl(_, _, _) -> [] + | implicitSynDcl(_, _, _) -> [] + | _ -> [err(top.location, "Implicit equations can only be used for " ++ + "attributes declared to be implicit; " ++ + attr.unparse ++ " is not implicit")] + end + else dl.errors ++ attr.errors ); + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + forwards to + if null(merrors) + then attr.attrDcl.attrDefDispatcher(dl, attr, monadFail(top.location), top.location) + else errorProductionStmt(merrors, location=top.location); +} + + +global partialDefaultAttributeDef::(ProductionStmt ::= PartiallyDecorated DefLHS PartiallyDecorated QNameAttrOccur Expr Location) = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr loc::Location -> + attributeDef(newPartial(dl), '.', newPartial(attr), '=', e, ';', location=loc); + +concrete production implicitAttributeDef +top::ProductionStmt ::= 'implicit' dl::DefLHS '.' attr::QNameAttrOccur '=' e::Expr ';' +{ + top.unparse = "\timplicit" ++ dl.unparse ++ "." ++ attr.unparse ++ " = ;"; + + top.productionAttributes := []; + top.defs := []; + top.uniqueSignificantExpression := []; + + top.containsPluck = false; + + local merrors::[Message] = + if attr.found && dl.found + then case attr.attrDcl of + | implicitSynDcl(_, _, _) -> [] + | implicitInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Implicit equations can only be used for " ++ + "attributes declared to be implicit; " ++ + attr.unparse ++ " is not implicit")] + end + else []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + local fwd::ProductionStmt = + (if null(merrors) + then if attr.found + then attr.attrDcl.attrDefDispatcher + --if not found, let the normal dispatcher handle it + else partialDefaultAttributeDef + else errorAttributeDef(merrors, _, _, _, location=_))(dl, attr, e, top.location); + forwards to fwd; +} + + + + +concrete production restrictedAttributeDef +top::ProductionStmt ::= 'restricted' dl::DefLHS '.' attr::QNameAttrOccur '=' e::Expr ';' +{ + e.downSubst = top.downSubst; + top.unparse = "\trestricted" ++ dl.unparse ++ "." ++ attr.unparse ++ " = ;"; + + top.productionAttributes := []; + top.defs := []; + top.uniqueSignificantExpression := []; + + top.containsPluck = false; + + local merrors::[Message] = + if attr.found && dl.found + then case attr.attrDcl of + | restrictedSynDcl(_, _, _) -> [] + | restrictedInhDcl(_, _, _) -> [] + | _ -> [err(top.location, "Restricted equations can only be used for " ++ + "attributes declared to be restricted; " ++ + attr.unparse ++ " is not restricted")] + end + else []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + local fwd::ProductionStmt = + (if null(merrors) + then if attr.found + then attr.attrDcl.attrDefDispatcher + --if not found, let the normal dispatcher handle it + else partialDefaultAttributeDef + else errorAttributeDef(merrors, _, _, _, location=_))(dl, attr, e, top.location); + + forwards to fwd; +} + + + + +concrete production unrestrictedAttributeDef +top::ProductionStmt ::= 'unrestricted' dl::DefLHS '.' attr::QNameAttrOccur '=' e::Expr ';' +{ + top.unparse = "\tunrestricted" ++ dl.unparse ++ "." ++ attr.unparse ++ " = ;"; + + top.productionAttributes := []; + top.defs := []; + top.uniqueSignificantExpression := []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + top.containsPluck = false; + + local restrictedErr::[Message] = + [err(top.location, + "Unrestricted equations can only be used for attributes " ++ + "not declared to be restricted or implicit; " ++ attr.unparse ++ " is restricted")]; + local implicitErr::[Message] = + [err(top.location, + "Unrestricted equations can only be used for attributes " ++ + "not declared to be restricted or implicit; " ++ attr.unparse ++ " is implicit")]; + local fwd::ProductionStmt = + (if attr.found + then case attr.attrDcl of + | restrictedSynDcl(_, _, _) -> errorAttributeDef(restrictedErr, _, _, _, location=_) + | restrictedInhDcl(_, _, _) -> errorAttributeDef(restrictedErr, _, _, _, location=_) + | implicitSynDcl(_, _, _) -> errorAttributeDef(implicitErr, _, _, _, location=_) + | implicitInhDcl(_, _, _) -> errorAttributeDef(implicitErr, _, _, _, location=_) + | _ -> partialDefaultAttributeDef + end + --if not found, let the normal dispatcher handle it + else partialDefaultAttributeDef)(dl, attr, e, top.location); + forwards to fwd; +} + + + + + + +--take a list of unallowed attributes and generate error messages for them +function buildExplicitAttrErrors +[Message] ::= l::[Pair] +{ + return case l of + | [] -> [] + | pair(name, loca)::t -> + err(loca, "Attributes accessed in restricted equations must be restricted; " ++ + name ++ " is not")::buildExplicitAttrErrors(t) + end; +} + + + +--productions for error checking on restricted attributes +abstract production restrictedSynAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.downSubst = top.downSubst; + e.originRules = top.originRules; + e.isRoot = true; + + top.containsPluck = false; + top.uniqueSignificantExpression := []; + + local merrors::[Message] = + --gives errors for implicit/unrestricted attributes used + buildExplicitAttrErrors(e.notExplicitAttributes); + + local fwd::ProductionStmt = + (if null(merrors) + then synthesizedAttributeDef(_, _, _, location=_) + else errorAttributeDef(merrors, _, _, _, location=_))(dl, attr, e, top.location); + forwards to fwd; +} + + +abstract production restrictedInhAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.downSubst = top.downSubst; + e.originRules = top.originRules; + e.isRoot = true; + + top.containsPluck = false; + top.uniqueSignificantExpression := []; + + local merrors::[Message] = + --gives errors for implicit/unrestricted attributes used + buildExplicitAttrErrors(e.notExplicitAttributes); + + local fwd::ProductionStmt = + (if null(merrors) + then inheritedAttributeDef(_, _, _, location=_) + else errorAttributeDef(merrors, _, _, _, location=_))(dl, attr, e, top.location); + forwards to fwd; +} + + + + +--productions for error checking on implicit attributes +abstract production implicitSynAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.downSubst = top.downSubst; + e.mDownSubst = top.downSubst; + e.finalSubst = e.mUpSubst; + e.env = top.env; + e.originRules = top.originRules; + e.isRoot = true; + + e.expectedMonad = attr.typerep; + + top.containsPluck = false; + top.uniqueSignificantExpression := []; + + local fwd::ProductionStmt = + (if null(e.merrors) + then if fst(monadsMatch(attr.typerep, e.mtyperep, e.mUpSubst)) + then synthesizedAttributeDef(_, _, e.monadRewritten, location=_) + else synthesizedAttributeDef(_, _, Silver_Expr { + $Expr {monadReturn(top.location)} + ($Expr {e.monadRewritten}) + }, location=_) + else errorAttributeDef(e.merrors, _, _, e.monadRewritten, location=_))(dl, attr, top.location); + forwards to fwd; +} + + +abstract production implicitInhAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; + + e.downSubst = top.downSubst; + e.mDownSubst = top.downSubst; + e.finalSubst = e.mUpSubst; + e.env = top.env; + e.originRules = top.originRules; + e.isRoot = true; + + e.expectedMonad = attr.typerep; + + top.containsPluck = false; + top.uniqueSignificantExpression := []; + + local fwd::ProductionStmt = + (if null(e.merrors) + then if fst(monadsMatch(attr.typerep, e.mtyperep, e.mUpSubst)) + then inheritedAttributeDef(_, _, e.monadRewritten, location=_) + else inheritedAttributeDef(_, _, Silver_Expr { + $Expr {monadReturn(top.location)} + ($Expr {e.monadRewritten}) + }, location=_) + else errorAttributeDef(e.merrors, _, _, e.monadRewritten, location=_))(dl, attr, top.location); + forwards to fwd; +} + diff --git a/grammars/silver/compiler/extension/implicit_monads/Project.sv b/grammars/silver/compiler/extension/implicit_monads/Project.sv new file mode 100644 index 000000000..255f02828 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Project.sv @@ -0,0 +1,21 @@ +grammar silver:compiler:extension:implicit_monads; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:definition:flow:driver; +imports silver:compiler:definition:flow:ast; +imports silver:compiler:driver:util; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +imports silver:util:cmdargs; + +imports silver:compiler:extension:convenience; +imports silver:compiler:extension:patternmatching; +imports silver:compiler:modification:list; + +imports silver:compiler:modification:lambda_fn; +imports silver:compiler:modification:let_fix; +imports silver:compiler:modification:primitivepattern; +imports silver:compiler:modification:copper; diff --git a/grammars/silver/compiler/extension/implicit_monads/README.md b/grammars/silver/compiler/extension/implicit_monads/README.md new file mode 100644 index 000000000..e3e6d5734 --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/README.md @@ -0,0 +1,54 @@ + +This extension implements monadification for implicit monads. We do +this by rewriting the expressions in implicit equations. + + + +# Equations + +We have both marked equations (e.g. `implicit t.a = e;`) and unmarked +equations (e.g. `t.a = e;`) for attributes. The marked equations are +primarily for consistency with the paper, and thus should probably be +left even though they aren't necessary anymore. + + + +# Expressions + +Some general notes on expression rewriting, which address common but +perhaps non-obvious elements of the rewriting: + +Expressions in implicit equations rewrite based on typing. We end up +having similar attributes (declared in `Util.sv`) as normal typing, +but we need our own because the equations need to take into account +the implicit monads which might be present when deciding whether an +expression is typable, or close enough to typable to rewrite. + +Most error checking is left to the rewritten version, with our +checking only caring about whether something is a monad or not. This +allows us to reduce the duplication of typechecking between this +extension and the host language, as well as making our job easier. + +Many expressions rewrite by using lambda functions to assign new names +to expressions. The main reason we do this is to keep the variable in +a bind from capturing the same variable in another subexpression. For +example, `e1 + (x * 2)`, not using a function to change the name of +`x * 2` can give us `bind(e1, \ x::Integer -> pure(x + x * 2))`, where +using a function for renaming can give us +``` +bind(e1, (\ x::Integer y::Integer -> pure(x + y))(_, x * 2)) +``` +where we don't have variable capture. A secondary reason for doing +this in some places is that it lets us rewrite constructed subparts of +the new expression with the names bound, while not needing to rewrite +the whole thing. For example, in `case_any`, we construct and rewrite +new `case` expressions in lambda functions so they have all the names +bound, then join them with `mplus`, then put them in a function to +bind the names we want. + +Throughout our expression rewriting, we check whether a monadic +expression is decorated, wrapping it in `new()` before binding it in +if it is. Silver does not appreciate a decorated expression as the +first argument to `bind`, and thus we need to check it and undecorate +it with `new()` if it is decorated. + diff --git a/grammars/silver/compiler/extension/implicit_monads/Util.sv b/grammars/silver/compiler/extension/implicit_monads/Util.sv new file mode 100644 index 000000000..1542009be --- /dev/null +++ b/grammars/silver/compiler/extension/implicit_monads/Util.sv @@ -0,0 +1,250 @@ +grammar silver:compiler:extension:implicit_monads; + + + +{- + EXPLANATION OF OUR VIEW OF A MONAD + + We assume our monads have their "inner type" as the last parameter + to their constructor (e.g. Either is a monad over Type, + not String). + + For two monad types to be the same, all their parameters must be the + same. For two monads to be the same, their non-monad parameters + must be the same. For example, Either and + Either have the same monad (Either) but are + not the same type. Either and Either do not + have the same monad. +-} + + +--The monad of the attribute the equation for which we are rewriting +inherited attribute expectedMonad::Type; +--The rewritten version of the current expression, exprs, etc. +synthesized attribute monadRewritten::a; +--Errors encountered in rewriting +synthesized attribute merrors::[Message] with ++; +--The type in the expression based on implicit monads +--We can't just use the normal typerep because it depends on the implicit monads +synthesized attribute mtyperep::Type; + + +-- TODO: There's lots of places where we can't automatically propagate these because +-- the host downSubst/upSubst attributes are mixed in too. +threaded attribute mDownSubst, mUpSubst::Substitution; + + +function isMonad +Boolean ::= ty::Type env::Decorated Env +{ + return case dropDecorated(ty) of + | appType(t, _) -> length(getInstanceDcl("silver:core:Monad", t, env)) > 0 + | t -> length(getInstanceDcl("silver:core:Monad", t, env)) > 0 + end; +} + +function isMonadPlus +Boolean ::= ty::Type env::Decorated Env +{ + return case dropDecorated(ty) of + | appType(t, _) -> length(getInstanceDcl("silver:core:MonadPlus", t, env)) > 0 + | t -> length(getInstanceDcl("silver:core:MonadPlus", t, env)) > 0 + end; +} + +function isMonadFail +Boolean ::= ty::Type env::Decorated Env +{ + return case dropDecorated(ty) of + | appType(t, _) -> length(getInstanceDcl("silver:core:MonadFail", t, env)) > 0 + | t -> length(getInstanceDcl("silver:core:MonadFail", t, env)) > 0 + end; +} + + +function dropDecorated +Type ::= ty::Type +{ + return if ty.isDecorated then ty.decoratedType else ty; +} + + +{-This checks two types are the same monad, (assuming they are monads) + though not necessarily the same monadic type (see discussion above)-} +function monadsMatch +Pair ::= ty1::Type ty2::Type subst::Substitution +{ + return + case dropDecorated(ty1), dropDecorated(ty2) of + | appType(c1, _), appType(c2, _) -> + tyMatch(c1, c2, subst) + | _, _ -> pair(false, subst) + end; +} + + +{-This is the easiest way to get case_any translation working. We + would be better off getting the error checking to occur prior to + rewriting so these functions don't show up. + + We also need to use this because we occasionally need to use new to + drop the decoration from the type of things we're passing into + binds.-} +function acceptableMonadFunction +Boolean ::= f::Decorated Expr +{ + return case f of + | functionReference(qNameId(name)) -> + name.name == "silver:core:alt" + | _ -> false + end; +} + + +function tyMatch +Pair ::= t1::Type t2::Type subst::Substitution +{ + local tycheck::TypeCheck = check(t1, t2); + tycheck.downSubst = subst; + return pair(!tycheck.typeerror, tycheck.upSubst); +} + + +function monadInnerType +Type ::= mty::Type loc::Location +{ + return case dropDecorated(mty) of + | appType(c, a) -> a + | _ -> error("The monadInnerType function should only be called " ++ + "once a type has been verified to be a monad (" ++ + prettyType(mty) ++ " at " ++ loc.unparse ++ ")") + end; +} + + +{-take the monad of mty and replace its inner type with the given type + to make a new monadic type-} +function monadOfType +Type ::= mty::Type newInner::Type +{ + return case dropDecorated(mty) of + | appType(c, _) -> appType(c, newInner) + | _ -> error("Tried to take a monad out of a non-monadic type (" ++ prettyType(mty) ++ ") to apply") + end; +} + + +--Print out the monad nicely rather than filled in with some other type +function monadToString +String ::= ty::Type +{ + return + case dropDecorated(ty) of + | appType(c, _) -> + --We use nonterminalType to get it to show just an underscore + --e.g. this gives us Maybe<_>, Either + prettyType(appType(c, nonterminalType("_", [], false))) + | _ -> error("Tried to get monadToString for a non-monadic type (" ++ prettyType(ty) ++ ")") + end; +} + + +{-find the name of the bind/return for a given monad to use to build + the rewritten term-} +function monadBind +Expr ::= l::Location +{ + return baseExpr(qNameId(name("silver:core:bind", l), location=l), location=l); +} +function monadReturn +Expr ::= l::Location +{ + return baseExpr(qNameId(name("silver:core:pure", l), location=l), location=l); +} + +--We want to produce a value, not a function, so we apply it to an argument +function monadFail +Expr ::= l::Location +{ + return + buildApplication + (baseExpr(qNameId(name("silver:core:fail", l), location=l), location=l), + [stringConst(terminal(String_t, "\"Automatically-inserted fail at " ++ + l.unparse ++ "\""), + location=l)], l); +} + + +function monadPlus +Expr ::= l::Location +{ + return baseExpr(qNameId(name("silver:core:alt", l), location=l), location=l); +} +function monadZero +Expr ::= l::Location +{ + return baseExpr(qNameId(name("silver:core:empty", l), location=l), location=l); +} + + + + + +{- + Some functions to build common structures to make rewriting easier. + By using these instead of Silver_Expr {...}, we can get actual locations where errors occur. + These can also be easier to read because we don't have all the "$Expr"s and "Silver_Expr"s around. +-} + +function buildApplication +Expr ::= fun::Expr args::[Expr] loc::Location +{ + return applicationExpr(fun, '(', buildApplicationReverseArgs(reverse(args), loc), ')', location=loc); +} + +--because the AST is set up as a snoc list, we build the arguments in reverse +--e.g. [a,b,c] gives application arguments (c, b, a) +function buildApplicationReverseArgs +AppExprs ::= args::[Expr] loc::Location +{ + return case args of + | [] -> emptyAppExprs(location=loc) + | hd::tl -> + snocAppExprs(buildApplicationReverseArgs(tl, loc), ',', + presentAppExpr(hd, location=loc), location=loc) + end; +} + + + +function buildLambda +Expr ::= n::String ty::Type body::Expr loc::Location +{ + -- \ n::ty -> body + return lambdap( + productionRHSCons(productionRHSElem(name(n, loc), + '::', + typerepTypeExpr(ty, location=loc), + location=loc), + productionRHSNil(location=loc), + location=loc), + body, + location=loc); +} + + +function buildMultiLambda +Expr ::= names::[Pair] body::Expr loc::Location +{ + local sig::ProductionRHS = + foldr(\ pr::Pair p::ProductionRHS -> + case pr of + | pair(n, ty) -> + productionRHSCons(productionRHSElem(name(n, loc), '::', + typerepTypeExpr(ty, location=loc), location=loc), + p, location=loc) + end, + productionRHSNil(location=loc), names); + return lambdap(sig, body, location=loc); +} + diff --git a/grammars/silver/compiler/extension/patternmatching/Case.sv b/grammars/silver/compiler/extension/patternmatching/Case.sv new file mode 100644 index 000000000..77b7583db --- /dev/null +++ b/grammars/silver/compiler/extension/patternmatching/Case.sv @@ -0,0 +1,1366 @@ +grammar silver:compiler:extension:patternmatching; + +imports silver:util:treeset as ts; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:modification:primitivepattern; +imports silver:compiler:modification:list; + +--Get mwdaWrn production for completeness analysis +import silver:compiler:analysis:warnings:flow; +--Get getNonforwardingProds to check all are covered +import silver:compiler:definition:flow:env only getNonforwardingProds; + +import silver:compiler:definition:type:syntax only typerepTypeExpr; +import silver:compiler:modification:let_fix; + +import silver:util:cmdargs hiding name; + +terminal Case_kwd 'case' lexer classes {KEYWORD,RESERVED}; +terminal Of_kwd 'of' lexer classes {KEYWORD,RESERVED}; +terminal Arrow_kwd '->' lexer classes {SPECOP}; +terminal Vbar_kwd '|' lexer classes {SPECOP}; +terminal Opt_Vbar_t /\|?/ lexer classes {SPECOP}; -- optional Coq-style vbar. +terminal When_kwd 'when' lexer classes {KEYWORD,RESERVED}; +terminal Matches_kwd 'matches' lexer classes {KEYWORD}; + +-- MR | ... +nonterminal MRuleList with location, config, unparse, env, frame, errors, freeVars, matchRuleList, matchRulePatternSize; +propagate errors, freeVars on MRuleList; + +-- Turns MRuleList (of MatchRules) into [AbstractMatchRule] +synthesized attribute matchRuleList :: [AbstractMatchRule]; +-- Notification of the number of expressions being matched upon +autocopy attribute matchRulePatternSize :: Integer; + +-- P -> E +nonterminal MatchRule with location, config, unparse, env, frame, errors, freeVars, matchRuleList, matchRulePatternSize; +nonterminal AbstractMatchRule with location, unparse, frame, freeVars, headPattern, isVarMatchRule, expandHeadPattern, hasCondition; + +-- The head pattern of a match rule +synthesized attribute headPattern :: Decorated Pattern; +-- Whether the head pattern of a match rule is a variable binder or not +synthesized attribute isVarMatchRule :: Boolean; +-- Turns A(B, C), D into B, C, D in the patterns list, with a list of named patterns to include. +synthesized attribute expandHeadPattern :: (AbstractMatchRule ::= [String]); +-- For completeness checking, we need to know if we have a condition +synthesized attribute hasCondition::Boolean; + +-- P , ... +nonterminal PatternList with location, config, unparse, patternList, env, frame, errors, patternVars, patternVarEnv; +propagate errors on PatternList; + +-- Turns PatternList into [Pattern] +synthesized attribute patternList :: [Decorated Pattern]; + + +{- NOTE ON ERRORS: #HACK2012 + - + - All of the real error checking should be done in PrimitiveMatch.sv on the + - more primitive form of pattern matching. BUT, there are a few + - kinds of errors that the pattern matching compiler will OBSCURE + - and so we must check for them here. + - + - ANY error on MRuleList, MatchRule, PatternList, or Pattern should + - be accompanied by a comment explaining why it's there, and not on + - primitive match. + -} + + +concrete production caseExpr_c +top::Expr ::= 'case' es::Exprs 'of' Opt_Vbar_t ml::MRuleList 'end' +{ + top.unparse = "case " ++ es.unparse ++ " of " ++ ml.unparse ++ " end"; + propagate freeVars; + + ml.matchRulePatternSize = length(es.rawExprs); + top.errors <- ml.errors; + + -- TODO: this is the only use of .rawExprs. FIXME + -- introduce the failure case here. + forwards to + caseExpr(es.rawExprs, ml.matchRuleList, true, + mkStrFunctionInvocation(top.location, "silver:core:error", + [stringConst(terminal(String_t, + "\"Error: pattern match failed at " ++ top.grammarName ++ " " ++ top.location.unparse ++ "\\n\""), location=top.location)]), + freshType(), location=top.location); +} + + +--Argument `complete` controls whether we check for case completeness or not (true -> check) +--This is used so generated case expressions can avoid the incomplete check +abstract production caseExpr +top::Expr ::= es::[Expr] ml::[AbstractMatchRule] complete::Boolean failExpr::Expr retType::Type +{ + top.unparse = + "(case " ++ implode(", ", map((.unparse), es)) ++ " of " ++ + implode(" | ", map((.unparse), ml)) ++ " | _ -> " ++ failExpr.unparse ++ + " end :: " ++ prettyType(retType) ++ ")"; + top.freeVars := concat(map(getFreeVars(top.frame, _), es) ++ map(getFreeVars(top.frame, _), ml) ++ [failExpr.freeVars]); + + {-Checking Pattern Completeness + + We want to check if a set of patterns covers all possible cases. + For this, we need to consider closed and non-closed nonterminals + separately. We will NOT count match rules with conditions as + contributing to completeness, as we will assume conditions are + actually conditional, and the rule with a condition will sometimes + match and sometimes not. + -} + local conditionlessRules::[AbstractMatchRule] = + partition((.hasCondition), ml).snd; + local conditionlessPatterns::[[Decorated Pattern]] = + map(\ x::AbstractMatchRule -> + case x of + | matchRule(plst, _, _) -> plst + end, conditionlessRules); + local completenessCounterExample::Maybe<[Pattern]> = + checkCompleteness(conditionlessPatterns, top.env, top.flowEnv); + + top.errors <- + case completenessCounterExample of + | just(lst) when complete -> + [mwdaWrn(top.config, top.location, + "This pattern-matching is not exhaustive. Here is an example of a " ++ + "case that is not matched: " ++ implode(", ", map((.unparse), lst)))] + | _ -> [] + end; + + --If we have only conditional rules, it isn't complete + top.errors <- + if complete && length(conditionlessRules) == 0 && length(ml) > 0 + then [mwdaWrn(top.config, top.location, + "This pattern-matching is not exhaustive because it only has conditional rules")] + else []; + + + {-Checking Pattern Overlaps + + We want to check if a set of patterns has cases which do not have + any patterns to distinguish between them. We do this by grouping + patterns as if we were compiling a pattern match from a conventional + (non-extensible) functional language, reducing until we have no + patterns left. + -} + top.errors <- checkOverlappingPatterns(es, ml); + + + {- + With the addition of completeness checking, we cannot + incrementally forward through a series of caseExpr, compiling + toward primitive matching as we go. That would lead to a lot of + false incompletes. Instead, we compile it in a function. + + We bind the expressions being matched with let expressions for + efficiency. + -} + local names::[String] = + map(\ x::Expr -> "__match_expr_" ++ toString(genInt()), es); + local nameExprs::[Expr] = + map(\ x::String -> baseExpr(qName(bogusLoc(), x), + location=bogusLoc()), names); + local compiledCase::Expr = + compileCaseExpr(nameExprs, ml, failExpr, retType, top.location, top.env); + local fwdResult::Expr = + foldr(\ p::(String, Expr) rest::Expr -> + makeLet(top.location, p.1, freshType(), p.2, rest), + compiledCase, zipWith(pair(_, _), names, es)); + forwards to fwdResult; +} + +function getFreeVars +attribute frame occurs on a, +attribute freeVars {frame} occurs on a => +ts:Set ::= frame::BlockContext x::a +{ + x.frame = frame; + return x.freeVars; +} + + +--Get the initial segment of the match rules which all have the same +--pattern type (constructor or var) and the rest of the rules +function initialSegmentPatternType +Pair<[AbstractMatchRule] [AbstractMatchRule]> ::= lst::[AbstractMatchRule] +{ + return case lst of + --this probably shouldn't be called with an empty list, but catch it anyway + | [] -> pair([], []) + | [mr] -> pair([mr], []) + | mr1::mr2::rest -> + if mr1.isVarMatchRule == mr2.isVarMatchRule + then --both have the same type of pattern + let sub::Pair<[AbstractMatchRule] [AbstractMatchRule]> = initialSegmentPatternType(mr2::rest) + in pair(mr1::sub.fst, sub.snd) end + else --the first has a different type of pattern than the second + pair([mr1], mr2::rest) + end; +} + + +{- + - Split rules into separate groups based on segments of the same kind of pattern type + - Assumes all match rules have at least one pattern; otherwise, you WILL get an error + - + - Consecutive variable patterns can go together, as they are simply + - turned into let expressions. + - Consecutive non-forwarding patterns (including primitive patterns) + - can go together, since a value can only match one of them. + - Consecutive instances of the same forwarding pattern can go together. + - Different forwarding productions need to go separately in case one + - would forward to the other (e.g. patterns `b() -> e1 | c() -> e2` + - where c() forwards to b() would match c() first if in the same + - primitive match). +-} +function splitPatternGroups +[[AbstractMatchRule]] ::= ml::[AbstractMatchRule] env::Decorated Env +{ + local firstPatt::Decorated Pattern = head(ml).headPattern; + + --First pattern is a variable + local vars::[AbstractMatchRule] = takeWhile((.isVarMatchRule), ml); + local varsRest::[[AbstractMatchRule]] = + splitPatternGroups(dropWhile((.isVarMatchRule), ml), env); + + --Type of the patterns; "" if primitive + local builtType::String = firstPatt.patternTypeName; + + --Primitive patterns, which cannot have forwarding productions and + -- thus need a different type of check + local primitives::[AbstractMatchRule] = + takeWhile(\ r::AbstractMatchRule -> + r.headPattern.patternTypeName == "" && + !r.isVarMatchRule, ml); + local primitivesRest::[[AbstractMatchRule]] = + splitPatternGroups(dropWhile(\ r::AbstractMatchRule -> + r.headPattern.patternTypeName == "" && + !r.isVarMatchRule, ml), + env); + + --All nonforwarding production names of the current type + local nonforwardingProds::[String] = + map((.fullName), filter(\ d::ValueDclInfo -> !d.hasForward, getKnownProds(builtType, env))); + + --Initial segment where the patterns are nonforwarding productions + local nonforwarding::[AbstractMatchRule] = + takeWhile(\ r::AbstractMatchRule -> + contains(r.headPattern.patternSortKey, + nonforwardingProds), ml); + local nonforwardingRest::[[AbstractMatchRule]] = + splitPatternGroups( + dropWhile(\ r::AbstractMatchRule -> + contains(r.headPattern.patternSortKey, + nonforwardingProds), ml), env); + + --Initial segment where the patterns are forwarding and all the same production + local forwardingOnes::[AbstractMatchRule] = + takeWhile(\ r::AbstractMatchRule -> + r.headPattern.patternSortKey == firstPatt.patternSortKey, + ml); + local forwardingRest::[[AbstractMatchRule]] = + splitPatternGroups( + dropWhile(\ r::AbstractMatchRule -> + r.headPattern.patternSortKey == firstPatt.patternSortKey, + ml), env); + + return + case ml of + | [] -> [] + | _ -> + if firstPatt.patternIsVariable + then vars::varsRest + else if builtType == "" && !firstPatt.patternIsVariable + then primitives::primitivesRest + else if contains(firstPatt.patternSortKey, nonforwardingProds) + then nonforwarding::nonforwardingRest + else forwardingOnes::forwardingRest + end; +} + + +--Compile a case expression `case es of ml` down into primitive matches +function compileCaseExpr +Expr ::= es::[Expr] ml::[AbstractMatchRule] failExpr::Expr retType::Type + loc::Location env::Decorated Env +{ + --Split rules into segments of non-forwarding constructors, all same + -- forwarding constructor, and variables based on first pattern + local groups::[[AbstractMatchRule]] = splitPatternGroups(ml, env); + + local compiledGroups::Expr = + compilePatternGroups(es, groups, failExpr, retType, loc, env); + + --Check if there is any match rule with empty patterns + local anyEmptyRules::Boolean = + any(map(\ m::AbstractMatchRule -> + case m of + | matchRule([], _, _) -> true + | _ -> false + end, ml)); + + --Assume all the rules are devoid of patterns + local finalStep::Expr = + foldr(\ mrule::AbstractMatchRule rest::Expr -> + case mrule of + | matchRule(_, nothing(), e) -> e + --cond is a Boolean + | matchRule(_, just((cond, nothing())), e) -> + ifThenElse('if', cond, 'then', e, 'else', rest, location=loc) + --cond is the expression for another match + | matchRule(_, just((cond, just(patt))), e) -> + Silver_Expr { + case $Expr{cond} of + | $Pattern{patt} -> $Expr{e} + | _ -> $Expr{rest} + end + } + end, + failExpr, ml); + + return + case ml of + | [] -> failExpr + | _ -> if anyEmptyRules then finalStep else compiledGroups + end; +} + + +--Compile a match where the sets of patterns are grouped based on +--having the same kind of first pattern (can go in the same primitive +--match together), create a series of primitive match expressions to +--implement the match +function compilePatternGroups +Expr ::= matchEs::[Expr] ruleGroups::[[AbstractMatchRule]] finalFail::Expr + retType::Type loc::Location env::Decorated Env +{ + local compileRest::Expr = + compilePatternGroups(matchEs, tail(ruleGroups), finalFail, + retType, loc, env); + + local firstGroup::[AbstractMatchRule] = + case ruleGroups of + | [] -> error("Shouldn't access firstGroup with empty ruleGroups") + | []::tl -> + error("Shouldn't have empty list of patterns in compilePatternGroups") + | hd::tl -> hd + end; + local firstPatt::Decorated Pattern = head(firstGroup).headPattern; + local failName::String = "__match_fail_" ++ toString(genInt()); + local firstMatchExpr::Expr = + case matchEs of + | [] -> + error("Shouldn't call compilePatternGroups with empty match expressions") + | e::tl -> e + end; + + --Modifying the order of rules in the same group (from ruleGroups) is fine, + --since we either have only the same constructor for a forwarding production + --or multiple non-forwarding productions where a value can only match one of them + local constructorGroups::[[AbstractMatchRule]] = groupMRules(firstGroup); + local mappedPatterns::[PrimPattern] = + map(allConCaseTransform(head(matchEs), tail(matchEs), + baseExpr(qName(loc, failName), location=loc), + retType, _, env), + constructorGroups); + local currentConCase::Expr = + matchPrimitive(firstMatchExpr, typerepTypeExpr(retType, location=loc), + foldPrimPatterns(mappedPatterns), + baseExpr(qName(loc, failName), location=loc), location=loc); + + -- A quick note about that freshType() hack: putting it here means there's ONE fresh type + -- generated, puching it inside 'bindHeadPattern' would generate multiple fresh types. + -- So don't try that! + local boundVarRules::[AbstractMatchRule] = + map(bindHeadPattern(firstMatchExpr, freshType(), _), firstGroup); + local currentVarCase::Expr = + compileCaseExpr(tail(matchEs), boundVarRules, + baseExpr(qName(loc, failName), location=loc), + retType, loc, env); + + local bindFailName::Expr = + makeLet(loc, failName, retType, compileRest, + if firstPatt.patternIsVariable + then currentVarCase + else currentConCase); + + return + case ruleGroups of + | [] -> finalFail + | _::_ -> bindFailName + end; +} + +{-- + - Takes a set of matchrules that all match against the SAME CONSTRUCTOR and pushes + - a complex case-expr within a primitive pattern that matches this constructor. + - + - @param currExpr (The current expression to match against in the overall complex case-expr) + - @param restExprs (The remaining expressions to match against in the overall complex case-expr) + - @param failCase (The failure expression) + - @param retType (The return type of the overall case-expr, and thus this) + - @param mrs (Match rules that all share the same head-pattern) + - @param env (Known environment) + - + - @return A primitive pattern matching the constructor, with the overall case-expr pushed down into it and compiled + -} +function allConCaseTransform +PrimPattern ::= currExpr::Expr restExprs::[Expr] failCase::Expr + retType::Type mrs::[AbstractMatchRule] env::Decorated Env +{ + local names :: [Name] = map(patternListVars, head(mrs).headPattern.patternSubPatternList); + + local subcase::Expr = + compileCaseExpr( + map(exprFromName, names) ++ annoAccesses ++ restExprs, + map(\ mr::AbstractMatchRule -> mr.expandHeadPattern(annos), mrs), + failCase, retType, head(mrs).location, env); + + local annos :: [String] = + nub(map(fst, flatMap((.patternNamedSubPatternList), map((.headPattern), mrs)))); + local annoAccesses :: [Expr] = + map(\ n::String -> access(currExpr, '.', qNameAttrOccur(qName(l, n), location=l), location=l), annos); + + -- Maybe this one is more reasonable? We need to test examples and see what happens... + local l :: Location = head(mrs).headPattern.location; + + return + case head(mrs).headPattern of + | prodAppPattern_named(qn,_,_,_,_,_) -> + prodPattern(qn, '(', convStringsToVarBinders(names, l), ')', '->', subcase, location=l) + | intPattern(it) -> integerPattern(it, '->', subcase, location=l) + | fltPattern(it) -> floatPattern(it, '->', subcase, location=l) + | strPattern(it) -> stringPattern(it, '->', subcase, location=l) + | truePattern(_) -> booleanPattern("true", '->', subcase, location=l) + | falsePattern(_) -> booleanPattern("false", '->', subcase, location=l) + | nilListPattern(_,_) -> nilPattern(subcase, location=l) + | consListPattern(h,_,t) -> conslstPattern(head(names), head(tail(names)), subcase, location=l) + | _ -> error("Can only have constructor patterns in allConCaseTransform: " ++ head(mrs).headPattern.unparse) + end; +} + + + + +--Check for overlapping patterns, which show up by standard pattern-matching compilation +function checkOverlappingPatterns +[Message] ::= es::[Expr] ml::[AbstractMatchRule] +{ + local errors::[Message] = + case ml of + --check for multiple match rules, with no patterns/conditions left to distinguish them + | matchRule([], _, e) :: _ :: _ -> + if areUselessPatterns(ml) + then [err(head(ml).location, "Pattern has overlapping cases!")] + else [] + | _ -> [] + end; + + local partMRs :: Pair<[AbstractMatchRule] [AbstractMatchRule]> = + partition((.isVarMatchRule), ml); + local varRules :: [AbstractMatchRule] = partMRs.fst; + local prodRules :: [AbstractMatchRule] = partMRs.snd; + + {-- + - All constructors? Check children of each constructor together, plus rest + -} + local allConCase :: [Message] = + let constructorGroups::[[AbstractMatchRule]] = groupMRules(prodRules) in + let mappedErrs::[Message] = + flatMap(allConCaseCheckOverlapping(es, _), constructorGroups) in + errors ++ mappedErrs + end end; + + {-- + - All variables? Drop the first pattern, and check the rest + -} + local allVarCase :: [Message] = + errors ++ + checkOverlappingPatterns(tail(es), map(dropHeadPattern(_), ml)); + + {-- + - Mixed con/var? Partition into segments + -} + local mixedCase :: [Message] = checkOverlappingMixedCaseMatches(es, ml); + + -- 4 cases: no patterns left, all constructors, all variables, or mixed con/var. + -- errors cases: more patterns no scrutinees, more scrutinees no patterns, no scrutinees multiple rules + return + case ml of + | matchRule([], c, e) :: _ -> errors + -- No match rules, only possible through abstract syntax + | [] -> [] + | _ -> if null(es) then [] + else if null(varRules) then allConCase + else if null(prodRules) then allVarCase + else mixedCase + end; +} + +{- + Check for overlapping patterns when we are mixing constructor and + variable patterns for first match. We do this by partitioning the + list into segments of only constructor or variable patterns in + order, then checking each segment as its own match. +-} +function checkOverlappingMixedCaseMatches +[Message] ::= es::[Expr] ml::[AbstractMatchRule] +{ + return if null(ml) + then [] + else let segments::Pair<[AbstractMatchRule] [AbstractMatchRule]> = + initialSegmentPatternType(ml) + in + checkOverlappingMixedCaseMatches(es, segments.snd) ++ + checkOverlappingPatterns(es, segments.fst) + end; +} + +{-- + - Expand the head of a match rule as if matched, and check for + - overlapping patterns based on the expansion. + -} +function allConCaseCheckOverlapping +[Message] ::= es::[Expr] mrs::[AbstractMatchRule] +{ + -- TODO: potential source of buggy error messages. We're using head(mrs) as the source of + -- authority for the length of pattern variables to match against. But each match rule may + -- actually have a different length (and .expandHeadPattern just applies whatever is there) + -- This is an erroneous condition, but it means we transform into a maybe-more erroneous condition. + local names :: [Name] = map(patternListVars, head(mrs).headPattern.patternSubPatternList); + + local l :: Location = head(mrs).headPattern.location; + local annos :: [String] = + nub(map(fst, flatMap((.patternNamedSubPatternList), map((.headPattern), mrs)))); + local annoAccesses :: [Expr] = + map(\ n::String -> access(head(es), '.', qNameAttrOccur(qName(l, n), location=l), location=l), annos); + + local subCaseCheck::[Message] = + checkOverlappingPatterns( + map(exprFromName, names) ++ annoAccesses ++ tail(es), + map(\ mr::AbstractMatchRule -> mr.expandHeadPattern(annos), mrs)); + -- TODO: head(mrs).location is probably not the correct thing to use here?? (generally) + + return subCaseCheck; +} + + + + + +{- + We check completeness with a function because we want to recursively + check, which we cannot do with attributes alone. + + We need the environment to look up any nonterminal matches and see + if the nonterminal is closed or not. + + We return nothing() if there are some patterns and the patterns are + complete, and just(plst) if plst is an example of a missing pattern + set (matching over multiple values at once). +-} +function checkCompleteness +Maybe<[Pattern]> ::= lst::[[Decorated Pattern]] env::Decorated Env + flowEnv::FlowEnv +{ + local pattGroups::([[Decorated Pattern]], [[Decorated Pattern]]) = + partition(\ plst::[Decorated Pattern] -> head(plst).patternIsVariable, lst); + local varGroup::[[Decorated Pattern]] = pattGroups.1; + local consGroup::[[Decorated Pattern]] = pattGroups.2; + + local allPattLens::[Integer] = map(\ x::[Decorated Pattern] -> length(x), lst); + local allSameLen::Boolean = + if null(lst) + then true + else all(map(\ x::Integer -> x == head(allPattLens), allPattLens)); + local numPatts::Integer = + if null(lst) || !allSameLen + then 0 + else head(allPattLens); + + --We delegate checking based on the kind of pattern which is first in the list. + local conPatts::[Decorated Pattern] = map(head, consGroup); + local isPrimPatts::Boolean = + foldr(\ a::Decorated Pattern b::Boolean -> b || a.isPrimitivePattern, + false, conPatts); + local isBoolPatts::Boolean = + foldr(\ a::Decorated Pattern b::Boolean -> b || a.isBoolPattern, + false, conPatts); + local isListPatts::Boolean = + foldr(\ a::Decorated Pattern b::Boolean -> b || a.isListPattern, + false, conPatts); + + {- + Each checking function checks completeness of the first patterns + in the sets, which are of the appropriate type, including checking + for variable patterns. If the first patterns are complete, it + then handles grouping the rest of the sets of patterns and + checking if they are complete as well. Correctly handling + grouping is the reason we need to have a separate function for + each kind of pattern. + -} + local boolComp::Maybe<[Pattern]> = checkBooleanCompleteness(consGroup, varGroup, env, flowEnv); + local listComp::Maybe<[Pattern]> = checkListCompleteness(consGroup, varGroup, env, flowEnv); + local ntComp::Maybe<[Pattern]> = checkNonterminalCompleteness(consGroup, varGroup, env, flowEnv); + local primComp::Maybe<[Pattern]> = checkPrimitiveCompleteness(consGroup, varGroup, env, flowEnv); + + return + if numPatts == 0 || !allSameLen + then nothing() + else if isBoolPatts + then boolComp + else if isListPatts + then listComp + else if isPrimPatts + then primComp + else if length(consGroup) > 0 + then ntComp + else --If we somehow end up with all vars to start, just check the rest + case checkCompleteness(map(tail, lst), env, flowEnv) of + | nothing() -> nothing() + | just(plst) -> just(wildcPattern('_', location=bogusLoc())::plst) + end; +} + +{- + We need blanks for (1) output when a production is missing and (2) + for expanding a variable pattern list to be the same length as an + expanded list from a head pattern with subpatterns. +-} +function generateWildcards +[Pattern] ::= n::Integer +{ + return repeat(wildcPattern('_', location=bogusLoc()), n); +} + +{- + Sometimes we need to decorate a list of wildcard patterns for the + type system to pass as an expanded list for completeness checking to + replace a variable. We should never need to access anything on + these, so passing in bottom values for the attributes is fine. +-} +function decoratePattList +[Decorated Pattern] ::= lst::[Pattern] +{ + return map(\ p::Pattern -> decorate p with { + config = error("not needed"); + frame = error("not needed"); + env = error("not needed"); + patternVarEnv = error("not needed"); + }, lst); +} + +--Group sets of patterns by the first pattern in each set +--The core groupBy function doesn't work because it groups contiguous +-- sets, and the patterns might not be contiguous. +function groupAllPattsByHead +[[[Decorated Pattern]]] ::= pattLists::[[Decorated Pattern]] +{ + return + if null(pattLists) + then [] + else case groupAllPattsByHeadHelp(head(pattLists), tail(pattLists)) of + | pair(thisGroup, others) -> + (head(pattLists)::thisGroup)::groupAllPattsByHead(others) + end; +} +function groupAllPattsByHeadHelp +Pair<[[Decorated Pattern]] [[Decorated Pattern]]> ::= + item::[Decorated Pattern] rest::[[Decorated Pattern]] +{ + return + case rest of + | [] -> pair([], []) + | h::t -> case groupAllPattsByHeadHelp(item, t) of + | pair(grp, rst) -> + if head(item).patternSortKey == head(h).patternSortKey + then pair(h::grp, rst) + else pair(grp, h::rst) + end + end; +} + +--This checks the primitive patterns all have the same type and generates an +-- example of a primitive value which is not covered by the given patterns +function generatePrimitiveMissingPattern +Maybe ::= patts::[Decorated Pattern] +{ + local ints::[Integer] = + foldr(\ p::Decorated Pattern l::[Integer] -> + case p of + | intPattern(int_t) -> toInteger(int_t.lexeme)::l + | _ -> l + end, [], patts); + local flts::[Float] = + foldr(\ p::Decorated Pattern l::[Float] -> + case p of + | fltPattern(flt_t) -> toFloat(flt_t.lexeme)::l + | _ -> l + end, [], patts); + local strs::[String] = + foldr(\ p::Decorated Pattern l::[String] -> + case p of --remove quotation marks + | strPattern(str_t) -> substring(1, length(str_t.lexeme) - 1, str_t.lexeme)::l + | _ -> l + end, [], patts); + return case ints, flts, strs of + | _::_, [], [] -> just(generateMissingIntegerPattern(ints, 0)) + | [], _::_, [] -> just(generateMissingFloatPattern(flts, 0.0)) + | [], [], _::_ -> just(generateMissingStringPattern(strs, "")) + | _, _, _ -> nothing() --type error, so don't generate a completeness error + end; +} + +function generateMissingIntegerPattern +Pattern ::= lst::[Integer] initial::Integer +{ + return if containsBy(\ a::Integer b::Integer -> a == b, initial, lst) + then generateMissingIntegerPattern(lst, initial + 1) + else intPattern(terminal(Int_t, toString(initial), bogusLoc()), location=bogusLoc()); +} +function generateMissingFloatPattern +Pattern ::= lst::[Float] initial::Float +{ + return if containsBy(\ a::Float b::Float -> a == b, initial, lst) + then generateMissingFloatPattern(lst, initial + 1.0) + else fltPattern(terminal(Float_t, toString(initial), bogusLoc()), location=bogusLoc()); +} +function generateMissingStringPattern +Pattern ::= lst::[String] initial::String +{ + return if containsBy(\ a::String b::String -> a == b, initial, lst) + then generateMissingStringPattern(lst, initial ++ "*") + else strPattern(terminal(String_t, "\"" ++ initial ++ "\"", bogusLoc()), location=bogusLoc()); +} + +--First pattern in each set in conPatts is a primitive +--The first match can only be completed by a variable +function checkPrimitiveCompleteness +Maybe<[Pattern]> ::= conPatts::[[Decorated Pattern]] varPatts::[[Decorated Pattern]] + env::Decorated Env flowEnv::FlowEnv +{ + local firstPatts::[Decorated Pattern] = map(head, conPatts); + local firstPattMissing::Maybe = + generatePrimitiveMissingPattern(firstPatts); + local numPatts::Integer = length(head(conPatts)); + + local grouped::[ [[Decorated Pattern]] ] = groupAllPattsByHead(conPatts); + local subcallCons::Maybe<[Pattern]> = + foldr(\ patts::[[Decorated Pattern]] rest::Maybe<[Pattern]> -> + case rest of + | nothing() -> + case checkCompleteness(map(tail, patts) ++ map(tail, varPatts), + env, flowEnv) of + | just(plst) -> just(new(head(head(patts)))::plst) + | nothing() -> nothing() + end + | just(plst) -> just(plst) + end, + nothing(), grouped); + local subcall::Maybe<[Pattern]> = + case subcallCons of + | just(plst) -> just(plst) + | nothing() -> + --If we have a case not covered by a pattern, we want to fill that in rather than '_' + --This gives us better error messages for missing patterns + case checkCompleteness(map(tail, varPatts), env, flowEnv), firstPattMissing of + | just(plst), just(fp) -> just(fp::plst) + | just(plst), nothing() -> just(wildcPattern('_', location=bogusLoc())::plst) + | nothing(), _ -> nothing() + end + end; + + return if length(varPatts) > 0 + then subcall + else case firstPattMissing of + | just(p) -> just(p::generateWildcards(numPatts - 1)) + | nothing() -> nothing() --only possible if there is a typing error + end; +} + +--First pattern in each set in consPatts is a Boolean pattern +--The first match can be completed by a variable or patterns for true and false +function checkBooleanCompleteness +Maybe<[Pattern]> ::= conPatts::[[Decorated Pattern]] varPatts::[[Decorated Pattern]] + env::Decorated Env flowEnv::FlowEnv +{ + --create groups for true and false + local grouped::([[Decorated Pattern]], [[Decorated Pattern]]) = + partition(\ plst::[Decorated Pattern] -> head(plst).patternSortKey == "true", conPatts); + local numPatts::Integer = length(head(conPatts)); + + local foundTrue::Boolean = length(grouped.1) > 0; + local foundFalse::Boolean = length(grouped.2) > 0; + local foundVar::Boolean = length(varPatts) > 0; + + --Check the completeness of the rest of the patterns where 'true' or a variable was first + local trueSubcall::Maybe<[Pattern]> = + checkCompleteness(map(\ plst::[Decorated Pattern] -> tail(plst), grouped.1) ++ + map(\ plst::[Decorated Pattern] -> tail(plst), varPatts), + env, flowEnv); + --Check the completeness of the rest of the patterns where 'false' or a variable was first + local falseSubcall::Maybe<[Pattern]> = + checkCompleteness(map(\ plst::[Decorated Pattern] -> tail(plst), grouped.2) ++ + map(\ plst::[Decorated Pattern] -> tail(plst), varPatts), + env, flowEnv); + --What's missing from subcalls, including the first pattern + local subcallResult::Maybe<[Pattern]> = + case trueSubcall of + | just(lst) -> just(truePattern('true', location=bogusLoc())::lst) + | nothing() -> + case falseSubcall of + | just(lst) -> just(falsePattern('false', location=bogusLoc())::lst) + | nothing() -> + --If completed by vars, need to check vars case is complete as well + if foundTrue && foundFalse + then nothing() + else checkCompleteness(varPatts, env, flowEnv) + end + end; + + return if foundVar + then subcallResult + else if foundTrue + then if foundFalse + then subcallResult + else just(falsePattern('false', location=bogusLoc())::generateWildcards(numPatts - 1)) + else just(truePattern('true', location=bogusLoc())::generateWildcards(numPatts - 1)); +} + +--First pattern in each set in consPatts is a list pattern +--The first match can be completed by a variable or patterns for cons and nil +function checkListCompleteness +Maybe<[Pattern]> ::= conPatts::[[Decorated Pattern]] varPatts::[[Decorated Pattern]] + env::Decorated Env flowEnv::FlowEnv +{ + --create groups for nil and cons + local grouped::([[Decorated Pattern]], [[Decorated Pattern]]) = + partition(\ plst::[Decorated Pattern] -> head(plst).patternSortKey == "silver:core:nil", conPatts); + local numPatts::Integer = length(head(conPatts)); + + local foundNil::Boolean = length(grouped.1) > 0; + local foundCons::Boolean = length(grouped.2) > 0; + local foundVar::Boolean = length(varPatts) > 0; + + --Check the completeness of the rest of the patterns where 'nil' or a variable was first + local nilSubcall::Maybe<[Pattern]> = + checkCompleteness(map(\ plst::[Decorated Pattern] -> tail(plst), grouped.1) ++ + map(\ plst::[Decorated Pattern] -> tail(plst), varPatts), + env, flowEnv); + --Check the completeness of the rest of the patterns where 'cons' or a variable was first + local consSubcall::Maybe<[Pattern]> = + --We check the subpatterns for the head and tail of the list and the overall tail together + checkCompleteness(map(\ plst::[Decorated Pattern] -> + head(plst).patternSubPatternList ++ tail(plst), grouped.2) ++ + map(\ plst::[Decorated Pattern] -> + decoratePattList(generateWildcards(2)) ++ tail(plst), varPatts), + env, flowEnv); + --What's missing from subcalls, including the first pattern + local subcallResult::Maybe<[Pattern]> = + case nilSubcall of + | just(lst) -> just(nilListPattern('[', ']', location=bogusLoc())::lst) + | nothing() -> + case consSubcall of + --If the missing subpattern is also a cons, wrap it in parentheses to display correctly + --Otherwise `(a::b)::c` displays as `a::b::c`, which means something different + | just(consListPattern(hd1, _, tl1)::tl::lst) -> + just(consListPattern( + nestedPatterns('(', consListPattern(hd1, '::', tl1, location=bogusLoc()), ')', + location=bogusLoc()), + '::', tl, location=bogusLoc())::lst) + | just(hd::tl::lst) -> + just(consListPattern(hd, '::', tl, location=bogusLoc())::lst) + | just(_) -> error("List must include patterns for at least head and tail") + | nothing() -> + --If completed by vars, need to check vars case is complete as well + if foundNil && foundCons + then nothing() + else checkCompleteness(varPatts, env, flowEnv) + end + end; + + return if foundVar + then subcallResult + else if foundNil + then if foundCons + then subcallResult + else just(consListPattern(wildcPattern('_', location=bogusLoc()), '::', + wildcPattern('_', location=bogusLoc()), location=bogusLoc()):: + generateWildcards(numPatts - 1)) + else just(nilListPattern('[', ']', location=bogusLoc())::generateWildcards(numPatts - 1)); +} + +--First pattern in each set in consPatts is a nonterminal pattern +--The first match can be completed by a variable or, for a non-closed nonterminal, +-- by having a pattern for each non-forwarding production +function checkNonterminalCompleteness +Maybe<[Pattern]> ::= conPatts::[[Decorated Pattern]] varPatts::[[Decorated Pattern]] + env::Decorated Env flowEnv::FlowEnv +{ + local numPatts::Integer = length(head(conPatts)); + + --All the patterns ought to have the same type. + local builtTypes::[String] = + nubBy(\ a::String b::String -> a == b, map((.patternTypeName), map(head, conPatts))); + local builtType::String = head(builtTypes); + + --Test whether all the required productions are represented in the productions + local requiredProds::[String] = getNonforwardingProds(builtType, flowEnv); + local groupedPatts::[ [[Decorated Pattern]] ] = + groupAllPattsByHead(conPatts); + local constructorReps::[Decorated Pattern] = + map(\ plst::[[Decorated Pattern]] -> head(head(plst)), groupedPatts); + local allRepresented::Maybe = + checkAllProdsRepresented(constructorReps, requiredProds, env); + + local hasVar::Boolean = length(varPatts) > 0 && length(head(varPatts)) > 0; + local isVarCompleted::Boolean = hasVar && allRepresented.isJust; + + --Test whether the children and rests of the list are complete, but only for required productions + --We don't care whether other productions are complete because they forward to required productions + local reqGroupedPatts::[ [[Decorated Pattern]] ] = + filter(\ plst::[[Decorated Pattern]] -> + contains(head(head(plst)).patternSortKey, requiredProds), groupedPatts); + local groupsWithVars::[ [[Decorated Pattern]] ] = + map(\ plst::[[Decorated Pattern]] -> plst ++ varPatts, reqGroupedPatts); + local subcallResult::Maybe<[Pattern]> = + case checkAllProdGroupsComplete(reqGroupedPatts, varPatts, env, flowEnv) of + | just(plst) -> just(plst) + | nothing() -> + --check if it was var completed, and, if so, if the var patterns are complete + if isVarCompleted + then + --If we have a case not covered by a pattern, we want to fill that in rather than '_' + --This gives us better error messages for missing patterns + case checkCompleteness(map(tail, varPatts), env, flowEnv), allRepresented of + | nothing(), _ -> nothing() + | just(plst), nothing() -> just(wildcPattern('_', location=bogusLoc())::plst) + | just(plst), just(p) -> just(p::plst) + end + else nothing() + end; + + --To find out if the nonterminal is closed or not, we need to look it up + local nt::QName = qName(bogusLoc(), builtType); + nt.env = env; + local isClosed::Boolean = + case nt.lookupType.dcls of + | ntDcl(_, _, closed, _) :: _ -> closed + | _ -> false -- default, if the lookup fails + end; + + -- TODO: named argument patterns are not handled yet + return + --If we are building multiple types or have unknown productions, + -- we have a type error, so don't check completeness + if length(builtTypes) != 1 + then nothing() + else if isClosed && !hasVar + --This is a hack to pass up a message about closed nonterminals as a pattern + then just(varPattern(name("", bogusLoc()), + location=bogusLoc())::generateWildcards(numPatts - 1)) + else if hasVar + then subcallResult + else case allRepresented of + | just(p) -> just(p::generateWildcards(numPatts - 1)) + | nothing() -> subcallResult + end; +} + +--Check every set of patterns in conGrps is complete, when varPatts is added to it +function checkAllProdGroupsComplete +Maybe<[Pattern]> ::= conGrps::[ [[Decorated Pattern]] ] varPatts::[[Decorated Pattern]] + env::Decorated Env flowEnv::FlowEnv +{ + local hdProdPatt::Decorated Pattern = head(head(head(conGrps))); + local numChildren::Integer = length(hdProdPatt.patternSubPatternList); + + --Check the completeness of the children of the current production, joined with the rest of the pattern list + local expandedVars::[[Decorated Pattern]] = + --We need to drop the head and add wildcards for the number of children + map(\ plst::[Decorated Pattern] -> + decoratePattList(generateWildcards(numChildren)) ++ tail(plst), varPatts); + local grpWithVars::[[Decorated Pattern]] = + map(\ plst::[Decorated Pattern] -> + head(plst).patternSubPatternList ++ tail(plst), head(conGrps)) ++ + expandedVars; + local hdComplete::Maybe<[Pattern]> = checkCompleteness(grpWithVars, env, flowEnv); + + return + case conGrps of + | [] -> nothing() + | _::rest -> + case hdComplete, hdProdPatt of + | just(plst), prodAppPattern_named(qname, _, _, _, _, _) -> + just(prodAppPattern(qname, '(', buildPatternList(take(numChildren, plst), bogusLoc()), + ')', location=bogusLoc())::drop(numChildren, plst)) + | just(_), _ -> error("Should not have anything but prodAppPattern_named here") + | nothing(), _ -> checkAllProdGroupsComplete(rest, varPatts, env, flowEnv) + end + end; +} + +--check that all required productions are present +function checkAllProdsRepresented +Maybe ::= givenPatts::[Decorated Pattern] requiredProds::[String] env::Decorated Env +{ + {- + We walk down through requiredProds rather than pattGroups. We + only care that everything in requiredProds is covered; we don't + care about anything else which shows up in pattGroups + (e.g. forwarding productions). + -} + local firstProdName::String = head(requiredProds); + local firstProdQName::QName = qName(bogusLoc(), firstProdName); + local pattFound::Boolean = + foldr(\ p::Decorated Pattern b::Boolean -> + b || p.patternSortKey == firstProdName, + false, givenPatts); + + firstProdQName.env = env; + local firstProdNumArgs::Integer = firstProdQName.lookupValue.typeScheme.typerep.arity; + local wildcards::PatternList = + buildPatternList(repeat(wildcPattern('_', location=bogusLoc()), firstProdNumArgs), bogusLoc()); + + return + case requiredProds of + | [] -> nothing() + | _::rest -> + if pattFound + then checkAllProdsRepresented(givenPatts, rest, env) + else just(prodAppPattern(firstProdQName, '(', wildcards, ')', + location=bogusLoc())) + end; +} + + + + +--Match Rules +concrete production mRuleList_one +top::MRuleList ::= m::MatchRule +{ + top.unparse = m.unparse; + + top.matchRuleList = m.matchRuleList; +} + +concrete production mRuleList_cons +top::MRuleList ::= h::MatchRule '|' t::MRuleList +{ + top.unparse = h.unparse ++ " | " ++ t.unparse; + + top.matchRuleList = h.matchRuleList ++ t.matchRuleList; +} + +concrete production matchRule_c +top::MatchRule ::= pt::PatternList '->' e::Expr +{ + top.unparse = pt.unparse ++ " -> " ++ e.unparse; + top.errors := pt.errors; -- e.errors is examined later, after transformation. + top.freeVars := ts:removeAll(pt.patternVars, e.freeVars); + + top.errors <- + if length(pt.patternList) == top.matchRulePatternSize then [] + else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; + + pt.patternVarEnv = []; + + top.matchRuleList = [matchRule(pt.patternList, nothing(), e, location=top.location)]; +} + +concrete production matchRuleWhen_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr '->' e::Expr +{ + top.unparse = pt.unparse ++ " when " ++ cond.unparse ++ " -> " ++ e.unparse; + top.errors := pt.errors; -- e.errors is examined later, after transformation, as is cond.errors + top.freeVars := ts:removeAll(pt.patternVars, cond.freeVars ++ e.freeVars); + + top.errors <- + if length(pt.patternList) == top.matchRulePatternSize then [] + else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; + + pt.patternVarEnv = []; + + top.matchRuleList = [matchRule(pt.patternList, just(pair(cond, nothing())), e, location=top.location)]; +} + +concrete production matchRuleWhenMatches_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern '->' e::Expr +{ + top.unparse = pt.unparse ++ " when " ++ cond.unparse ++ " matches " ++ p.unparse ++ " -> " ++ e.unparse; + top.errors := pt.errors; -- e.errors is examined later, after transformation, as is cond.errors + top.freeVars := ts:removeAll(pt.patternVars, cond.freeVars ++ ts:removeAll(p.patternVars, e.freeVars)); + + top.errors <- + if length(pt.patternList) == top.matchRulePatternSize then [] + else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; + + pt.patternVarEnv = []; + p.patternVarEnv = pt.patternVars; + + top.matchRuleList = [matchRule(pt.patternList, just(pair(cond, just(p))), e, location=top.location)]; +} + +abstract production matchRule +top::AbstractMatchRule ::= pl::[Decorated Pattern] + --Condition, if it exists, is either a Boolean expression or a + -- pair of an expression and a pattern for it to match + cond::Maybe>> e::Expr +{ + top.unparse = + implode(", ", map((.unparse), pl)) ++ + case cond of + | just(pair(c, just(p))) -> " when " ++ c.unparse ++ " matches " ++ p.unparse + | just(pair(c, nothing())) -> " when " ++ c.unparse + | nothing() -> "" + end ++ + " -> " ++ e.unparse; + top.freeVars := ts:removeAll(concat(map((.patternVars), pl)), + case cond of + | just((e1, just(p))) -> getFreeVars(top.frame, e1) ++ ts:removeAll(p.patternVars, e.freeVars) + | just((e1, nothing())) -> getFreeVars(top.frame, e1) ++ e.freeVars + | nothing() -> e.freeVars + end); + top.headPattern = head(pl); + -- If pl is null, and we're consulted, then we're missing patterns, pretend they're _ + top.isVarMatchRule = null(pl) || head(pl).patternIsVariable; + -- For this, we safely know that pl is not null: + top.expandHeadPattern = + \ named::[String] -> + matchRule( + head(pl).patternSubPatternList ++ + map( + \ n::String -> + fromMaybe( + decorate wildcPattern('_', location=top.location) + with { frame = head(pl).frame; config=head(pl).config; env=head(pl).env; patternVarEnv = []; }, + lookup(n, head(pl).patternNamedSubPatternList)), + named) ++ + tail(pl), + cond, e, location=top.location); + + top.hasCondition = cond.isJust; +} + +concrete production patternList_one +top::PatternList ::= p::Pattern +{ + top.unparse = p.unparse; + + top.patternVars = p.patternVars; + p.patternVarEnv = top.patternVarEnv; + + top.patternList = [p]; +} +concrete production patternList_snoc +top::PatternList ::= ps::PatternList ',' p::Pattern +{ + top.unparse = ps.unparse ++ ", " ++ p.unparse; + + forwards to appendPatternList(ps, patternList_one(p, location=p.location)); +} +abstract production patternList_more +top::PatternList ::= p::Pattern ',' ps1::PatternList +{ + top.unparse = p.unparse ++ (if ps1.unparse == "" then "" else ", " ++ ps1.unparse); + + top.patternVars = p.patternVars ++ ps1.patternVars; + ps1.patternVarEnv = p.patternVarEnv ++ p.patternVars; + + top.patternList = p :: ps1.patternList; +} + +-- lol, dangling comma bug TODO +concrete production patternList_nil +top::PatternList ::= +{ + top.unparse = ""; + + top.patternVars = []; + top.patternList = []; +} + +---------------------------------------------------- +-- Added Functions +---------------------------------------------------- +function appendPatternList +PatternList ::= p1::PatternList p2::PatternList +{ + return + case p1 of + | patternList_more(h, _, t) -> + patternList_more(h, ',', appendPatternList(t, p2), location=p1.location) + | patternList_one(h) -> + patternList_more(h, ',', p2, location=p1.location) + | patternList_nil() -> p2 + end; +} + +function patternListVars +Name ::= p::Decorated Pattern +{ + local n :: String = + case p of + | varPattern(pvn) -> "__sv_pv_" ++ toString(genInt()) ++ "_" ++ pvn.name + | h -> "__sv_tmp_pv_" ++ toString(genInt()) + end; + return name(n, p.location); +} +function convStringsToVarBinders +VarBinders ::= s::[Name] l::Location +{ + return if null(s) then nilVarBinder(location=l) + else if null(tail(s)) then oneVarBinder(varVarBinder(head(s), location=head(s).location), location=l) + else consVarBinder(varVarBinder(head(s), location=head(s).location), ',', convStringsToVarBinders(tail(s), l), location=l); +} +function exprFromName +Expr ::= n::Name +{ + return baseExpr(qNameId(n, location=n.location), location=n.location); +} + +function foldPrimPatterns +PrimPatterns ::= l::[PrimPattern] +{ + return if null(tail(l)) then onePattern(head(l), location=head(l).location) + else consPattern(head(l), '|', foldPrimPatterns(tail(l)), location=head(l).location); +} + +{-- + - Remove the first pattern from the rule, and put a let binding of it into + - the expression. + - + - Would like to make this an attribute instead of a function, but + - (a) we don't have lambdas yet, and the attr would need to be a function value + - (b) we don't have a nice way of applying to all element of a list of functions + - e.g. right now we 'map(this(x, y, _), list)' + -} +function bindHeadPattern +AbstractMatchRule ::= headExpr::Expr headType::Type absRule::AbstractMatchRule +{ + -- If it's '_' we do nothing, otherwise, bind away! + return case absRule of + | matchRule(headPat :: restPat, cond, e) -> + case headPat.patternVariableName of + | just(pvn) -> + matchRule( + restPat, + case cond of + | just(pair(c, p)) -> just(pair(makeLet(absRule.location, pvn, headType, headExpr, c), p)) + | nothing() -> nothing() + end, + makeLet(absRule.location, pvn, headType, headExpr, e), + location=absRule.location) + | nothing() -> matchRule(restPat, cond, e, location=absRule.location) + end + | r -> r -- Don't crash when we see a rule with too few patterns (should be an error) + end; +} + +{- + - Drop the first pattern from a match rule +-} +function dropHeadPattern +AbstractMatchRule ::= absRule::AbstractMatchRule +{ + return case absRule of + | matchRule(headPat :: restPat, cond, e) -> + matchRule(restPat, cond, e, location=absRule.location) + | r -> r -- Don't crash when we see a rule with too few patterns (should be an error) + end; +} + +function makeLet +Expr ::= l::Location s::String t::Type e::Expr o::Expr +{ + return letp( + assignExpr( + name(s, l), '::', typerepTypeExpr(t, location=l), '=', e, location=l), + o, location=l); +} + +function ensureDecoratedExpr +Expr ::= e::PartiallyDecorated Expr +{ + local et :: Type = performSubstitution(e.typerep, e.upSubst); + local eRef :: Expr = exprRef(e, location=e.location); + + return if isDecorable(et, e.env) + then decorateExprWithEmpty('decorate', eRef, 'with', '{', '}', location=e.location) + else eRef; +} + +instance Eq AbstractMatchRule { + eq = \ a::AbstractMatchRule b::AbstractMatchRule -> + a.headPattern.patternSortKey == b.headPattern.patternSortKey; +} +instance Ord AbstractMatchRule { + lte = \ a::AbstractMatchRule b::AbstractMatchRule -> + a.headPattern.patternSortKey <= b.headPattern.patternSortKey; +} +{-- + - Given a list of match rules, examine the "head pattern" of each. + - Sort and group by the key of this head pattern. + - + - i.e. [cons, nil, cons] becomes [[cons, cons], [nil]] (where 'cons' is the key of the head pattern) + -} +function groupMRules +[[AbstractMatchRule]] ::= l::[AbstractMatchRule] +{ + return group(sort(l)); +} + +{-- + - Given a list of match rules, which are presumed to match empty + - patterns (this is not checked), turn them into nested + - conditionals. + -} +function buildMatchWhenConditionals +Expr ::= ml::[AbstractMatchRule] failExpr::Expr +{ + return + case ml of + | matchRule(_, just(pair(c, nothing())), e) :: tl -> + Silver_Expr { + if $Expr{c} + then $Expr{e} + else $Expr{buildMatchWhenConditionals(tl, failExpr)} + } + | matchRule(_, just(pair(c, just(p))), e) :: tl -> + Silver_Expr { + case $Expr{c} of + | $Pattern{p} -> $Expr{e} + | _ -> $Expr{buildMatchWhenConditionals(tl, failExpr)} + end + } + | matchRule(_, nothing(), e) :: tl -> e + | [] -> failExpr + end; +} + +{-- + - Check whether there are patterns that overlap in a list of match + - rules which are presumed to match empty patterns (this is not + - checked here). + - + - An answer of true definitively means there are useless patterns. + - An answer of false means there may be, but it would require + - analysis of the conditions on the patterns to determine whether + - they are actually useless. We do not do that. + -} +function areUselessPatterns +Boolean ::= ml::[AbstractMatchRule] +{ + return + case ml of + | matchRule(_, just(_), _) :: tl -> + areUselessPatterns(tl) + | matchRule(_, nothing(), _) :: _ :: _ -> true + | matchRule(_, nothing(), _) :: [] -> false + | [] -> false + end; +} + + diff --git a/grammars/silver/compiler/extension/patternmatching/PatternTypes.sv b/grammars/silver/compiler/extension/patternmatching/PatternTypes.sv new file mode 100644 index 000000000..0694c431b --- /dev/null +++ b/grammars/silver/compiler/extension/patternmatching/PatternTypes.sv @@ -0,0 +1,412 @@ +grammar silver:compiler:extension:patternmatching; + +import silver:compiler:modification:list only LSqr_t, RSqr_t; + +{-- + - The forms of syntactic patterns that are permissible in (nested) case expresssions. + -} +nonterminal Pattern with location, config, unparse, env, frame, errors, patternVars, patternVarEnv, patternIsVariable, patternVariableName, patternSubPatternList, patternNamedSubPatternList, patternSortKey, isPrimitivePattern, isBoolPattern, isListPattern, patternTypeName; + +{-- + - The names of all var patterns in the pattern. + -} +synthesized attribute patternVars :: [String]; +{-- + - The names of all var patterns seen so far. + -} +autocopy attribute patternVarEnv :: [String]; +{-- + - False if it actually matches anything specific, true if it's a variable/wildcard. + -} +synthesized attribute patternIsVariable :: Boolean; +{-- + - The name of the variable, if any (e.g. wildcards!) + -} +synthesized attribute patternVariableName :: Maybe; +{-- + - Each positional child pattern below this one. + -} +synthesized attribute patternSubPatternList :: [Decorated Pattern]; +{-- + - Each named child pattern below this one. + -} +synthesized attribute patternNamedSubPatternList :: [Pair]; +{-- + - The sort (and grouping) key of the pattern. + - "~var" if patternIsVariable is true. (TODO: actually, we should call it undefined! It's not used.) + - fullname if it's a production. + - otherwise, it is type-dependent, but same values should be the same! + -} +synthesized attribute patternSortKey :: String; + + +{- + For completeness checking: +-} + +--For non-nonterminal patterns, which generally require a default case for completeness +synthesized attribute isPrimitivePattern::Boolean; + +--We need a special case for Booleans, since they can be complete +-- without variables but are not nonterminals +synthesized attribute isBoolPattern::Boolean; + +--Lists occupy a place somewhere between nonterminals and primitives, +-- so we'll handle them specially +synthesized attribute isListPattern::Boolean; + +--The fully-qualified name of the type being built +synthesized attribute patternTypeName::String; + + +-- These are the "canonical" patterns: + +{-- + - Match on a production, extracting children as patterns, too. + - The production name may be qualified. + - TODO: if not qualified filter down to productions on scruntinee type + -} +concrete production prodAppPattern_named +top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' +{ + top.unparse = prod.unparse ++ "(" ++ ps.unparse ++ ")"; + top.errors := ps.errors ++ nps.errors; + + local parms :: Integer = prod.lookupValue.typeScheme.arity; + + top.errors <- + if null(prod.lookupValue.dcls) || length(ps.patternList) == parms then [] + else [err(prod.location, prod.name ++ " has " ++ toString(parms) ++ " parameters but " ++ toString(length(ps.patternList)) ++ " patterns were provided")]; + + top.patternVars = ps.patternVars ++ nps.patternVars; + nps.patternVarEnv = ps.patternVarEnv ++ ps.patternVars; + + top.patternIsVariable = false; + top.patternVariableName = nothing(); + top.patternSubPatternList = ps.patternList; + top.patternNamedSubPatternList = nps.namedPatternList; + top.patternSortKey = prod.lookupValue.fullName; + + top.isPrimitivePattern = false; + top.isBoolPattern = false; + top.isListPattern = false; + top.patternTypeName = prod.lookupValue.typeScheme.typerep.outputType.baseType.typeName; +} + +concrete production prodAppPattern +top::Pattern ::= prod::QName '(' ps::PatternList ')' +{ + forwards to prodAppPattern_named(prod, '(', ps, ',', namedPatternList_nil(location=top.location), ')', location=top.location); +} + +concrete production propAppPattern_onlyNamed +top::Pattern ::= prod::QName '(' nps::NamedPatternList ')' +{ + forwards to prodAppPattern_named(prod, '(', patternList_nil(location=top.location), ',', nps, ')', location=top.location); +} + +{-- + - Match anything, and bind nothing. + -} +concrete production wildcPattern +top::Pattern ::= '_' +{ + top.unparse = "_"; + top.errors := []; + + top.patternVars = []; + top.patternIsVariable = true; + top.patternVariableName = nothing(); + top.patternSubPatternList = []; + top.patternSortKey = "~var"; + + --This might be used with these kinds of patterns, but it isn't one itself + top.isPrimitivePattern = false; + top.isBoolPattern = false; + top.isListPattern = false; +} + +{-- + - Match anything, bind it to a name. + - Note 1: must be lower case, to avoid newbie confusions "case (e::a) of Expr -> ..." + - Note 2: must not shadow a production name, to avoid "forgot parens" confusion + - (e.g. "case maybe of nothing -> ..." vs "nothing()") + -} +concrete production varPattern +top::Pattern ::= v::Name +{ + top.unparse = v.name; + top.errors := []; + top.errors <- + if contains(v.name, top.patternVarEnv) + then [err(v.location, "Duplicate pattern variable " ++ v.name)] + else []; + top.errors <- + if isUpper(substring(0,1,v.name)) + then [err(v.location, "Pattern variable names start with a lower case letter")] + else []; + top.errors <- + case getValueDcl(v.name, top.env) of + | prodDcl(_,_) :: _ -> + [err(v.location, "Pattern variables should not share the name of a production. (Potential confusion between '" ++ v.name ++ "' and '" ++ v.name ++ "()')")] + | _ -> [] + end; + + top.patternVars = [v.name]; + top.patternIsVariable = true; + top.patternVariableName = just(v.name); + top.patternSubPatternList = []; + top.patternSortKey = "~var"; + + --This might be used with these kinds of patterns, but it isn't one itself + top.isPrimitivePattern = false; + top.isBoolPattern = false; + top.isListPattern = false; +} + +{-- + - For other extensions to pattern matching. Basically acts like a wildcard. + -} +abstract production errorPattern +top::Pattern ::= msg::[Message] +{ + top.unparse = s"{-${messagesToString(msg)}-}"; + top.errors := msg; + + top.patternVars = []; + top.patternIsVariable = true; + top.patternVariableName = nothing(); + top.patternSubPatternList = []; + top.patternSortKey = "error"; + + top.isPrimitivePattern = false; + top.isBoolPattern = false; + top.isListPattern = false; +} + +aspect default production +top::Pattern ::= +{ + -- All other patterns should never set these to anything else, so let's default them. + top.patternIsVariable = false; + top.patternVariableName = nothing(); + top.patternNamedSubPatternList = []; + + --This should only be accessed on production patterns + top.patternTypeName = ""; +} + +-- arbitrary nesting of patterns +concrete production nestedPatterns +top::Pattern ::= '(' p::Pattern ')' +{ + top.unparse = s"(${p.unparse})"; + forwards to p; +} + +-------------------------------------------------------------------------------- + +-- Below are the non-canonical patterns, i.e. those for other types + +concrete production intPattern +top::Pattern ::= num::Int_t +{ + top.unparse = num.lexeme; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = num.lexeme; + + top.isPrimitivePattern = true; + top.isBoolPattern = false; + top.isListPattern = false; +} + +concrete production fltPattern +top::Pattern ::= num::Float_t +{ + top.unparse = num.lexeme; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = num.lexeme; + + top.isPrimitivePattern = true; + top.isBoolPattern = false; + top.isListPattern = false; +} + +concrete production strPattern +top::Pattern ::= str::String_t +{ + top.unparse = str.lexeme; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = str.lexeme; + + top.isPrimitivePattern = true; + top.isBoolPattern = false; + top.isListPattern = false; +} + +concrete production truePattern +top::Pattern ::= 'true' +{ + top.unparse = "true"; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = "true"; + + top.isPrimitivePattern = true; + top.isBoolPattern = true; + top.isListPattern = false; +} + +concrete production falsePattern +top::Pattern ::= 'false' +{ + top.unparse = "false"; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = "false"; + + top.isPrimitivePattern = true; + top.isBoolPattern = true; + top.isListPattern = false; +} + +abstract production nilListPattern +top::Pattern ::= '[' ']' +{ + top.unparse = "[]"; + top.errors := []; + + top.patternVars = []; + top.patternSubPatternList = []; + top.patternSortKey = "silver:core:nil"; + + top.isPrimitivePattern = true; + top.isBoolPattern = false; + top.isListPattern = true; +} + +concrete production consListPattern +top::Pattern ::= hp::Pattern '::' tp::Pattern +{ + top.unparse = hp.unparse ++ "::" ++ tp.unparse; + top.errors := hp.errors ++ tp.errors; + + top.patternVars = hp.patternVars ++ tp.patternVars; + hp.patternVarEnv = top.patternVarEnv; + tp.patternVarEnv = hp.patternVarEnv ++ hp.patternVars; + + top.patternSubPatternList = [hp, tp]; + top.patternSortKey = "silver:core:cons"; + + top.isPrimitivePattern = true; + top.isBoolPattern = false; + top.isListPattern = true; +} + +-- List literal patterns +concrete production listPattern +top::Pattern ::= '[' ps::PatternList ']' +{ + top.unparse = s"[${ps.unparse}]"; + forwards to ps.asListPattern; +} + +synthesized attribute asListPattern::Pattern occurs on PatternList; + +aspect production patternList_one +top::PatternList ::= p::Pattern +{ + top.asListPattern = + consListPattern(p, '::', nilListPattern('[', ']', location=top.location), location=top.location); +} +aspect production patternList_more +top::PatternList ::= p::Pattern ',' ps1::PatternList +{ + top.asListPattern = consListPattern(p, '::', ps1.asListPattern, location=top.location); +} +aspect production patternList_nil +top::PatternList ::= +{ + top.asListPattern = nilListPattern('[', ']', location=top.location); +} + +synthesized attribute namedPatternList::[Pair]; + +nonterminal NamedPatternList with location, config, unparse, frame, env, errors, patternVars, patternVarEnv, namedPatternList; + +concrete production namedPatternList_one +top::NamedPatternList ::= p::NamedPattern +{ + top.unparse = p.unparse; + top.errors := p.errors; + + top.patternVars = p.patternVars; + top.namedPatternList = p.namedPatternList; +} +concrete production namedPatternList_more +top::NamedPatternList ::= p::NamedPattern ',' ps::NamedPatternList +{ + top.unparse = p.unparse ++ ", " ++ ps.unparse; + top.errors := p.errors ++ ps.errors; + + top.patternVars = p.patternVars ++ ps.patternVars; + ps.patternVarEnv = p.patternVarEnv ++ p.patternVars; + top.namedPatternList = p.namedPatternList ++ ps.namedPatternList; +} + +-- Abstract only to avoid parse conflict +abstract production namedPatternList_nil +top::NamedPatternList ::= +{ + top.unparse = ""; + top.errors := []; + + top.patternVars = []; + top.namedPatternList = []; +} + +nonterminal NamedPattern with location, config, unparse, frame, env, errors, patternVars, patternVarEnv, namedPatternList; + +concrete production namedPattern +top::NamedPattern ::= qn::QName '=' p::Pattern +{ + top.unparse = s"${qn.unparse}=${p.unparse}"; + top.errors := p.errors; + + -- TODO: Error checking for annotation patterns is a bit broken. + -- We can check that it is an annotation here, but any other + -- errors will show up in the generated code, potentially in the wrong + -- (or more than one) place. + top.errors <- + if qn.lookupAttribute.found && !qn.lookupAttribute.dcl.isAnnotation + then [err(qn.location, s"${qn.name} is not an annotation")] + else []; + + top.patternVars = p.patternVars; + top.namedPatternList = [pair(qn.lookupAttribute.fullName, p)]; +} + +--helper function for building patternLists from lists of patterns +function buildPatternList +PatternList ::= plst::[Pattern] loc::Location +{ + return case plst of + | [] -> patternList_nil(location=loc) + | h::t -> + patternList_more(h, ',', buildPatternList(t, loc), location=loc) + end; +} + diff --git a/grammars/silver/compiler/extension/regex/Regex.sv b/grammars/silver/compiler/extension/regex/Regex.sv new file mode 100644 index 000000000..62cfefb3a --- /dev/null +++ b/grammars/silver/compiler/extension/regex/Regex.sv @@ -0,0 +1,35 @@ +grammar silver:compiler:extension:regex; + +import silver:util:treeset as ts; +import silver:compiler:definition:core; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:env; +import silver:compiler:metatranslation; +import silver:reflect; +import silver:regex:concrete_syntax; + +terminal MatchesOp_t '=~' precedence = 9, association = left, lexer classes OP; + +concrete production literalRegex +top::Expr ::= RegexSlash_t reg::Regex RegexSlash_t +layout {} +{ + top.unparse = s"/${reg.unparse}/"; + propagate freeVars; + forwards to + if null(getTypeDcl("silver:regex:Regex", top.env)) + then errorExpr([err(top.location, "Use of regexes requires import of silver:regex")], location=top.location) + else translate(top.location, reflect(reg.ast)); +} + +concrete production matches +top::Expr ::= e::Expr '=~' r::Expr +{ + top.unparse = s"(${e.unparse}) =~ (${r.unparse})"; + propagate freeVars; + forwards to + if null(getValueDcl("silver:regex:matches", top.env)) + then errorExpr([err(top.location, "Use of regexes requires import of silver:regex")], location=top.location) + else Silver_Expr { silver:regex:matches($Expr{r}, $Expr{e}) }; +} + diff --git a/grammars/silver/compiler/extension/rewriting/Expr.sv b/grammars/silver/compiler/extension/rewriting/Expr.sv new file mode 100644 index 000000000..48514baa7 --- /dev/null +++ b/grammars/silver/compiler/extension/rewriting/Expr.sv @@ -0,0 +1,791 @@ +grammar silver:compiler:extension:rewriting; + +-- Environment mapping variables that were defined on the rule RHS to Booleans indicating whether +-- the variable was explicitly (i.e. not implicitly) decorated in the pattern. +inherited attribute boundVars::[Pair] occurs on Expr, Exprs, ExprInhs, ExprInh, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr, AssignExpr, PrimPatterns, PrimPattern; +propagate boundVars on Expr, Exprs, ExprInhs, ExprInh, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr, AssignExpr, PrimPatterns, PrimPattern + excluding baseExpr, application, access, letp, prodPatternNormal, prodPatternGadt; + +attribute transform occurs on Expr; + +synthesized attribute decRuleExprs::[(String, Decorated Expr with {decorate, boundVars})] occurs on Expr, AssignExpr, PrimPatterns, PrimPattern; + +aspect default production +top::Expr ::= +{ + top.transform = + antiquoteASTExpr(Silver_Expr { + -- Constrain the type of the wrapped expression to the type that was inferred here, + -- to allow for any type class constraints to be resolved in the translation. + silver:rewrite:anyASTExpr( + let rewrite_rule_anyAST_val__::$TypeExpr{typerepTypeExpr(finalType(top), location=top.location)} = $Expr{top} + in rewrite_rule_anyAST_val__ + end) + }); + top.decRuleExprs = []; -- Only needed on things resulting from the translation of caseExpr +} + +aspect production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName _ _ +{ + -- In regular pattern matching nonterminal values are always effectively decorated, but we are + -- using the same typing behavior while matching on *undecorated* trees. So when a variable is + -- referenced as decorated we must seperately handle the cases of when it was already decorated + -- or was decorated implicitly (in which case we need to explicitly decorate it here.) The same + -- problem exists when dealing with implicit undecoration. + top.transform = + case lookup(q.name, top.boundVars) of + | just(bindingIsDecorated) -> + -- The variable is bound in the rule + if finalType(top).isDecorated && !bindingIsDecorated + then + -- We want the decorated version, but the bound value is undecorated + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalType(top).decoratedType, location=builtin)} -> + $Expr{ + decorateExprWithEmpty( + 'decorate', Silver_Expr { e }, 'with', '{', '}', + location=top.location)}) + }), + consASTExpr(varASTExpr(q.name), nilASTExpr()), + nilNamedASTExpr()) + else if isDecorable(finalType(top), top.env) && bindingIsDecorated + -- We want the undecorated version, but the bound value is decorated + then + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr(silver:core:new) + }), + consASTExpr(varASTExpr(q.name), nilASTExpr()), + nilNamedASTExpr()) + -- Both (or neither) the bound value/desired type is a decorated nonterminal - just return the value + else varASTExpr(q.name) + | nothing() -> + -- The variable is bound in an enclosing let/match + -- Explicitly undecorate the variable, if appropriate for the final expected type + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) && !finalType(top).isDecorated + then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(silver:core:new($Expr{top})) }) + else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }) + end; +} + +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.transform = + -- Explicitly undecorate the variable, if appropriate for the final expected type + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) && !finalType(top).isDecorated + then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(silver:core:new($Expr{top})) }) + else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); +} + +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.transform = + -- Explicitly undecorate the variable, if appropriate for the final expected type + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) && !finalType(top).isDecorated + then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(silver:core:new($Expr{top})) }) + else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); +} + +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.transform = + -- Explicitly undecorate the variable, if appropriate for the final expected type + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) && !finalType(top).isDecorated + then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(silver:core:new($Expr{top})) }) + else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); +} + +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.transform = + -- Explicitly undecorate the variable, if appropriate for the final expected type + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) && !finalType(top).isDecorated + then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(silver:core:new($Expr{top})) }) + else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); +} + +aspect production errorApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.transform = applyASTExpr(e.transform, es.transform, anns.transform); + e.boundVars = top.boundVars; + es.boundVars = top.boundVars; + anns.boundVars = top.boundVars; +} + +aspect production functionInvocation +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.transform = + case e, es of + | productionReference(q), _ -> prodCallASTExpr(q.lookupValue.fullName, es.transform, anns.transform) + + -- Special cases for efficiency + | classMemberReference(q), snocAppExprs(oneAppExprs(presentAppExpr(e1)), _, presentAppExpr(e2)) -> + if q.lookupValue.fullName == "silver:core:eq" + then eqeqASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:neq" + then neqASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:lt" + then ltASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:lte" + then lteqASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:gt" + then gtASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:gte" + then gteqASTExpr(e1.transform, e2.transform) + else if q.lookupValue.fullName == "silver:core:append" + then appendASTExpr(e1.transform, e2.transform) + else applyASTExpr(e.transform, es.transform, anns.transform) + | classMemberReference(q), oneAppExprs(presentAppExpr(e)) -> + if q.lookupValue.fullName == "silver:core:toString" + then toStringASTExpr(e.transform) + else if q.lookupValue.fullName == "silver:core:toInteger" + then toIntegerASTExpr(e.transform) + else if q.lookupValue.fullName == "silver:core:toFloat" + then toFloatASTExpr(e.transform) + else if q.lookupValue.fullName == "silver:core:toBoolean" + then toBooleanASTExpr(e.transform) + else if q.lookupValue.fullName == "silver:core:length" + then lengthASTExpr(e.transform) + else applyASTExpr(e.transform, es.transform, anns.transform) + + | _, _ -> applyASTExpr(e.transform, es.transform, anns.transform) + end; + e.boundVars = top.boundVars; + es.boundVars = top.boundVars; + anns.boundVars = top.boundVars; +} + +aspect production partialApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs anns::PartiallyDecorated AnnoAppExprs +{ + top.transform = applyASTExpr(e.transform, es.transform, anns.transform); + e.boundVars = top.boundVars; + es.boundVars = top.boundVars; + anns.boundVars = top.boundVars; +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + -- Flow analysis has no way to track what e is decorated with across reflect/reify, + -- so if the inh set is unspecialized, assume that it has the reference set. + local finalTy::Type = + case finalType(e) of + | decoratedType(nt, varType(_)) -> + decoratedType(nt, inhSetType(sort(concat(getInhsForNtRef(nt.typeName, top.flowEnv))))) + | t -> t + end; + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalTy, location=builtin)} -> e.forward) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); +} + +aspect production errorAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); + e.boundVars = top.boundVars; +} + +aspect production annoAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); + e.boundVars = top.boundVars; +} + +aspect production terminalAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); + e.boundVars = top.boundVars; +} + + +aspect production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- Flow analysis has no way to track what e is decorated with across reflect/reify, + -- so if the inh set is unspecialized, assume that it has the reference set. + local finalTy::Type = + case finalType(e) of + | decoratedType(nt, varType(_)) -> + decoratedType(nt, inhSetType(sort(concat(getInhsForNtRef(nt.typeName, top.flowEnv))))) + | t -> t + end; + top.transform = + case e of + -- Special cases to avoid introducing a reference and causing flow errors. + | decorateExprWith(_, eUndec, _, _, inh, _) -> + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + $Expr{ + lambdap( + productionRHSCons( + productionRHSElem( + name("_e", builtin), '::', + typerepTypeExpr(finalType(eUndec), location=builtin), + location=builtin), + inh.lambdaParams, + location=builtin), + Silver_Expr { + $Expr{ + decorateExprWith( + 'decorate', baseExpr(qName(builtin, "_e"), location=builtin), + 'with', '{', inh.bodyExprInhTransform, '}', + location=builtin)}.$qName{q.name} + }, + location=builtin)}) + }), + consASTExpr(eUndec.transform, inh.transform), + nilNamedASTExpr()) + | lexicalLocalReference(qn, _, _) when + case lookup(qn.name, top.boundVars) of + | just(bindingIsDecorated) -> !bindingIsDecorated + | nothing() -> false + end -> + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalType(e).decoratedType, location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(varASTExpr(qn.name), nilASTExpr()), + nilNamedASTExpr()) + | _ -> + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalTy, location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()) + end; + e.boundVars = top.boundVars; +} + +aspect production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- Flow analysis has no way to track what e is decorated with across reflect/reify, + -- so if the inh set is unspecialized, assume that it has the reference set. + local finalTy::Type = + case finalType(e) of + | decoratedType(nt, varType(_)) -> + decoratedType(nt, inhSetType(sort(concat(getInhsForNtRef(nt.typeName, top.flowEnv))))) + | t -> t + end; + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalTy, location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); + e.boundVars = top.boundVars; +} + +aspect production errorDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- Flow analysis has no way to track what e is decorated with across reflect/reify, + -- so if the inh set is unspecialized, assume that it has the reference set. + local finalTy::Type = + case finalType(e) of + | decoratedType(nt, varType(_)) -> + decoratedType(nt, inhSetType(sort(concat(getInhsForNtRef(nt.typeName, top.flowEnv))))) + | t -> t + end; + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ e::$TypeExpr{typerepTypeExpr(finalTy, location=builtin)} -> e.$qName{q.name}) + }), + consASTExpr(e.transform, nilASTExpr()), + nilNamedASTExpr()); + e.boundVars = top.boundVars; +} + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + $Expr{ + lambdap( + productionRHSCons( + productionRHSElem( + name("_e", builtin), '::', + typerepTypeExpr(finalType(e), location=builtin), + location=builtin), + inh.lambdaParams, + location=builtin), + decorateExprWith( + 'decorate', baseExpr(qName(builtin, "_e"), location=builtin), + 'with', '{', inh.bodyExprInhTransform, '}', + location=builtin), + location=builtin)}) + }), + consASTExpr(e.transform, inh.transform), + nilNamedASTExpr()); +} + +attribute transform occurs on ExprInhs; +synthesized attribute lambdaParams::ProductionRHS occurs on ExprInhs; +functor attribute bodyExprInhTransform occurs on ExprInhs, ExprInh; +propagate bodyExprInhTransform on ExprInhs; + +aspect production exprInhsEmpty +top::ExprInhs ::= +{ + top.transform = nilASTExpr(); + top.lambdaParams = productionRHSNil(location=builtin); +} + +aspect production exprInhsOne +top::ExprInhs ::= lhs::ExprInh +{ + top.transform = consASTExpr(lhs.transform, nilASTExpr()); + top.lambdaParams = + productionRHSCons(lhs.lambdaParam, productionRHSNil(location=builtin), location=builtin); +} + +aspect production exprInhsCons +top::ExprInhs ::= lhs::ExprInh inh::ExprInhs +{ + top.transform = consASTExpr(lhs.transform, inh.transform); + top.lambdaParams = productionRHSCons(lhs.lambdaParam, inh.lambdaParams, location=builtin); +} + +attribute transform occurs on ExprInh; +synthesized attribute lambdaParam::ProductionRHSElem occurs on ExprInh; + +aspect production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' +{ + top.transform = e.transform; + + local paramName::String = implode("_", explode(":", lhs.name)); + top.lambdaParam = + productionRHSElem( + name(paramName, builtin), '::', + typerepTypeExpr(finalType(e), location=builtin), + location=builtin); + top.bodyExprInhTransform = + exprInh( + lhs, '=', baseExpr(qName(builtin, paramName), location=builtin), ';', + location=builtin); +} + +aspect production trueConst +top::Expr ::= 'true' +{ + top.transform = booleanASTExpr(true); +} + +aspect production falseConst +top::Expr ::= 'false' +{ + top.transform = booleanASTExpr(false); +} + +aspect production and +top::Expr ::= e1::Expr '&&' e2::Expr +{ + top.transform = andASTExpr(e1.transform, e2.transform); +} + +aspect production or +top::Expr ::= e1::Expr '||' e2::Expr +{ + top.transform = orASTExpr(e1.transform, e2.transform); +} + +aspect production notOp +top::Expr ::= '!' e::Expr +{ + top.transform = notASTExpr(e.transform); +} + +aspect production intConst +top::Expr ::= i::Int_t +{ + top.transform = integerASTExpr(toInteger(i.lexeme)); +} + +aspect production floatConst +top::Expr ::= f::Float_t +{ + top.transform = floatASTExpr(toFloat(f.lexeme)); +} + +aspect production noteAttachment +top::Expr ::= 'attachNote' note::Expr 'on' e::Expr 'end' +{ + top.transform = noteAttachmentASTExpr(note.transform, e.transform); +} + +aspect production plus +top::Expr ::= e1::Expr '+' e2::Expr +{ + top.transform = plusASTExpr(e1.transform, e2.transform); +} + +aspect production minus +top::Expr ::= e1::Expr '-' e2::Expr +{ + top.transform = minusASTExpr(e1.transform, e2.transform); +} + +aspect production multiply +top::Expr ::= e1::Expr '*' e2::Expr +{ + top.transform = multiplyASTExpr(e1.transform, e2.transform); +} + +aspect production divide +top::Expr ::= e1::Expr '/' e2::Expr +{ + top.transform = divideASTExpr(e1.transform, e2.transform); +} + +aspect production modulus +top::Expr ::= e1::Expr '%' e2::Expr +{ + top.transform = modulusASTExpr(e1.transform, e2.transform); +} + +aspect production neg +top::Expr ::= '-' e::Expr +{ + top.transform = negASTExpr(e.transform); +} + +aspect production stringConst +top::Expr ::= s::String_t +{ + top.transform = stringASTExpr(unescapeString(substring(1, length(s.lexeme) - 1, s.lexeme))); +} + +aspect production terminalConstructor +top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' +{ + top.transform = terminalASTExpr(t.typerep.typeName, es.transform, el.transform); +} + +aspect production ifThenElse +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr +{ + top.transform = ifThenElseASTExpr(e1.transform, e2.transform, e3.transform); + top.decRuleExprs = e1.decRuleExprs ++ e2.decRuleExprs ++ e3.decRuleExprs; +} + +-- Extensions +aspect production emptyList +top::Expr ::= '[' ']' +{ + top.transform = nilListASTExpr(); +} + +aspect production consListOp +top::Expr ::= h::Expr '::' t::Expr +{ + top.transform = + -- This is a forwarding prod, so we can't decorate e1 and e2 with boundVars here. + -- TODO: need some way for the flow analysis to track that e1 and e2 will be provided with boundVars through the forward. + case forward of + | functionInvocation(_, snocAppExprs(snocAppExprs(emptyAppExprs(), _, decH), _, decT), _) -> + consListASTExpr(decH.transform, decT.transform) + | _ -> error("Unexpected forward") + end; +} + +aspect production fullList +top::Expr ::= '[' es::Exprs ']' +{ + -- TODO: Consider refactoring listtrans on Exprs to decorate the expressions here + -- before forwarding via partially decorated references. + local decEs::Exprs = es; + decEs.downSubst = top.downSubst; + decEs.finalSubst = top.finalSubst; + decEs.frame = top.frame; + decEs.config = top.config; + decEs.compiledGrammars = top.compiledGrammars; + decEs.grammarName = top.grammarName; + decEs.env = top.env; + decEs.flowEnv = top.flowEnv; + decEs.boundVars = top.boundVars; + decEs.isRoot = top.isRoot; + decEs.originRules = top.originRules; + + top.transform = listASTExpr(decEs.transform); +} + +aspect production listPlusPlus +top::Expr ::= e1::PartiallyDecorated Expr e2::PartiallyDecorated Expr +{ + top.transform = + -- This is a forwarding prod, so we can't decorate e1 and e2 with boundVars here. + -- TODO: need some way for the flow analysis to track that e1 and e2 will be provided with boundVars through the forward. + case forward of + | functionInvocation(_, snocAppExprs(snocAppExprs(emptyAppExprs(), _, decE1), _, decE2), _) -> + appendASTExpr(decE1.transform, decE2.transform) + | _ -> error("Unexpected forward") + end; +} + +-- TODO: Awful hack to allow case to appear on rule RHS. +-- This is interfering (should really be defined on primitive match) +-- and only supports variables from the rule LHS appearing in the match expressions. +aspect production caseExpr_c +top::Expr ::= 'case' es::Exprs 'of' o::Opt_Vbar_t ml::MRuleList 'end' +{ + local decEs::Exprs = es; + decEs.downSubst = top.downSubst; + decEs.finalSubst = top.finalSubst; + decEs.frame = top.frame; + decEs.config = top.config; + decEs.compiledGrammars = top.compiledGrammars; + decEs.grammarName = top.grammarName; + decEs.env = top.env; + decEs.flowEnv = top.flowEnv; + decEs.boundVars = top.boundVars; + decEs.isRoot = top.isRoot; + decEs.originRules = top.originRules; + + top.transform = + applyASTExpr( + antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + $Expr{ + lambdap( + decEs.lambdaParams, + caseExpr_c( + 'case', decEs.lambdaParamRefs, 'of', + o, ml, 'end', + location=builtin), + location=builtin)}) + }), + decEs.transform, + nilNamedASTExpr()); +} + +-- Modifications +aspect production letp +top::Expr ::= la::AssignExpr e::Expr +{ + top.transform = letASTExpr(la.transform, e.transform); + top.decRuleExprs = la.decRuleExprs ++ e.decRuleExprs; + + la.boundVars = top.boundVars; + e.boundVars = top.boundVars ++ la.varBindings; +} + +attribute transform occurs on AssignExpr; +attribute varBindings occurs on AssignExpr; + +aspect production appendAssignExpr +top::AssignExpr ::= a1::AssignExpr a2::AssignExpr +{ + top.transform = appendNamedASTExprs(a1.transform, a2.transform); + top.varBindings = a1.varBindings ++ a2.varBindings; + top.decRuleExprs = a1.decRuleExprs ++ a2.decRuleExprs; +} + +aspect production assignExpr +top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr +{ + top.transform = + consNamedASTExpr(namedASTExpr(id.name, e.transform), nilNamedASTExpr()); + + -- If this is a generated pattern variable binding, figure out whether the corresponding + -- primitive pattern variable was implictly decorated. + local isDecorated::Boolean = + case e of + | lexicalLocalReference(qn, _, _) -> + fromMaybe(finalType(e).isDecorated, lookup(qn.name, top.boundVars)) + | _ -> finalType(e).isDecorated + end; + top.varBindings = [pair(id.name, isDecorated)]; + top.decRuleExprs = e.decRuleExprs; +} + +aspect production matchPrimitiveReal +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + top.decRuleExprs = e.decRuleExprs ++ pr.decRuleExprs ++ f.decRuleExprs; +} + +-- TODO: Support for lambdas capturing rule LHS variables + +-- Expr "collection" productions +attribute transform occurs on Exprs; +attribute lambdaParams occurs on Exprs; +synthesized attribute lambdaParamRefs::Exprs occurs on Exprs; + +aspect production exprsEmpty +top::Exprs ::= +{ + top.transform = nilASTExpr(); + top.lambdaParams = productionRHSNil(location=builtin); + top.lambdaParamRefs = exprsEmpty(location=builtin); +} +aspect production exprsSingle +top::Exprs ::= e::Expr +{ + top.transform = consASTExpr(e.transform, nilASTExpr()); + + local lambdaParamName::String = "__exprs_param_" ++ toString(genInt()); + top.lambdaParams = + productionRHSCons( + productionRHSElem( + name(lambdaParamName, builtin), '::', + typerepTypeExpr(finalType(e), location=builtin), + location=builtin), + productionRHSNil(location=builtin), + location=builtin); + top.lambdaParamRefs = + exprsSingle( + baseExpr(qName(builtin,lambdaParamName), location=builtin), + location=builtin); +} +aspect production exprsCons +top::Exprs ::= e1::Expr ',' e2::Exprs +{ + top.transform = consASTExpr(e1.transform, e2.transform); + + local lambdaParamName::String = "__exprs_param_" ++ toString(genInt()); + top.lambdaParams = + productionRHSCons( + productionRHSElem( + name(lambdaParamName, builtin), '::', + typerepTypeExpr(finalType(e1), location=builtin), + location=builtin), + e2.lambdaParams, + location=builtin); + top.lambdaParamRefs = + exprsCons( + baseExpr(qName(builtin, lambdaParamName), location=builtin), + ',', e2.lambdaParamRefs, + location=builtin); +} + +attribute transform occurs on AppExpr; + +aspect production missingAppExpr +top::AppExpr ::= '_' +{ + top.transform = missingArgASTExpr(); +} +aspect production presentAppExpr +top::AppExpr ::= e::Expr +{ + top.transform = e.transform; +} + +attribute transform occurs on AppExprs; + +aspect production snocAppExprs +top::AppExprs ::= es::AppExprs ',' e::AppExpr +{ + -- Inefficient, ugh. + top.transform = appendASTExprs(es.transform, consASTExpr(e.transform, nilASTExpr())); +} +aspect production oneAppExprs +top::AppExprs ::= e::AppExpr +{ + top.transform = consASTExpr(e.transform, nilASTExpr()); +} +aspect production emptyAppExprs +top::AppExprs ::= +{ + top.transform = nilASTExpr(); +} + +attribute transform occurs on AnnoExpr; + +aspect production annoExpr +top::AnnoExpr ::= qn::QName '=' e::AppExpr +{ + top.transform = namedASTExpr(qn.lookupAttribute.fullName, e.transform); +} + +attribute transform occurs on AnnoAppExprs; + +aspect production snocAnnoAppExprs +top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr +{ + -- Inefficient, ugh. + top.transform = appendNamedASTExprs(es.transform, consNamedASTExpr(e.transform, nilNamedASTExpr())); +} + +aspect production oneAnnoAppExprs +top::AnnoAppExprs ::= e::AnnoExpr +{ + top.transform = consNamedASTExpr(e.transform, nilNamedASTExpr()); +} + +aspect production emptyAnnoAppExprs +top::AnnoAppExprs ::= +{ + top.transform = nilNamedASTExpr(); +} + +aspect production exprRef +top::Expr ::= e::PartiallyDecorated Expr +{ + top.transform = e.transform; + e.boundVars = top.boundVars; +} diff --git a/grammars/silver/extension/rewriting/Pattern.sv b/grammars/silver/compiler/extension/rewriting/Pattern.sv similarity index 87% rename from grammars/silver/extension/rewriting/Pattern.sv rename to grammars/silver/compiler/extension/rewriting/Pattern.sv index cd8274dfb..f7b9d599f 100644 --- a/grammars/silver/extension/rewriting/Pattern.sv +++ b/grammars/silver/compiler/extension/rewriting/Pattern.sv @@ -1,4 +1,4 @@ -grammar silver:extension:rewriting; +grammar silver:compiler:extension:rewriting; synthesized attribute transform::a; @@ -33,11 +33,11 @@ attribute transform occurs on MRuleList, MatchRule; synthesized attribute isPolymorphic :: Boolean occurs on MRuleList, MatchRule, PatternList, Pattern, NamedPatternList, NamedPattern; inherited attribute typeHasUniversalVars :: Boolean occurs on Pattern; inherited attribute typesHaveUniversalVars :: [Boolean] occurs on PatternList; -autocopy attribute namedTypesHaveUniversalVars :: [Pair] occurs on NamedPatternList, NamedPattern; +autocopy attribute namedTypesHaveUniversalVars :: [(String, Boolean)] occurs on NamedPatternList, NamedPattern; synthesized attribute wrappedMatchRuleList :: [AbstractMatchRule] occurs on MRuleList, MatchRule; -autocopy attribute decRuleExprsIn::[Pair] occurs on MRuleList, MatchRule; +autocopy attribute decRuleExprsIn::[(String, Decorated Expr with {decorate, boundVars})] occurs on MRuleList, MatchRule; inherited attribute ruleIndex::Integer occurs on MRuleList, MatchRule; aspect production mRuleList_one @@ -63,11 +63,14 @@ aspect production matchRule_c top::MatchRule ::= pt::PatternList _ e::Expr { -- Awful hack: pattern match type checking is happens on the forward "primitive match". - -- However, we + -- However, we are translating on the pattern matching extension syntax, + -- so we need the Decorated Expr here. + -- Solution: extract the Decorated Exprs from the case expression and compute + -- the translation on them. top.transform = rewriteRule( pt.firstTransform, - case lookupBy(stringEq, toString(top.ruleIndex), top.decRuleExprsIn) of + case lookup(toString(top.ruleIndex), top.decRuleExprsIn) of | just(e) -> e.transform | nothing() -> error("Failed to find decorated RHS " ++ toString(top.ruleIndex)) end); @@ -88,13 +91,13 @@ top::MatchRule ::= pt::PatternList 'when' cond::Expr _ e::Expr top.transform = require( pt.firstTransform, - case lookupBy(stringEq, toString(top.ruleIndex) ++ "_cond", top.decRuleExprsIn) of + case lookup(toString(top.ruleIndex) ++ "_cond", top.decRuleExprsIn) of | just(e) -> e.transform | nothing() -> error("Failed to find decorated RHS " ++ toString(top.ruleIndex) ++ "_cond") end) <* rewriteRule( pt.firstTransform, - case lookupBy(stringEq, toString(top.ruleIndex), top.decRuleExprsIn) of + case lookup(toString(top.ruleIndex), top.decRuleExprsIn) of | just(e) -> e.transform | nothing() -> error("Failed to find decorated RHS " ++ toString(top.ruleIndex)) end); @@ -117,14 +120,14 @@ top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern _ e::E require( pt.firstTransform, matchASTExpr( - case lookupBy(stringEq, toString(top.ruleIndex) ++ "_cond", top.decRuleExprsIn) of + case lookup(toString(top.ruleIndex) ++ "_cond", top.decRuleExprsIn) of | just(e) -> e.transform | nothing() -> error("Failed to find decorated RHS " ++ toString(top.ruleIndex) ++ "_cond") end, p.transform, booleanASTExpr(true), booleanASTExpr(false))) <* rewriteRule( pt.firstTransform, - case lookupBy(stringEq, toString(top.ruleIndex), top.decRuleExprsIn) of + case lookup(toString(top.ruleIndex), top.decRuleExprsIn) of | just(e) -> e.transform | nothing() -> error("Failed to find decorated RHS " ++ toString(top.ruleIndex)) end); @@ -226,8 +229,12 @@ top::NamedPattern ::= qn::QName '=' p::Pattern top.isPolymorphic = p.isPolymorphic; p.typeHasUniversalVars = fromMaybe( - error("transform undefined in the presence of errors"), - lookupBy(stringEq, last(explode(":", qn.name)), top.namedTypesHaveUniversalVars)); + -- Should be an internal error, but error checking for annotation patterns is broken, + -- so we might demand a transform from a pattern that mentions an annotation that the + -- nonterminal type doesn't have. + -- See the comment on the silver:compiler:extension:patternmatching:namedPattern production. + false, + lookup(last(explode(":", qn.name)), top.namedTypesHaveUniversalVars)); } attribute transform occurs on Pattern; @@ -239,16 +246,17 @@ top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' prodCallASTPattern(prod.lookupValue.fullName, ps.transform, nps.transform); top.isPolymorphic = ps.isPolymorphic || nps.isPolymorphic; - local outputFreeVars::[TyVar] = prod.lookupValue.typerep.outputType.freeVariables; + local prodType::Type = prod.lookupValue.typeScheme.typerep; + local outputFreeVars::[TyVar] = prodType.outputType.freeVariables; ps.typesHaveUniversalVars = map( - \ t::Type -> !null(intersectBy(tyVarEqual, outputFreeVars, t.freeVariables)), - prod.lookupValue.typerep.inputTypes); + \ t::Type -> !null(intersect(outputFreeVars, t.freeVariables)), + prodType.inputTypes); nps.namedTypesHaveUniversalVars = map( - \ t::NamedArgType -> - pair(t.argName, !null(intersectBy(tyVarEqual, outputFreeVars, t.argType.freeVariables))), - prod.lookupValue.typerep.namedTypes); + \ t::Pair -> + pair(t.fst, !null(intersect(outputFreeVars, t.snd.freeVariables))), + prodType.namedTypes); } aspect production wildcPattern diff --git a/grammars/silver/compiler/extension/rewriting/Rewriting.sv b/grammars/silver/compiler/extension/rewriting/Rewriting.sv new file mode 100644 index 000000000..4a946451d --- /dev/null +++ b/grammars/silver/compiler/extension/rewriting/Rewriting.sv @@ -0,0 +1,289 @@ +grammar silver:compiler:extension:rewriting; + +imports silver:core hiding id; +imports silver:util:treeset as ts; + +imports silver:rewrite hiding repeat; +imports silver:compiler:metatranslation; +imports silver:langutil:pp; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:definition:env; +imports silver:compiler:translation:java:core only finalType; +imports silver:compiler:extension:patternmatching; +imports silver:compiler:modification:list; +imports silver:compiler:modification:primitivepattern; +imports silver:compiler:modification:lambda_fn; +imports silver:compiler:modification:let_fix; + +-- Note that these being infix operators means that this wouldn't pass the MDA, +-- despite being a Silver "extension". This could be fixed by refactoring the +-- Silver Expr grammar into an "ETF" style with seperate operator nonterminals. +terminal Sequence_t '<*' precedence = 12, association = left; -- Same as * +terminal Choice_t '<+' precedence = 11, association = left; -- Same as + + +concrete production sequenceOperator +top::Expr ::= s1::Expr '<*' s2::Expr +{ + top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; + forwards to mkStrFunctionInvocation(top.location, "silver:rewrite:sequence", [s1, s2]); +} + +concrete production choiceOperator +top::Expr ::= s1::Expr '<+' s2::Expr +{ + top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; + forwards to mkStrFunctionInvocation(top.location, "silver:rewrite:choice", [s1, s2]); +} + + +terminal Traverse_t 'traverse' lexer classes {KEYWORD, RESERVED}; + +concrete production traverseProdExprAnno +top::Expr ::= 'traverse' n::QName '(' es::AppExprs ',' anns::AnnoAppExprs ')' +{ + top.unparse = s"traverse ${n.name}(${es.unparse}, ${anns.unparse})"; + + local numChildren::Integer = n.lookupValue.typeScheme.arity; + local annotations::[String] = map(fst, n.lookupValue.typeScheme.typerep.namedTypes); + es.appExprTypereps = repeat(nonterminalType("silver:rewrite:Strategy", [], false), numChildren); + es.appExprApplied = n.unparse; + anns.appExprApplied = n.unparse; + anns.funcAnnotations = + map(pair(_, nonterminalType("silver:rewrite:Strategy", [], false)), annotations); + anns.remainingFuncAnnotations = anns.funcAnnotations; + + local localErrors::[Message] = + es.errors ++ anns.traverseErrors ++ + if null(getTypeDcl("silver:rewrite:Strategy", top.env)) + then [err(top.location, "Term rewriting requires import of silver:rewrite")] + else []; + + propagate downSubst, upSubst, freeVars; + + local transform::Strategy = + traversal(n.lookupValue.fullName, es.traverseTransform, anns.traverseTransform); + local fwrd::Expr = translate(builtin, reflect(new(transform))); + + forwards to if !null(localErrors) then errorExpr(localErrors, location=builtin) else fwrd; +} +concrete production traverseProdAnno +top::Expr ::= 'traverse' n::QName '(' anns::AnnoAppExprs ')' +{ + forwards to traverseProdExprAnno($1, n, $3, emptyAppExprs(location=$3.location), ',', anns, $5, location=top.location); +} +concrete production traverseProdExpr +top::Expr ::= 'traverse' n::QName '(' es::AppExprs ')' +{ + forwards to traverseProdExprAnno($1, n, $3, es, ',', emptyAnnoAppExprs(location=$4.location), $5, location=top.location); +} +concrete production traverseProdEmpty +top::Expr ::= 'traverse' n::QName '(' ')' +{ + forwards to traverseProdExprAnno($1, n, $3, emptyAppExprs(location=$3.location), ',', emptyAnnoAppExprs(location=$4.location), $4, location=top.location); +} + +abstract production traverseConsList +top::Expr ::= 'traverse' '(' h::AppExpr '::' t::AppExpr ')' +{ + top.unparse = s"traverse (${h.unparse} :: ${t.unparse})"; + + local transform::Strategy = consListCongruence(h.traverseTransform, t.traverseTransform); + forwards to translate(top.location, reflect(new(transform))); +} +concrete production traverseConsListFirstMissing +top::Expr ::= 'traverse' '(' h::'_' '::' t::AppExpr ')' +{ + forwards to traverseConsList($1, $2, missingAppExpr(h, location=h.location), $4, t, $6, location=top.location); +} +concrete production traverseConsListFirstPresent +top::Expr ::= 'traverse' '(' h::Expr '::' t::AppExpr ')' +{ + forwards to traverseConsList($1, $2, presentAppExpr(h, location=h.location), $4, t, $6, location=top.location); +} + +concrete production traverseNilList +top::Expr ::= 'traverse' '[' ']' +{ + top.unparse = s"traverse []"; + + local transform::Strategy = nilListCongruence(); + forwards to translate(top.location, reflect(new(transform))); +} + +concrete production traverseList +top::Expr ::= 'traverse' '[' es::AppExprs ']' +{ + top.unparse = s"traverse [${es.unparse}]"; + + local transform::Strategy = foldr(consListCongruence, nilListCongruence(), es.traverseTransform); + forwards to translate(top.location, reflect(new(transform))); +} + +-- Compute our own errors on AnnoAppExprs, since we want to ignore missing annotations (like in patterns) +synthesized attribute traverseErrors::[Message] occurs on AnnoAppExprs, AnnoExpr; +synthesized attribute traverseTransform::a; +attribute traverseTransform occurs on AppExpr; +attribute traverseTransform> occurs on AnnoExpr; +attribute traverseTransform<[Strategy]> occurs on AppExprs; +attribute traverseTransform<[Pair]> occurs on AnnoAppExprs; + +aspect production missingAppExpr +top::AppExpr ::= '_' +{ + top.traverseTransform = id(); +} +aspect production presentAppExpr +top::AppExpr ::= e::Expr +{ + top.traverseTransform = antiquoteStrategy(e); +} + +aspect production snocAppExprs +top::AppExprs ::= es::AppExprs ',' e::AppExpr +{ + top.traverseTransform = es.traverseTransform ++ [e.traverseTransform]; +} +aspect production oneAppExprs +top::AppExprs ::= e::AppExpr +{ + top.traverseTransform = [e.traverseTransform]; +} +aspect production emptyAppExprs +top::AppExprs ::= +{ + top.traverseTransform = []; +} + +aspect production annoExpr +top::AnnoExpr ::= qn::QName '=' e::AppExpr +{ + top.traverseErrors = + e.errors ++ + if !extractNamedArg(qn.name, top.funcAnnotations).fst.isJust + then [err(qn.location, "Named parameter '" ++ qn.name ++ "' is not appropriate for '" ++ top.appExprApplied ++ "'")] + else []; + top.traverseTransform = pair(qn.lookupAttribute.fullName, e.traverseTransform); +} + +aspect production snocAnnoAppExprs +top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr +{ + top.traverseErrors = es.traverseErrors ++ e.traverseErrors; + top.traverseTransform = es.traverseTransform ++ [e.traverseTransform]; +} +aspect production oneAnnoAppExprs +top::AnnoAppExprs ::= e::AnnoExpr +{ + top.traverseErrors = e.traverseErrors; + top.traverseTransform = [e.traverseTransform]; +} +aspect production emptyAnnoAppExprs +top::AnnoAppExprs ::= +{ + top.traverseErrors = []; + top.traverseTransform = []; +} + +terminal Rule_t 'rule' lexer classes {KEYWORD, RESERVED}; + +concrete production ruleExpr +top::Expr ::= 'rule' 'on' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' +{ + top.unparse = "rule on " ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; + propagate freeVars; + + -- Find the free type variables (i.e. lacking a definition) to add as skolem constants + local freeTyVars::[String] = + filter(\ tv::String -> null(getTypeDcl(tv, top.env)), nub(ty.lexicalTypeVariables)); + ty.env = newScopeEnv(addNewLexicalTyVars(top.grammarName, ty.location, [], freeTyVars), top.env); + + -- Pattern matching error checking (mostly) happens on what caseExpr forwards to, + -- so we need to decorate one of those here. + local checkExpr::Expr = + caseExpr( + [hackExprType(ty.typerep, location=builtin)], + ml.wrappedMatchRuleList, false, + errorExpr([], location=builtin), + ty.typerep, + location=builtin); + checkExpr.env = top.env; + checkExpr.flowEnv = top.flowEnv; + checkExpr.finalSubst = checkExpr.upSubst; -- Not top.finalSubst to avoid circularity + checkExpr.grammarName = top.grammarName; + checkExpr.frame = top.frame; + checkExpr.config = top.config; + checkExpr.compiledGrammars = top.compiledGrammars; + checkExpr.boundVars = []; + checkExpr.isRoot = top.isRoot; + checkExpr.originRules = top.originRules; + + ml.matchRulePatternSize = 1; + ml.ruleIndex = 0; + ml.decRuleExprsIn = checkExpr.decRuleExprs; + + local localErrors::[Message] = + ty.errors ++ ml.errors ++ checkExpr.errors ++ + ty.errorsKindStar ++ + if null(getTypeDcl("silver:rewrite:Strategy", top.env)) + then [err(top.location, "Term rewriting requires import of silver:rewrite")] + else []; + + -- Can't use an error production here, unfortunately, due to circular dependency issues. + top.errors := if !null(localErrors) then localErrors else forward.errors; + + thread downSubst, upSubst on top, checkExpr, forward; + + local finalRuleType::Type = + freshenType( + performSubstitution(ty.typerep, checkExpr.upSubst), + ty.typerep.freeVariables); + local transform::Strategy = + if ml.isPolymorphic + then requireType(antiquoteASTExpr( + Silver_Expr { + silver:rewrite:anyASTExpr( + \ $TypeExpr{typerepTypeExpr(finalRuleType, location=builtin)} -> unit()) + })) <* ml.transform + else ml.transform; + + local fwrd::Expr = translate(top.location, reflect(new(transform))); + + --forwards to unsafeTrace(fwrd, print(top.location.unparse ++ ": " ++ show(80, transform.pp) ++ "\n\n\n", unsafeIO())); + forwards to fwrd; +} + +-- Hack dummy expr with a given type +abstract production hackExprType +top::Expr ::= t::Type +{ + top.typerep = t; + forwards to errorExpr([], location=builtin); +} + +-- Strategy meta-translation +abstract production antiquoteASTExpr +top::ASTExpr ::= e::Expr +{ + top.pp = pp"antiquoteASTExpr {${text(e.unparse)}}"; + forwards to error("no forward"); +} + +abstract production antiquoteStrategy +top::Strategy ::= e::Expr +{ + top.pp = pp"antiquoteStrategy {${text(e.unparse)}}"; + forwards to error("no forward"); +} + +aspect production nonterminalAST +top::AST ::= prodName::String children::ASTs annotations::NamedASTs +{ + directAntiquoteProductions <- + ["silver:compiler:extension:rewriting:antiquoteASTExpr", + "silver:compiler:extension:rewriting:antiquoteStrategy"]; +} + +global builtin::Location = builtinLoc("rewriting"); diff --git a/grammars/silver/compiler/extension/silverconstruction/Syntax.sv b/grammars/silver/compiler/extension/silverconstruction/Syntax.sv new file mode 100644 index 000000000..d52d02729 --- /dev/null +++ b/grammars/silver/compiler/extension/silverconstruction/Syntax.sv @@ -0,0 +1,156 @@ +grammar silver:compiler:extension:silverconstruction; + +imports silver:langutil:pp; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:modification:list; +imports silver:compiler:extension:patternmatching; + +concrete production quoteAGDcl +top::Expr ::= 'Silver_AGDcl' '{' ast::AGDcl '}' +{ + top.unparse = s"Silver_AGDcl " ++ substitute("\n"," ", "{${ast.unparse}}"); + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production quoteProductionStmt +top::Expr ::= 'Silver_ProductionStmt' '{' ast::ProductionStmt '}' +{ + top.unparse = s"Silver_ProductionStmt" ++ substitute("\n"," ", "{${ast.unparse}}"); + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production quoteExpr +top::Expr ::= 'Silver_Expr' '{' ast::Expr '}' +{ + top.unparse = s"Silver_Expr {${ast.unparse}}"; + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production quoteExprInh +top::Expr ::= 'Silver_ExprInh' '{' ast::ExprInh '}' +{ + top.unparse = s"Silver_ExprInh {${ast.unparse}}"; + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production quotePattern +top::Expr ::= 'Silver_Pattern' '{' ast::Pattern '}' +{ + top.unparse = s"Silver_Pattern {${ast.unparse}}"; + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production quoteTypeExpr +top::Expr ::= 'Silver_TypeExpr' '{' ast::TypeExpr '}' +{ + top.unparse = s"Silver_TypeExpr {${ast.unparse}}"; + forwards to translate(top.location, reflect(new(ast))); +} + +concrete production antiquoteExpr +top::Expr ::= '$Expr' '{' e::Expr '}' +{ + top.unparse = s"$$Expr{${e.unparse}}"; + forwards to + errorExpr( + [err(top.location, "$Expr should not occur outside of quoted Silver literal")], + location=top.location); +} + +concrete production antiquoteExprInhs +top::ExprInhs ::= '$ExprInhs' '{' e::Expr '}' +{ + top.unparse = s"$$ExprInhs{${e.unparse}}"; + -- TODO: [err(top.location, "$ExprInhs should not occur outside of quoted Silver literal")] + forwards to exprInhsEmpty(location=top.location); +} + +concrete production antiquoteTypeExpr +top::TypeExpr ::= '$TypeExpr' '{' e::Expr '}' +{ + top.unparse = s"$$TypeExpr{${e.unparse}}"; + forwards to + errorTypeExpr( + [err(top.location, "$TypeExpr should not occur outside of quoted Silver literal")], + location=top.location); +} + +concrete production antiquotePattern +top::Pattern ::= '$Pattern' '{' e::Expr '}' +{ + top.unparse = s"$$Pattern{${e.unparse}}"; + forwards to + errorPattern( + [err(top.location, "$Pattern should not occur outside of quoted Silver literal")], + location=top.location); +} + + +concrete production antiquoteAspectRHS +top::AspectRHS ::= '$AspectRHS' '{' e::Expr '}' +{ + top.unparse = s"$$AspectRHS{${e.unparse}}"; + forwards to aspectRHSElemNil(location=top.location); +} + +concrete production antiquoteProductionStmt +top::ProductionStmt ::= '$ProductionStmt' '{' e::Expr '}' +{ + top.unparse = s"$$ProductionStmt{${e.unparse}}"; + forwards to + errorProductionStmt( + [err(top.location, "$ProductionStmt should not occur outside of quoted Silver Literal.")], + location=top.location); +} + + +concrete production antiquoteQName +top::QName ::= '$QName' '{' e::Expr '}' +{ + top.unparse = s"$$QName{${e.unparse}}"; + forwards to + qNameError( + [err(top.location, "$QName should not occur outside of quoted Silver literal")], + location=top.location); +} + +concrete production antiquoteQNameAttrOccur +top::QNameAttrOccur ::= '$QNameAttrOccur' '{' e::Expr '}' +{ + top.unparse = s"$$QNameAttrOccur{${e.unparse}}"; + forwards to + qNameAttrOccur( + qNameError( + [err(top.location, "$QNameAttrOccur should not occur outside of quoted Silver literal")], + location=top.location), + location=top.location); +} + +concrete production antiquoteName +top::Name ::= '$Name' '{' e::Expr '}' +{ + top.unparse = s"$$Name{${e.unparse}}"; + -- TODO: [err(top.location, "$Name should not occur outside of quoted Silver literal")] + forwards to name("err", top.location); +} + +concrete production antiquote_qName +top::QName ::= '$qName' '{' e::Expr '}' +{ + top.unparse = s"$$qName{${e.unparse}}"; + forwards to + qNameError( + [err(top.location, "$qName should not occur outside of Silver_Expr")], + location=top.location); +} + +concrete production antiquote_name +top::Name ::= '$name' '{' e::Expr '}' +{ + top.unparse = s"$$name{${e.unparse}}"; + -- TODO: [err(top.location, "$Name should not occur outside of quoted Silver literal")] + forwards to name("err", top.location); +} diff --git a/grammars/silver/compiler/extension/silverconstruction/Terminals.sv b/grammars/silver/compiler/extension/silverconstruction/Terminals.sv new file mode 100644 index 000000000..6a4c46217 --- /dev/null +++ b/grammars/silver/compiler/extension/silverconstruction/Terminals.sv @@ -0,0 +1,22 @@ +grammar silver:compiler:extension:silverconstruction; + +marking terminal SilverExpr_t 'Silver_Expr' lexer classes {KEYWORD, RESERVED}; +marking terminal SilverExprInh_t 'Silver_ExprInh' lexer classes {KEYWORD, RESERVED}; +marking terminal SilverPattern_t 'Silver_Pattern' lexer classes {KEYWORD, RESERVED}; +marking terminal SilverAGDcl_t 'Silver_AGDcl' lexer classes {KEYWORD, RESERVED}; +marking terminal SilverProductionStmt_t 'Silver_ProductionStmt' lexer classes {KEYWORD, RESERVED}; +marking terminal SilverTypeExpr_t 'Silver_TypeExpr' lexer classes {KEYWORD, RESERVED}; + +lexer class Antiquote extends SPECOP; + +terminal AntiquoteExpr_t '$Expr' lexer classes {Antiquote}; +terminal AntiquoteExprInhs_t '$ExprInhs' lexer classes {Antiquote}; +terminal AntiquoteTypeExpr_t '$TypeExpr' lexer classes {Antiquote}; +terminal AntiquotePattern_t '$Pattern' lexer classes {Antiquote}; +terminal AntiquoteAspectRHS_t '$AspectRHS' lexer classes {Antiquote}; +terminal AntiquoteProductionStmt_t '$ProductionStmt' lexer classes {Antiquote}; +terminal AntiquoteQName_t '$QName' lexer classes {Antiquote}; +terminal AntiquoteQNameAttrOccur_t '$QNameAttrOccur' lexer classes {Antiquote}; +terminal AntiquoteName_t '$Name' lexer classes {Antiquote}; +terminal Antiquote_qName_t '$qName' lexer classes {Antiquote}; +terminal Antiquote_name_t '$name' lexer classes {Antiquote}; diff --git a/grammars/silver/compiler/extension/silverconstruction/Translation.sv b/grammars/silver/compiler/extension/silverconstruction/Translation.sv new file mode 100644 index 000000000..33a990a55 --- /dev/null +++ b/grammars/silver/compiler/extension/silverconstruction/Translation.sv @@ -0,0 +1,55 @@ +grammar silver:compiler:extension:silverconstruction; + +imports silver:reflect; +imports silver:compiler:metatranslation; + +aspect production nonterminalAST +top::AST ::= prodName::String children::ASTs annotations::NamedASTs +{ + directAntiquoteProductions <- + ["silver:compiler:extension:silverconstruction:antiquoteExpr", + "silver:compiler:extension:silverconstruction:antiquoteExprInhs", + "silver:compiler:extension:silverconstruction:antiquoteTypeExpr", + "silver:compiler:extension:silverconstruction:antiquotePattern", + "silver:compiler:extension:silverconstruction:antiquoteAspectRHS", + "silver:compiler:extension:silverconstruction:antiquoteProductionStmt", + "silver:compiler:extension:silverconstruction:antiquoteQName", + "silver:compiler:extension:silverconstruction:antiquoteQNameAttrOccur", + "silver:compiler:extension:silverconstruction:antiquoteName"]; + + -- "Indirect" antiquote productions + antiquoteTranslation <- + case prodName, children, annotations of + | "silver:compiler:extension:silverconstruction:antiquote_qName", + consAST(_, consAST(_, consAST(a, consAST(_, nilAST())))), + consNamedAST(namedAST("silver:core:location", locAST), nilNamedAST()) -> + case reify(a) of + | right(e) -> + just( + mkFullFunctionInvocation( + givenLocation, + baseExpr(qName(givenLocation, "silver:compiler:metatranslation:makeQName"), location=givenLocation), + [e, locAST.translation], + [])) + | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") + end + | "silver:compiler:extension:silverconstruction:antiquote_qName", _, _ -> + error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") + | "silver:compiler:extension:silverconstruction:antiquote_name", + consAST(_, consAST(_, consAST(a, consAST(_, nilAST())))), + consNamedAST(namedAST("silver:core:location", locAST), nilNamedAST()) -> + case reify(a) of + | right(e) -> + just( + mkFullFunctionInvocation( + givenLocation, + baseExpr(qName(givenLocation, "silver:compiler:metatranslation:makeName"), location=givenLocation), + [e, locAST.translation], + [])) + | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") + end + | "silver:compiler:extension:silverconstruction:antiquote_name", _, _ -> + error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") + | _, _, _ -> nothing() + end; +} diff --git a/grammars/silver/compiler/extension/strategyattr/ConcreteSyntax.sv b/grammars/silver/compiler/extension/strategyattr/ConcreteSyntax.sv new file mode 100644 index 000000000..f005384e8 --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/ConcreteSyntax.sv @@ -0,0 +1,241 @@ +grammar silver:compiler:extension:strategyattr; + +inherited attribute givenGenName::String; + +concrete production partialStrategyAttributeDcl +top::AGDcl ::= 'partial' 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c ';' +{ + top.unparse = "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; + e.givenGenName = a.name; + forwards to strategyAttributeDcl(false, a, [], [], e.ast, location=top.location); +} + +concrete production totalStrategyAttributeDcl +top::AGDcl ::= 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c ';' +{ + top.unparse = "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; + e.givenGenName = a.name; + forwards to strategyAttributeDcl(true, a, [], [], e.ast, location=top.location); +} + +closed nonterminal StrategyExpr_c with location, givenGenName, unparse, ast; + +concrete productions top::StrategyExpr_c +| 'id' +{ + top.unparse = "id"; + top.ast = id(genName=top.givenGenName, location=top.location); +} +| 'fail' +{ + top.unparse = "fail"; + top.ast = fail(genName=top.givenGenName, location=top.location); +} +| s1::StrategyExpr_c '<*' s2::StrategyExpr_c +{ + top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; + top.ast = sequence(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_fst"; + s2.givenGenName = top.givenGenName ++ "_snd"; +} +| s1::StrategyExpr_c '<+' s2::StrategyExpr_c +{ + top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; + top.ast = choice(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_left"; + s2.givenGenName = top.givenGenName ++ "_right"; +} +| 'all' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"all(${s.unparse})"; + top.ast = allTraversal(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_all_arg"; +} +| 'some' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"some(${s.unparse})"; + top.ast = someTraversal(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_some_arg"; +} +| 'one' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"one(${s.unparse})"; + top.ast = oneTraversal(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_one_arg"; +} +| id::StrategyQName '(' s::StrategyExprs_c ')' +{ + top.unparse = s"${id.ast.unparse}(${s.unparse})"; + top.ast = prodTraversal(id.ast, s.ast, genName=top.givenGenName, location=top.location); + s.index = 1; + s.givenGenName = top.givenGenName ++ "_" ++ id.ast.name; +} +| 'rec' n::Name Arrow_t s::StrategyExpr_c +{ + top.unparse = s"rec ${n.name} -> (${s.unparse})"; + top.ast = recComb(n, s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName; +} +| 'rule' 'on' id::Name '::' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' +{ + top.unparse = "rule on " ++ id.unparse ++ "::" ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; + top.ast = rewriteRule(id, ty, ml, genName=top.givenGenName, location=top.location); +} +| 'rule' 'on' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' +{ + top.unparse = "rule on " ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; + top.ast = rewriteRule(name("top", top.location), ty, ml, genName=top.givenGenName, location=top.location); +} +| id::StrategyQName +{ + top.unparse = id.ast.unparse; + top.ast = nameRef(id.ast, genName=top.givenGenName, location=top.location); +} +| '(' s::StrategyExpr_c ')' +{ + top.unparse = s"(${s.unparse})"; + top.ast = s.ast; + s.givenGenName = top.givenGenName; +} +| 'printTerm' +{ + top.unparse = s"printTerm"; + top.ast = printTerm(genName=top.givenGenName, location=top.location); +} +| 'try' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"try(${s.unparse})"; + top.ast = try(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_try_arg"; +} +| 'repeat' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"repeat(${s.unparse})"; + top.ast = repeatS(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_repeat_arg"; +} +| 'reduce' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"reduce(${s.unparse})"; + top.ast = reduce(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_reduce_arg"; +} +| 'bottomUp' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"bottomUp(${s.unparse})"; + top.ast = bottomUp(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_bottomUp_arg"; +} +| 'topDown' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"topDown(${s.unparse})"; + top.ast = topDown(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_topDown_arg"; +} +| 'downUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' +{ + top.unparse = s"downUp(${s1.unparse}, ${s2.unparse})"; + top.ast = downUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_downUp_arg1"; + s2.givenGenName = top.givenGenName ++ "_downUp_arg2"; +} +| 'allBottomUp' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"allBottomUp(${s.unparse})"; + top.ast = allBottomUp(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_allBottomUp_arg"; +} +| 'allTopDown' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"allTopDown(${s.unparse})"; + top.ast = allTopDown(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_allTopDown_arg"; +} +| 'allDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' +{ + top.unparse = s"allDownUp(${s1.unparse}, ${s2.unparse})"; + top.ast = allDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_allDownUp_arg1"; + s2.givenGenName = top.givenGenName ++ "_allDownUp_arg2"; +} +| 'someBottomUp' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"someBottomUp(${s.unparse})"; + top.ast = someBottomUp(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_someBottomUp_arg"; +} +| 'someTopDown' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"someTopDown(${s.unparse})"; + top.ast = someTopDown(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_someTopDown_arg"; +} +| 'someDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' +{ + top.unparse = s"someDownUp(${s1.unparse}, ${s2.unparse})"; + top.ast = someDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_someDownUp_arg1"; + s2.givenGenName = top.givenGenName ++ "_someDownUp_arg2"; +} +| 'onceBottomUp' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"onceBottomUp(${s.unparse})"; + top.ast = onceBottomUp(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_onceBottomUp_arg"; +} +| 'onceTopDown' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"onceTopDown(${s.unparse})"; + top.ast = onceTopDown(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_onceTopDown_arg"; +} +| 'onceDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' +{ + top.unparse = s"onceDownUp(${s1.unparse}, ${s2.unparse})"; + top.ast = onceDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); + s1.givenGenName = top.givenGenName ++ "_onceDownUp_arg1"; + s2.givenGenName = top.givenGenName ++ "_onceDownUp_arg2"; +} +| 'innermost' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"innermost(${s.unparse})"; + top.ast = innermost(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_innermost_arg"; +} +| 'outermost' '(' s::StrategyExpr_c ')' +{ + top.unparse = s"outermost(${s.unparse})"; + top.ast = outermost(s.ast, genName=top.givenGenName, location=top.location); + s.givenGenName = top.givenGenName ++ "_outermost_arg"; +} + +autocopy attribute index::Integer; + +nonterminal StrategyExprs_c with location, index, givenGenName, unparse, ast; +concrete productions top::StrategyExprs_c +| h::StrategyExpr_c ',' t::StrategyExprs_c +{ + top.unparse = h.unparse ++ ", " ++ t.unparse; + top.ast = consStrategyExpr(h.ast, t.ast); + h.givenGenName = top.givenGenName ++ "_arg" ++ toString(top.index); + t.givenGenName = top.givenGenName; + t.index = top.index + 1; +} +| h::StrategyExpr_c +{ + top.unparse = h.unparse; + top.ast = consStrategyExpr(h.ast, nilStrategyExpr()); + h.givenGenName = top.givenGenName ++ "_arg" ++ toString(top.index); +} +| +{ + top.unparse = ""; + top.ast = nilStrategyExpr(); +} + +nonterminal StrategyQName with location, ast; +concrete productions top::StrategyQName +(strategyQNameOne) | id::StrategyName_t +{ top.ast = qNameId(name(id.lexeme, id.location), location=top.location); } +(strategyQNameCons) | id::StrategyName_t ':' qn::StrategyQName +{ top.ast = qNameCons(name(id.lexeme, id.location), $2, qn.ast, location=top.location); } diff --git a/grammars/silver/compiler/extension/strategyattr/DclInfo.sv b/grammars/silver/compiler/extension/strategyattr/DclInfo.sv new file mode 100644 index 000000000..4ecc02002 --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/DclInfo.sv @@ -0,0 +1,61 @@ +grammar silver:compiler:extension:strategyattr; + +synthesized attribute isStrategy::Boolean occurs on AttributeDclInfo; +attribute isTotal occurs on AttributeDclInfo; +synthesized attribute containsErrors::Boolean occurs on AttributeDclInfo; +synthesized attribute liftedStrategyNames::[String] occurs on AttributeDclInfo; +synthesized attribute givenRecVarNameEnv::[Pair] occurs on AttributeDclInfo; +synthesized attribute givenRecVarTotalEnv::[Pair] occurs on AttributeDclInfo; +attribute partialRefs, totalRefs, containsTraversal occurs on AttributeDclInfo; +synthesized attribute strategyExpr :: StrategyExpr occurs on AttributeDclInfo; + +aspect default production +top::AttributeDclInfo ::= +{ + top.isStrategy = false; + top.isTotal = true; + top.containsErrors = false; + top.liftedStrategyNames = []; + top.givenRecVarNameEnv = []; + top.givenRecVarTotalEnv = []; + top.partialRefs := []; + top.totalRefs := []; + top.containsTraversal := false; + top.strategyExpr = error("Internal compiler error: must be defined for all strategy attribute declarations"); +} + +abstract production strategyDcl +top::AttributeDclInfo ::= + fn::String isTotal::Boolean + containsErrors::Boolean liftedStrategyNames::[String] + givenRecVarNameEnv::[Pair] givenRecVarTotalEnv::[Pair] + partialRefs::[String] totalRefs::[String] containsTraversal::Boolean + e::StrategyExpr +{ + top.fullName = fn; + propagate compareKey, isEqual; + + production tyVar::TyVar = freshTyVar(starKind()); + top.typeScheme = polyType([tyVar], + if isTotal + then varType(tyVar) + else appType(nonterminalType("silver:core:Maybe", [starKind()], false), varType(tyVar))); + top.isSynthesized = true; + top.isStrategy = true; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations + top.attributionDispatcher = strategyAttributionDcl(_, _, _, _, location=_); + top.propagateDispatcher = propagateStrategy(_, location=_); + + top.isTotal = isTotal; + top.containsErrors = containsErrors; + top.liftedStrategyNames = liftedStrategyNames; + top.givenRecVarNameEnv = givenRecVarNameEnv; + top.givenRecVarTotalEnv = givenRecVarTotalEnv; + top.partialRefs := partialRefs; + top.totalRefs := totalRefs; + top.containsTraversal := containsTraversal; + top.strategyExpr = e; +} diff --git a/grammars/silver/compiler/extension/strategyattr/Project.sv b/grammars/silver/compiler/extension/strategyattr/Project.sv new file mode 100644 index 000000000..0c0307a14 --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/Project.sv @@ -0,0 +1,18 @@ +grammar silver:compiler:extension:strategyattr; + +imports silver:core hiding id, sequence, fail; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax hiding Arrow_t; +imports silver:compiler:extension:autoattr; +imports silver:compiler:extension:patternmatching; +imports silver:compiler:modification:list; +--imports silver:compiler:extension:rewriting; +imports silver:compiler:extension:silverconstruction; +imports silver:compiler:modification:let_fix; +imports silver:compiler:modification:lambda_fn; + +exports silver:compiler:extension:strategyattr:convenience; +exports silver:compiler:extension:strategyattr:construction; diff --git a/grammars/silver/compiler/extension/strategyattr/Strategy.sv b/grammars/silver/compiler/extension/strategyattr/Strategy.sv new file mode 100644 index 000000000..a9759fdbc --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/Strategy.sv @@ -0,0 +1,175 @@ +grammar silver:compiler:extension:strategyattr; + +abstract production strategyAttributeDcl +top::AGDcl ::= isTotal::Boolean a::Name recVarNameEnv::[Pair] recVarTotalEnv::[Pair] e::StrategyExpr +{ + top.unparse = (if isTotal then "" else "partial ") ++ "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; + top.occursDefs := []; + top.specDefs := []; + top.refDefs := []; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + -- Define these directly to avoid circular dependencies, + -- since the forward contributes to the env. + propagate errors, moduleNames; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; + top.errors <- + if isTotal && !e.isTotal + -- Not an error since we can still translate this, but the translation may raise run-time errors in case of failure + then [wrn(e.location, s"Implementation of total strategy ${a.name} is not total")] + else []; + + e.recVarNameEnv = recVarNameEnv; + e.recVarTotalEnv = recVarTotalEnv; + e.recVarTotalNoEnvEnv = recVarTotalEnv; + e.outerAttr = a.name; + e.isOutermost = true; + + local fwrd::AGDcl = + foldr( + appendAGDcl(_, _, location=top.location), + defsAGDcl( + [attrDef( + defaultEnvItem( + strategyDcl( + fName, isTotal, + !null(top.errors), map(fst, e.liftedStrategies), recVarNameEnv, recVarTotalEnv, e.partialRefs, e.totalRefs, e.containsTraversal, e, + sourceGrammar=top.grammarName, sourceLocation=a.location)))], + location=top.location), + map( + \ d::(String, Decorated StrategyExpr with LiftedInhs) -> + strategyAttributeDcl( + d.snd.isTotalNoEnv, name(d.fst, top.location), d.snd.recVarNameEnv, d.snd.recVarTotalNoEnvEnv, new(d.snd), + location=top.location), + e.liftedStrategies)); + + -- Uncomment for debugging + --forwards to unsafeTrace(fwrd, printT(a.name ++ " = " ++ e.unparse ++ "; lifted " ++ implode(", ", map(fst, e.liftedStrategies)) ++ "\n\n", unsafeIO())); + + forwards to fwrd; +} + +abstract production strategyAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + production attribute localErrors::[Message] with ++; + localErrors := + attl.errors ++ attl.errorsTyVars ++ nt.lookupType.errors ++ nttl.errors ++ nttl.errorsTyVars; + localErrors <- + if length(attl.types) > 0 + then [err(attl.location, "Explicit type arguments are not allowed for strategy attributes")] + else []; + + -- Technically we could do this check on the propagate, but it seems clearer to raise it here + localErrors <- + flatMap( + \ totalAttr::String -> + if null(getOccursDcl(totalAttr, nt.lookupType.fullName, top.env)) + then [err(top.location, s"Total strategy attribute ${totalAttr} referenced by ${at.name} does not occur on ${nt.name}")] + else [], + nub(at.lookupAttribute.dcl.totalRefs)); + + -- TODO: Check that the type parameters of any rules of type nt match nttl + + top.errors := if !null(localErrors) then localErrors else forward.errors; + + forwards to + foldr( + appendAGDcl(_, _, location=top.location), + defaultAttributionDcl( + at, + botlSome( + bTypeList( + '<', + typeListSingle( + case nttl of + | botlSome(tl) -> + appTypeExpr( + nominalTypeExpr(nt.qNameType, location=top.location), + tl, location=top.location) + | botlNone() -> nominalTypeExpr(nt.qNameType, location=top.location) + end, + location=top.location), + '>', location=top.location), + location=top.location), + nt, nttl, + location=top.location), + map( + \ n::String -> + attributionDcl( + 'attribute', qName(top.location, n), attl, 'occurs', 'on', nt, nttl, ';', + location=top.location), + at.lookupAttribute.dcl.liftedStrategyNames)); +} + +{-- + - Propagate a strategy attribute on the enclosing production + - @param attr The name of the attribute to propagate + -} +abstract production propagateStrategy +top::ProductionStmt ::= attr::PartiallyDecorated QName +{ + top.unparse = s"propagate ${attr.unparse}"; + + production isTotal::Boolean = attr.lookupAttribute.dcl.isTotal; + production e::StrategyExpr = attr.lookupAttribute.dcl.strategyExpr; + e.grammarName = top.grammarName; + e.config = top.config; + e.frame = top.frame; + e.env = top.env; + e.recVarNameEnv = attr.lookupAttribute.dcl.givenRecVarNameEnv; + e.recVarTotalEnv = attr.lookupAttribute.dcl.givenRecVarTotalEnv; + e.outerAttr = attr.lookupAttribute.fullName; + e.isOutermost = true; + e.inlinedStrategies = [attr.lookupAttribute.fullName]; -- Don't unfold the top-level strategy within itself + + production e2::StrategyExpr = e.optimize; + e2.grammarName = e.grammarName; + e2.config = e.config; + e2.frame = e.frame; + e2.env = e.env; + e2.recVarNameEnv = e.recVarNameEnv; + e2.recVarTotalEnv = e.recVarTotalEnv; + e2.outerAttr = e.outerAttr; + e2.isOutermost = e.isOutermost; + e2.inlinedStrategies = e.inlinedStrategies; + e2.flowEnv = top.flowEnv; + + -- Can't do this with forwarding to avoid circular dependency of + -- forward -> dcl.containsErrors -> dcl.flowEnv -> forward.flowDefs + top.errors := + if + -- Check for errors in this or inlined strategy expressions that would be reported on the attribute definition + attr.lookupAttribute.dcl.containsErrors || + any(map((.containsErrors), flatMap(getAttrDcl(_, top.env), attr.lookupAttribute.dcl.partialRefs))) || + -- Check for total strategy ref occurs errors that would already be reported on the occurence + (!null(getOccursDcl(attr.lookupAttribute.fullName, top.frame.signature.outputElement.typerep.typeName, top.env)) && + any(map(null, map(getOccursDcl(_, top.frame.signature.outputElement.typerep.typeName, top.env), attr.lookupAttribute.dcl.totalRefs)))) + then [] + else forward.errors; + + local fwrd::ProductionStmt = + foldr( + productionStmtAppend(_, _, location=top.location), + attributeDef( + concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), + '.', + qNameAttrOccur(new(attr), location=top.location), + '=', + if isTotal then e2.totalTranslation else e2.partialTranslation, + ';', + location=top.location), + map( + \ n::String -> propagateOneAttr(qName(top.location, n), location=top.location), + attr.lookupAttribute.dcl.liftedStrategyNames)); + + -- Uncomment for debugging + --forwards to unsafeTrace(fwrd, printT(attr.name ++ " on " ++ top.frame.fullName ++ " = " ++ (if isTotal then e2.totalTranslation else e2.partialTranslation).unparse ++ ";\n\n", unsafeIO())); + forwards to fwrd; +} diff --git a/grammars/silver/compiler/extension/strategyattr/StrategyExpr.sv b/grammars/silver/compiler/extension/strategyattr/StrategyExpr.sv new file mode 100644 index 000000000..ec6bf10d5 --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/StrategyExpr.sv @@ -0,0 +1,1075 @@ +grammar silver:compiler:extension:strategyattr; + +import silver:compiler:metatranslation; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; +import silver:compiler:driver:util; + +annotation genName::String; -- Used to generate the names of lifted strategy attributes + +autocopy attribute recVarNameEnv::[Pair]; -- name, (isTotal, genName) +autocopy attribute recVarTotalEnv::[Pair]; -- name, (isTotal, genName) +autocopy attribute recVarTotalNoEnvEnv::[Pair]; -- same as above but doesn't depend on env +inherited attribute isOutermost::Boolean; +autocopy attribute outerAttr::String; +autocopy attribute inlinedStrategies::[String]; +type LiftedInhs = {recVarNameEnv, recVarTotalNoEnvEnv, outerAttr, isOutermost}; +monoid attribute liftedStrategies::[(String, Decorated StrategyExpr with LiftedInhs)]; +synthesized attribute attrRefName::Maybe; +synthesized attribute isId::Boolean; +synthesized attribute isFail::Boolean; +synthesized attribute isTotal::Boolean; +synthesized attribute isTotalNoEnv::Boolean; -- same as above but doesn't depend on env +inherited attribute givenInputElements::[NamedSignatureElement]; +synthesized attribute attrRefNames::[Maybe]; +monoid attribute containsFail::Boolean with false, ||; +monoid attribute allId::Boolean with true, &&; +monoid attribute freeRecVars::[String]; +monoid attribute partialRefs::[String]; +monoid attribute totalRefs::[String]; +monoid attribute matchesFrame::Boolean with false, ||; +monoid attribute containsTraversal::Boolean with false, ||; + +synthesized attribute partialTranslation::Expr; -- Maybe on a +synthesized attribute totalTranslation::Expr; -- a on a, can raise a runtime error if demanded on partial strategy expression + +-- Nonterminal-independent algebraic simplifications +-- Theoretically these could be applied to the strategy before lifting/propagation, +-- but probably not much of an improvement. +partial strategy attribute genericStep = + rule on top::StrategyExpr of + | sequence(fail(), _) -> fail(location=top.location, genName=top.genName) + | sequence(_, fail()) -> fail(location=top.location, genName=top.genName) + | sequence(id(), s) -> s + | sequence(s, id()) -> s + | choice(fail(), s) -> s + | choice(s, fail()) -> s + | choice(s, _) when s.isTotal -> s + | allTraversal(id()) -> id(location=top.location, genName=top.genName) + | someTraversal(fail()) -> fail(location=top.location, genName=top.genName) + | oneTraversal(fail()) -> fail(location=top.location, genName=top.genName) + | prodTraversal(_, ss) when ss.containsFail -> fail(location=top.location, genName=top.genName) + | recComb(n, s) when !contains(n.name, s.freeRecVars) -> s + | inlined(_, fail()) -> fail(location=top.location, genName=top.genName) + end; +-- Nonterminal-dependent, production-independent optimizations +partial strategy attribute ntStep = + rule on top::StrategyExpr of + -- Only inline references to partial strategies, as inlining total + -- strategies would not permit any additional simplification. + | partialRef(n) when + n.matchesFrame && n.attrDcl.isStrategy && + !contains(n.attrDcl.fullName, top.inlinedStrategies) && + null(n.attrDcl.givenRecVarNameEnv) && + !n.attrDcl.containsTraversal -> + inlined(n, n.attrDcl.strategyExpr, location=top.location, genName=top.genName) + | partialRef(n) when !n.matchesFrame -> fail(location=top.location, genName=top.genName) + | inlined(n, _) when !n.matchesFrame -> fail(location=top.location, genName=top.genName) + | inlined(n, id()) when n.matchesFrame -> id(location=top.location, genName=top.genName) + | inlined(n1, totalRef(n2)) when n1.matchesFrame -> totalRef(n2, location=top.location, genName=top.genName) + end; +-- Production-dependent optimizations +partial strategy attribute prodStep = + rule on top::StrategyExpr of + | allTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> id(location=top.location, genName=top.genName) + | someTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> fail(location=top.location, genName=top.genName) + | oneTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> fail(location=top.location, genName=top.genName) + | prodTraversal(p, s) when p.lookupValue.fullName != top.frame.fullName -> fail(location=top.location, genName=top.genName) + | rewriteRule(_, _, ml) when !ml.matchesFrame -> fail(location=top.location, genName=top.genName) + end <+ + rewriteRule( + id, id, + onceBottomUp( + rule on top::MRuleList of + | mRuleList_cons(h, _, t) when !h.matchesFrame -> t + | mRuleList_cons(h, _, mRuleList_one(t)) when !t.matchesFrame -> mRuleList_one(h, location=top.location) + end)); +attribute prodStep occurs on MRuleList; + +strategy attribute genericSimplify = innermost(genericStep); +strategy attribute ntSimplify = + (sequence(ntSimplify, ntSimplify) <+ + choice(ntSimplify, ntSimplify) <+ + allTraversal(genericSimplify) <+ + someTraversal(genericSimplify) <+ + oneTraversal(genericSimplify) <+ + prodTraversal(id, genericSimplify) <+ + recComb(id, ntSimplify) <+ + inlined(id, ntSimplify) <+ + id) <* + try((genericStep <+ ntStep) <* ntSimplify); +strategy attribute optimize = + (sequence(optimize, ntSimplify) <+ + choice(optimize, optimize) <+ + allTraversal(genericSimplify) <+ + someTraversal(genericSimplify) <+ + oneTraversal(genericSimplify) <+ + prodTraversal(id, genericSimplify) <+ + recComb(id, optimize) <+ + inlined(id, optimize) <+ + id) <* + try((genericStep <+ ntStep <+ prodStep) <* optimize); + +nonterminal StrategyExpr with + config, grammarName, env, location, unparse, errors, frame, compiledGrammars, flowEnv, -- Normal expression stuff + genName, outerAttr, isOutermost, recVarNameEnv, recVarTotalEnv, recVarTotalNoEnvEnv, liftedStrategies, attrRefName, isId, isFail, isTotal, isTotalNoEnv, freeRecVars, partialRefs, totalRefs, containsTraversal, -- Frame-independent attrs + partialTranslation, totalTranslation, matchesFrame, -- Frame-dependent attrs + inlinedStrategies, genericStep, ntStep, prodStep, genericSimplify, ntSimplify, optimize; -- Optimization stuff + +nonterminal StrategyExprs with + config, grammarName, env, unparse, errors, compiledGrammars, flowEnv, -- Normal expression stuff + outerAttr, recVarNameEnv, recVarTotalEnv, recVarTotalNoEnvEnv, givenInputElements, liftedStrategies, attrRefNames, containsFail, allId, freeRecVars, partialRefs, totalRefs, containsTraversal, -- Frame-independent attrs + inlinedStrategies, genericSimplify; -- Optimization stuff + +flowtype StrategyExpr = + decorate {env, grammarName, config, recVarNameEnv, recVarTotalEnv, outerAttr, isOutermost}, -- NOT frame + forward {decorate}, + -- Normal expression stuff + unparse {}, errors {decorate, compiledGrammars, flowEnv}, + -- Frame-independent attrs + liftedStrategies {recVarNameEnv, recVarTotalNoEnvEnv, outerAttr, isOutermost}, isTotalNoEnv {recVarNameEnv, recVarTotalNoEnvEnv, outerAttr, isOutermost}, + attrRefName {recVarNameEnv}, isId {}, isFail {}, + isTotal {decorate}, freeRecVars {decorate}, partialRefs {decorate}, totalRefs {decorate}, containsTraversal {decorate, flowEnv}, + genericStep {decorate, inlinedStrategies}, genericSimplify {decorate, inlinedStrategies}, + -- Frame-dependent attrs + partialTranslation {decorate, flowEnv, frame}, totalTranslation {decorate, flowEnv, frame}, matchesFrame {decorate, frame}, + ntStep {decorate, inlinedStrategies, frame}, prodStep {decorate, inlinedStrategies, frame}, + ntSimplify {decorate, inlinedStrategies, frame}, optimize {decorate, inlinedStrategies, frame}; + +flowtype StrategyExprs = + decorate {env, grammarName, config, recVarNameEnv, recVarTotalEnv, outerAttr}, -- NOT frame + forward {}, + -- Normal expression stuff + -- Frame-independent attrs + liftedStrategies {recVarNameEnv, recVarTotalNoEnvEnv, outerAttr}, + attrRefNames {env, recVarNameEnv, givenInputElements}, + containsFail {}, allId {}, freeRecVars {decorate}, partialRefs {decorate}, totalRefs {decorate}; + +propagate errors on StrategyExpr, StrategyExprs excluding partialRef, totalRef, rewriteRule; +propagate containsFail, allId on StrategyExprs; +propagate freeRecVars on StrategyExpr, StrategyExprs excluding recComb; +propagate partialRefs, totalRefs, containsTraversal on StrategyExpr, StrategyExprs; +propagate genericSimplify on StrategyExprs; +propagate prodStep on MRuleList; +propagate genericStep, ntStep, prodStep, genericSimplify, ntSimplify, optimize on StrategyExpr; + +-- Convert an expression of type a to Maybe +function asPartial +Expr ::= e::Expr +{ return Silver_Expr { silver:core:just($Expr{e}) }; } + +-- Convert an expression of type Maybe to a +function asTotal +Expr ::= t::Type e::Expr +{ + return + Silver_Expr { + let res::$TypeExpr{typerepTypeExpr(t, location=e.location)} = + silver:core:error("Total result demanded when partial strategy failed") + in silver:core:fromMaybe(res, $Expr{e}) + end + }; +} + +aspect default production +top::StrategyExpr ::= +{ + -- At least 1 of these should be defined for every production: + top.partialTranslation = asPartial(top.totalTranslation); + top.totalTranslation = asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); + + top.attrRefName = nothing(); + top.matchesFrame := true; -- Consulted only when attrRefName is just(...) + top.isId = false; + top.isFail = false; + top.isTotal = false; + top.isTotalNoEnv = false; +} + +-- Basic combinators +abstract production id +top::StrategyExpr ::= +{ + top.unparse = "id"; + propagate liftedStrategies; + top.isId = true; + top.isTotal = true; + top.isTotalNoEnv = true; + top.totalTranslation = Silver_Expr { $name{top.frame.signature.outputElement.elementName} }; +} + +abstract production fail +top::StrategyExpr ::= +{ + top.unparse = "fail"; + propagate liftedStrategies; + top.isFail = true; + top.partialTranslation = Silver_Expr { silver:core:nothing() }; +} + +abstract production sequence +top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr +{ + top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; + + local s2Name::String = fromMaybe(top.genName ++ "_snd", s2.attrRefName); + local s2Total::Boolean = attrIsTotal(top.env, s2Name); -- Can differ from s2.isTotal because we lift without env + top.liftedStrategies := + s1.liftedStrategies ++ + if s2.attrRefName.isJust + then [] + else [pair(s2Name, s2)]; + top.isTotal = s1.isTotal && s2.isTotal; + top.isTotalNoEnv = s1.isTotalNoEnv && s2.isTotalNoEnv; + + s1.isOutermost = false; + s2.isOutermost = false; + + -- Equations for all inh attributes on the nt that we know about. + -- This is safe because the MWDA requires that all inh dependencies of a syn attribute + -- be exported by the syn occurence anyway. + -- TODO - future optimization potential: this is where common sub-trees shared between + -- the incoming tree and the result of s1 get re-decorated. + local allInhs::ExprInhs = + foldr( + exprInhsCons(_, _, location=top.location), + exprInhsEmpty(location=top.location), + map( + \ a::AttributeDclInfo -> + Silver_ExprInh { + $name{a.fullName} = $name{top.frame.signature.outputElement.elementName}.$name{a.fullName}; + }, + filter( + (.isInherited), + flatMap( + getAttrDcl(_, top.env), + map((.attrOccurring), getAttrsOn(top.frame.lhsNtName, top.env)))))); + top.partialTranslation = + -- Optimizations when one or both of these is total, in this case a + -- monadic bind may not be required. + case s1.isTotal, s2Total of + | true, true -> + Silver_Expr { + silver:core:just(decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name}) + } + | true, false -> + Silver_Expr { + decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name} + } + | false, true -> + Silver_Expr { + silver:core:map( + \ res::$TypeExpr{typerepTypeExpr(top.frame.signature.outputElement.typerep, location=top.location)} -> + decorate res with { $ExprInhs{allInhs} }.$name{s2Name}, + $Expr{s1.partialTranslation}) + } + | false, false -> + Silver_Expr { + silver:core:bind( + $Expr{s1.partialTranslation}, + \ res::$TypeExpr{typerepTypeExpr(top.frame.signature.outputElement.typerep, location=top.location)} -> + decorate res with { $ExprInhs{allInhs} }.$name{s2Name}) + } + end; + local totalTrans::Expr = + Silver_Expr { + decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name} + }; + top.totalTranslation = if s2Total then totalTrans else asTotal(top.frame.signature.outputElement.typerep, totalTrans); +} + +abstract production choice +top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr +{ + top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; + propagate liftedStrategies; + top.isTotal = s1.isTotal || s2.isTotal; + top.isTotalNoEnv = s1.isTotalNoEnv || s2.isTotalNoEnv; + + s1.isOutermost = false; + s2.isOutermost = false; + + top.partialTranslation = + Silver_Expr { + silver:core:orElse($Expr{s1.partialTranslation}, $Expr{s2.partialTranslation}) + }; + top.totalTranslation = + if s1.isTotal + then s1.totalTranslation + else + Silver_Expr { + silver:core:fromMaybe($Expr{s2.totalTranslation}, $Expr{s1.partialTranslation}) + }; +} + +-- Traversals +abstract production allTraversal +top::StrategyExpr ::= s::StrategyExpr +{ + top.unparse = s"all(${s.unparse})"; + + local sName::String = fromMaybe(top.genName ++ "_all_arg", s.attrRefName); + local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env + top.liftedStrategies := + if s.attrRefName.isJust + then [] + else [pair(sName, s)]; + top.isTotal = s.isTotal; + top.isTotalNoEnv = s.isTotalNoEnv; + + top.containsTraversal <- true; + + s.isOutermost = false; + s.frame = error("No frame for traversal strategies"); -- TODO: This equation shouldn't exist, but frame is an autocopy + + local sBaseName::String = last(explode(":", sName)); + -- pair(child name, attr occurs on child) + local childAccesses::[Pair] = + map( + \ e::NamedSignatureElement -> + pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), + top.frame.signature.inputElements); + top.partialTranslation = + if sTotal + then asPartial(top.totalTranslation) + else + {- Translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + case a.s, c.s of + | just(a_s), just(c_s) -> just(prod(a_s, b, c_s)) + | _, _ -> nothing() + end + Could also be implemented as chained monadic binds. Maybe more efficient this way? -} + caseExpr( + flatMap( + \ a::Pair -> + if a.snd then [Silver_Expr { $name{a.fst}.$name{sName} }] else [], + childAccesses), + [matchRule( + flatMap( + \ a::Pair -> + if a.snd + then + [decorate Silver_Pattern { silver:core:just($name{a.fst ++ "_" ++ sBaseName}) } + with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }] + else [], + childAccesses), + nothing(), + Silver_Expr { + silver:core:just( + $Expr{ + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> + if a.snd + then Silver_Expr { $name{a.fst ++ "_" ++ sBaseName} } + else Silver_Expr { $name{a.fst} }, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements))}) + }, + location=top.location)], + false, + Silver_Expr { silver:core:nothing() }, + appType(nonterminalType("silver:core:Maybe", [starKind()], false), top.frame.signature.outputElement.typerep), + location=top.location); + top.totalTranslation = + if sTotal + then + {- When s is total, optimized translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + prod(a.s, b, c.s) -} + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> + if a.snd + then Silver_Expr { $name{a.fst}.$name{sName} } + else Silver_Expr { $name{a.fst} }, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements)) + else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); +} + +abstract production someTraversal +top::StrategyExpr ::= s::StrategyExpr +{ + top.unparse = s"some(${s.unparse})"; + + local sName::String = fromMaybe(top.genName ++ "_some_arg", s.attrRefName); + local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env + top.liftedStrategies := + if s.attrRefName.isJust + then [] + else [pair(sName, s)]; + + top.containsTraversal <- true; + + s.isOutermost = false; + s.frame = error("No frame for traversal strategies"); -- TODO: This equation shouldn't exist, but frame is an autocopy + + -- pair(child name, attr occurs on child) + local childAccesses::[Pair] = + map( + \ e::NamedSignatureElement -> + pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), + top.frame.signature.inputElements); + local matchingChildren::[String] = map(fst, filter(snd, childAccesses)); + top.partialTranslation = + if sTotal + then + if !null(matchingChildren) + then asPartial(top.totalTranslation) + else Silver_Expr { silver:core:nothing() } + else + {- Translation of some(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + if a.s.isJust || c.s.isJust + then just(prod(fromMaybe(a, a.s), b, fromMaybe(c, c.s))) + else nothing() + Not sure of a clean way to do this with monads -} + Silver_Expr { + if $Expr{ + foldr( + or(_, '||', _, location=top.location), + falseConst('false', location=top.location), + map( + \ a::String -> Silver_Expr { $name{a}.$name{sName}.isJust }, + matchingChildren))} + then + silver:core:just( + $Expr{ + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> + if a.snd + then Silver_Expr { silver:core:fromMaybe($name{a.fst}, $name{a.fst}.$name{sName}) } + else Silver_Expr { $name{a.fst} }, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements))}) + else silver:core:nothing() + }; + top.totalTranslation = + if sTotal && !null(matchingChildren) + then + {- When s is total, optimized translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + prod(a.s, b, c.s) -} + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> + if a.snd + then Silver_Expr { $name{a.fst}.$name{sName} } + else Silver_Expr { $name{a.fst} }, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements)) + else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); +} + +abstract production oneTraversal +top::StrategyExpr ::= s::StrategyExpr +{ + top.unparse = s"one(${s.unparse})"; + + local sName::String = fromMaybe(top.genName ++ "_one_arg", s.attrRefName); + local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env + top.liftedStrategies := + if s.attrRefName.isJust + then [] + else [pair(sName, s)]; + + top.containsTraversal <- true; + + s.isOutermost = false; + s.frame = error("No frame for traversal strategies"); -- TODO: This equation shouldn't exist, but frame is an autocopy + + local sBaseName::String = last(explode(":", sName)); + -- pair(child name, attr occurs on child) + local childAccesses::[Pair] = + map( + \ e::NamedSignatureElement -> + pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), + top.frame.signature.inputElements); + local matchingChildren::[String] = map(fst, filter(snd, childAccesses)); + top.partialTranslation = + if sTotal + then + if !null(matchingChildren) + then asPartial(top.totalTranslation) + else Silver_Expr { silver:core:nothing() } + else + {- Translation of one(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + case a.s, c.s of + | just(a_s), _ -> just(prod(a_s, b, c)) + | _, just(c_s) -> just(prod(a, b, c_s)) + | _, _ -> nothing() + end + Could also be implemented as + orElse( + bind(a.s, \ a_s::Foo -> pure(prod(a_s, b, c))), + bind(c.s, \ c_s::Bar -> pure(prod(a, b, c_s))) -} + caseExpr( + map( + \ a::String -> Silver_Expr { $name{a}.$name{sName} }, + matchingChildren), + map( + \ i::Integer -> + let childI::String = head(drop(i, matchingChildren)) + in let childIndex::Integer = positionOf(childI, map(fst, childAccesses)) + in + matchRule( + map( + \ p::Pattern -> decorate p with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }, + repeat(wildcPattern('_', location=top.location), i) ++ + Silver_Pattern { silver:core:just($name{childI ++ "_" ++ sBaseName}) } :: + repeat(wildcPattern('_', location=top.location), length(matchingChildren) - (i + 1))), + nothing(), + Silver_Expr { + silver:core:just( + $Expr{ + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> Silver_Expr { $name{a.fst} }, + take(childIndex, childAccesses)) ++ + Silver_Expr { $name{childI ++ "_" ++ sBaseName} } :: + map( + \ a::Pair -> Silver_Expr { $name{a.fst} }, + drop(childIndex + 1, childAccesses)), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements))}) + }, + location=top.location) + end end, + range(0, length(matchingChildren))), + false, + Silver_Expr { silver:core:nothing() }, + appType(nonterminalType("silver:core:Maybe", [starKind()], false), top.frame.signature.outputElement.typerep), + location=top.location); + top.totalTranslation = + if sTotal && !null(matchingChildren) + then + {- When s is total, optimized translation of one(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): + prod(a.s, b, c) -} + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair -> + if a.fst == head(matchingChildren) + then Silver_Expr { $name{a.fst}.$name{sName} } + else Silver_Expr { $name{a.fst} }, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements)) + else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); +} + +abstract production prodTraversal +top::StrategyExpr ::= prod::QName s::StrategyExprs +{ + top.unparse = s"${prod.unparse}(${s.unparse})"; + + top.errors <- prod.lookupValue.errors; + + local numParams::Integer = length(s.givenInputElements); + local numArgs::Integer = length(s.attrRefNames); + top.errors <- + if prod.lookupValue.found && numArgs != numParams + then [err(top.location, s"Wrong number of arguments to ${prod.name}: expected ${toString(numParams)}, got ${toString(numArgs)}")] + else []; + + propagate liftedStrategies; + + s.givenInputElements = + if prod.lookupValue.found + then prod.lookupValue.dcl.namedSignature.inputElements + else []; + + top.containsTraversal <- true; + + -- pair(child name, if attr occurs on child then just(attr name) else nothing()) + local childAccesses::[Pair>] = + zipWith(pair, top.frame.signature.inputNames, s.attrRefNames); + top.partialTranslation = -- This is never total + if prod.lookupValue.fullName == top.frame.fullName + then + {- Translation of prod(s1, s2, s3, s4) for prod::(Foo ::= a::Foo b::Integer c::Bar d::Baz) + where s4 is total: + case a.s1, c.s3 of + | just(a_s1), just(c_s3) -> just(prod(a_s1, b, c_s3, d.s4)) + | _, _ -> nothing() + end + Could also be implemented as chained monadic binds. Maybe more efficient this way? -} + caseExpr( + flatMap( + \ a::Pair> -> + case a.snd of + | just(attr) when !attrIsTotal(top.env, attr) -> [Silver_Expr { $name{a.fst}.$name{attr} }] + | _ -> [] + end, + childAccesses), + [matchRule( + flatMap( + \ a::Pair> -> + case a.snd of + | just(attr) when !attrIsTotal(top.env, attr) -> + [decorate Silver_Pattern { silver:core:just($name{a.fst ++ "_" ++ last(explode(":", attr))}) } + with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }] + | _ -> [] + end, + childAccesses), + nothing(), + Silver_Expr { + silver:core:just( + $Expr{ + mkFullFunctionInvocation( + top.location, + baseExpr(qName(top.location, top.frame.fullName), location=top.location), + map( + \ a::Pair> -> + case a.snd of + | just(attr) when attrIsTotal(top.env, attr) -> Silver_Expr { $name{a.fst}.$name{attr} } + | just(attr) -> Silver_Expr { $name{a.fst ++ "_" ++ last(explode(":", attr))} } + | nothing() -> Silver_Expr { $name{a.fst} } + end, + childAccesses), + map( + makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), + top.frame.signature.namedInputElements))}) + }, + location=top.location)], + false, + Silver_Expr { silver:core:nothing() }, + appType(nonterminalType("silver:core:Maybe", [starKind()], false), top.frame.signature.outputElement.typerep), + location=top.location) + else Silver_Expr { silver:core:nothing() }; +} + +abstract production consStrategyExpr +top::StrategyExprs ::= h::StrategyExpr t::StrategyExprs +{ + top.unparse = s"${h.unparse}, ${t.unparse}"; + + top.liftedStrategies := + -- Slight hack: when h is id (common case for prod traversals), there is no need for a new attribute. + -- However this can't be avoided during the optimization phase, which happens after lifting. + -- So, just don't lift the strategy, and we won't find the occurence of the non-existant attribute + -- during translation - which means we will treat it as id anyway! + (if h.attrRefName.isJust || h.isId + then [] + else [pair(h.genName, h)]) ++ + t.liftedStrategies; + + local hType::Type = head(top.givenInputElements).typerep; + local attr::String = fromMaybe(h.genName, h.attrRefName); + local attrMatch::Boolean = attrMatchesFrame(top.env, attr, hType); + top.attrRefNames = + (if !null(top.givenInputElements) && attrMatch && !h.isId + then just(attr) + else nothing()) :: t.attrRefNames; + top.errors <- + if !null(top.givenInputElements) && !attrMatch && !h.isId + then [wrn(h.location, s"This (non-identity) strategy attribute does not occur on ${prettyType(hType)} and will be treated as identity")] + else []; + + top.containsFail <- h.isFail; + top.allId <- h.isId; + + h.isOutermost = false; + t.givenInputElements = + if !null(top.givenInputElements) then tail(top.givenInputElements) else []; +} + +abstract production nilStrategyExpr +top::StrategyExprs ::= +{ + top.unparse = ""; + top.liftedStrategies := []; + top.attrRefNames = []; +} + +-- Recursive strategies +abstract production recComb +top::StrategyExpr ::= n::Name s::StrategyExpr +{ + top.unparse = s"rec ${n.name} -> (${s.unparse})"; + + local sName::String = if top.isOutermost then top.outerAttr else top.genName ++ "_rec_body"; + top.liftedStrategies := + if top.isOutermost + then s.liftedStrategies + else [pair(sName, s)]; + top.freeRecVars := remove(n.name, s.freeRecVars); + + -- Decorate s assuming that the bound strategy is total, in order to check for totality. + -- See Fig 4 of the strategy attributes paper (https://www-users.cse.umn.edu/~evw/pubs/kramer20sle/kramer20sle.pdf) + local s2::StrategyExpr = s; + s2.recVarTotalEnv = pair(n.name, true) :: s.recVarTotalEnv; + s2.recVarTotalNoEnvEnv = pair(n.name, true) :: s.recVarTotalNoEnvEnv; + s2.env = s.env; + s2.config = s.config; + s2.grammarName = s.grammarName; + s2.recVarNameEnv = s.recVarNameEnv; + s2.outerAttr = s.outerAttr; + s2.isOutermost = top.isOutermost; + top.isTotal = s2.isTotal; + top.isTotalNoEnv = s2.isTotalNoEnv; + + s.recVarNameEnv = pair(n.name, sName) :: top.recVarNameEnv; + s.recVarTotalEnv = pair(n.name, top.isTotal) :: top.recVarTotalEnv; + s.recVarTotalNoEnvEnv = pair(n.name, top.isTotalNoEnv) :: top.recVarTotalNoEnvEnv; + s.isOutermost = top.isOutermost; + + local sTotal::Boolean = attrIsTotal(top.env, sName); + top.partialTranslation = + if top.isOutermost + then s.partialTranslation + else if sTotal + then asPartial(top.totalTranslation) + else Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$name{sName} }; + top.totalTranslation = + if top.isOutermost + then s.totalTranslation + else if sTotal + then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$name{sName} } + else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); +} + +-- Rules +abstract production rewriteRule +top::StrategyExpr ::= id::Name ty::TypeExpr ml::MRuleList +{ + top.unparse = "rule on " ++ id.name ++ "::" ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; + propagate liftedStrategies; + + -- Pattern matching error checking (mostly) happens on what caseExpr forwards to, + -- so we need to decorate one of those here. + production checkExpr::Expr = + letp( + assignExpr(id, '::', ty, '=', errorExpr([], location=top.location), location=top.location), + caseExpr( + [hackExprType(ty.typerep, location=top.location)], + -- TODO: matchRuleList on MRuleList depends on frame for some reason. + -- Re-decorate ml here as a workaround to avoid checkExpr depending on top.frame + decorate ml with { + env = top.env; + config = top.config; + matchRulePatternSize = 1; + frame = error("not needed"); + }.matchRuleList, false, + errorExpr([], location=top.location), + ty.typerep, + location=top.location), + location=top.location); + checkExpr.env = top.env; + checkExpr.flowEnv = top.flowEnv; + checkExpr.downSubst = emptySubst(); + checkExpr.finalSubst = checkExpr.upSubst; + checkExpr.grammarName = top.grammarName; + checkExpr.config = top.config; + checkExpr.compiledGrammars = top.compiledGrammars; + checkExpr.originRules = []; + checkExpr.isRoot = false; + + -- Frame doesn't really matter, since we will re-check any expressions occuring in ml when propagated. + -- Need all this to construct a bogus frame... + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + local myFlowGraph :: ProductionGraph = constructAnonymousGraph(checkExpr.flowDefs, top.env, myProds, myFlow); + checkExpr.frame = bogusContext(myFlowGraph, sourceGrammar=top.grammarName); + + top.errors := checkExpr.errors; + top.errors <- + if !isDecorable(ty.typerep, top.env) + then [wrn(ty.location, "Only rules on nonterminals can have an effect")] + else []; + top.errors <- ty.errorsKindStar; + + local res::Expr = + caseExpr( + [Silver_Expr { $name{top.frame.signature.outputElement.elementName} }], + ml.translation, false, + Silver_Expr { silver:core:nothing() }, + appType(nonterminalType("silver:core:Maybe", [starKind()], false), ty.typerep), + location=top.location); + top.partialTranslation = + if unify(ty.typerep, top.frame.signature.outputElement.typerep).failure + then Silver_Expr { silver:core:nothing() } + else if top.frame.signature.outputElement.elementName == id.name + then res + else Silver_Expr { + let $Name{id}::Decorated $TypeExpr{ty} = $name{top.frame.signature.outputElement.elementName} + in $Expr{res} + end + }; +} + +-- Hack dummy expr with a given type +abstract production hackExprType +top::Expr ::= t::Type +{ + top.typerep = t; + forwards to errorExpr([], location=top.location); +} + +attribute matchesFrame occurs on MRuleList, MatchRule, PatternList, Pattern; +propagate matchesFrame on MRuleList, MatchRule, PatternList; + +synthesized attribute translation::a; +attribute translation<[AbstractMatchRule]> occurs on MRuleList; + +aspect production mRuleList_one +top::MRuleList ::= m::MatchRule +{ + top.translation = [m.translation]; +} + +aspect production mRuleList_cons +top::MRuleList ::= h::MatchRule '|' t::MRuleList +{ + top.translation = h.translation :: t.translation; +} + +attribute translation occurs on MatchRule; + +aspect production matchRule_c +top::MatchRule ::= pt::PatternList _ e::Expr +{ + top.translation = + matchRule( + pt.patternList, nothing(), Silver_Expr { silver:core:just($Expr{e}) }, + location=top.location); +} + +aspect production matchRuleWhen_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr _ e::Expr +{ + top.translation = + matchRule( + pt.patternList, just(pair(cond, nothing())), Silver_Expr { silver:core:just($Expr{e}) }, + location=top.location); +} + +aspect production matchRuleWhenMatches_c +top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern _ e::Expr +{ + top.translation = + matchRule( + pt.patternList, just(pair(cond, just(p))), Silver_Expr { silver:core:just($Expr{e}) }, + location=top.location); +} + +aspect default production +top::Pattern ::= +{ + top.matchesFrame := true; +} + +aspect production prodAppPattern_named +top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' +{ + top.matchesFrame := prod.lookupValue.fullName == top.frame.fullName; +} + +-- References to other attributes or rec variables +abstract production nameRef +top::StrategyExpr ::= id::QName +{ + top.unparse = id.unparse; + + -- Forwarding depends on env here, these must be computed without env + propagate liftedStrategies; + top.attrRefName = just(fromMaybe(id.name, lookup(id.name, top.recVarNameEnv))); + top.isId = false; + top.isFail = false; + top.isTotalNoEnv = fromMaybe(false, lookup(id.name, top.recVarTotalNoEnvEnv)); + + local attrDcl::AttributeDclInfo = id.lookupAttribute.dcl; + forwards to + if lookup(id.name, top.recVarNameEnv).isJust + then recVarRef(id, genName=top.genName, location=top.location) + else if !null(id.lookupAttribute.errors) + then errorRef(id.lookupAttribute.errors, id, genName=top.genName, location=top.location) + else if attrIsTotal(top.env, id.name) + then totalRef(qNameAttrOccur(id, location=top.location), genName=top.genName, location=top.location) + else partialRef(qNameAttrOccur(id, location=top.location), genName=top.genName, location=top.location); +} +abstract production errorRef +top::StrategyExpr ::= msg::[Message] id::Decorated QName +{ + top.unparse = id.unparse; + + propagate liftedStrategies; + top.attrRefName = just(id.name); + + top.errors <- msg; + top.partialTranslation = Silver_Expr { silver:core:nothing() }; +} +abstract production recVarRef +top::StrategyExpr ::= id::Decorated QName +{ + top.unparse = id.unparse; + + propagate liftedStrategies; + top.attrRefName = lookup(id.name, top.recVarNameEnv); + top.isTotal = lookup(id.name, top.recVarTotalEnv).fromJust; + top.freeRecVars <- [id.name]; + + top.partialTranslation = + if attrIsTotal(top.env, top.attrRefName.fromJust) + then asPartial(top.totalTranslation) + else Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$qName{top.attrRefName.fromJust} }; + top.totalTranslation = + if attrIsTotal(top.env, top.attrRefName.fromJust) + then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$qName{top.attrRefName.fromJust} } + else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); +} +abstract production partialRef +top::StrategyExpr ::= attr::QNameAttrOccur +{ + top.unparse = attr.unparse; + + -- Lookup for error checking is *not* contextual, since we don't know the frame here + production attrDclFound::Boolean = case attr of qNameAttrOccur(a) -> a.lookupAttribute.found end; + production attrDcl::AttributeDclInfo = case attr of qNameAttrOccur(a) -> a.lookupAttribute.dcl end; + local attrTypeScheme::PolyType = attrDcl.typeScheme; + top.errors := + if !attrDcl.isSynthesized + then [err(attr.location, s"Attribute ${attr.name} cannot be used as a partial strategy, because it is not a synthesized attribute")] + else case attrTypeScheme.typerep, attrTypeScheme.boundVars of + | appType(nonterminalType("silver:core:Maybe", _, _), varType(a1)), [a2] when a1 == a2 && attrDcl.isSynthesized -> [] + | appType(nonterminalType("silver:core:Maybe", _, _), a), _ when pair(a.baseType, attrDcl.isSynthesized) matches pair(nonterminalType(nt, _, _), true) -> + if null(getOccursDcl(attrDcl.fullName, nt, top.env)) + then [wrn(attr.location, s"Attribute ${attr.name} cannot be used as a partial strategy, because it doesn't occur on its own nonterminal type ${nt}")] + else [] + | errorType(), _ -> [] + | _, _ -> [err(attr.location, s"Attribute ${attr.name} cannot be used as a partial strategy")] + end; + + propagate liftedStrategies; + top.attrRefName = just(attr.name); + top.matchesFrame := attr.matchesFrame; + top.isTotal = false; + top.partialRefs <- [attrDcl.fullName]; + + attr.attrFor = top.frame.signature.outputElement.typerep; + + top.partialTranslation = + if attr.matchesFrame + then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$QNameAttrOccur{attr} } + else Silver_Expr { silver:core:nothing() }; +} +abstract production totalRef +top::StrategyExpr ::= attr::QNameAttrOccur +{ + top.unparse = attr.unparse; + + -- Lookup for error checking is *not* contextual, since we don't know the frame here + production attrDclFound::Boolean = case attr of qNameAttrOccur(a) -> a.lookupAttribute.found end; + production attrDcl::AttributeDclInfo = case attr of qNameAttrOccur(a) -> a.lookupAttribute.dcl end; + local attrTypeScheme::PolyType = attrDcl.typeScheme; + top.errors := + if !attrDcl.isSynthesized + then [err(attr.location, s"Attribute ${attr.name} cannot be used as a total strategy, because it is not a synthesized attribute")] + else case attrTypeScheme.typerep.baseType, attrTypeScheme.boundVars of + | varType(a1), [a2] when a1 == a2 -> [] + | nonterminalType(nt, _, _), _ -> + if null(getOccursDcl(attrDcl.fullName, nt, top.env)) + then [wrn(attr.location, s"Attribute ${attr.name} cannot be used as a total strategy, because it doesn't occur on its own nonterminal type ${nt}")] + else [] + | errorType(), _ -> [] + | _, _ -> [err(attr.location, s"Attribute ${attr.name} cannot be used as a total strategy")] + end; + + propagate liftedStrategies; + top.attrRefName = just(attr.name); + top.matchesFrame := attr.matchesFrame; + top.isTotal = true; + top.isTotalNoEnv = true; + top.totalRefs <- [attrDcl.fullName]; + + attr.attrFor = top.frame.signature.outputElement.typerep; + + top.totalTranslation = Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$QNameAttrOccur{attr} }; +} + +-- The result of performing an inlining optimization +abstract production inlined +top::StrategyExpr ::= attr::Decorated QNameAttrOccur s::StrategyExpr +{ + top.unparse = s"(${s.unparse} aka ${attr.unparse})"; + propagate liftedStrategies; + top.attrRefName = just(attr.attrDcl.fullName); + top.isTotal = s.isTotal; + top.partialTranslation = + if attr.matchesFrame + then s.partialTranslation + else Silver_Expr { silver:core:nothing() }; + top.totalTranslation = s.totalTranslation; + + s.isOutermost = top.isOutermost; + s.inlinedStrategies = attr.attrDcl.fullName :: top.inlinedStrategies; +} + +attribute matchesFrame occurs on QNameAttrOccur; + +aspect production qNameAttrOccur +top::QNameAttrOccur ::= at::QName +{ + top.matchesFrame := top.found && + case top.typerep of + | appType(nonterminalType("silver:core:Maybe", _, _), t) -> !unify(top.attrFor, t).failure + | t -> !unify(top.attrFor, t).failure + end; +} + +function attrIsTotal +Boolean ::= env::Decorated Env attrName::String +{ + local dcls::[AttributeDclInfo] = getAttrDcl(attrName, env); + return + case dcls of + | [] -> false + | d :: _ -> + case d.typeScheme.typerep of + | appType(nonterminalType("silver:core:Maybe", _, _), _) -> false + | _ -> true + end + end; +} + +function attrMatchesFrame +Boolean ::= env::Decorated Env attrName::String attrFor::Type +{ + return + decorate qNameAttrOccur(qName(loc("", -1, -1, -1, -1, -1, -1), attrName), location=loc("", -1, -1, -1, -1, -1, -1)) + with { env = env; attrFor = attrFor; }.matchesFrame; +} + +function attrMatchesChild +Boolean ::= env::Decorated Env attrName::String frame::BlockContext +{ + return + any( + map( + \ e::NamedSignatureElement -> attrMatchesFrame(env, attrName, e.typerep), + frame.signature.inputElements)); +} + +instance Eq StrategyExpr { + eq = \ s1::StrategyExpr s2::StrategyExpr -> s1.unparse == s2.unparse; +} diff --git a/grammars/silver/extension/strategyattr/StrategyUtils.sv b/grammars/silver/compiler/extension/strategyattr/StrategyUtils.sv similarity index 76% rename from grammars/silver/extension/strategyattr/StrategyUtils.sv rename to grammars/silver/compiler/extension/strategyattr/StrategyUtils.sv index 9a0648965..f34400f2f 100644 --- a/grammars/silver/extension/strategyattr/StrategyUtils.sv +++ b/grammars/silver/compiler/extension/strategyattr/StrategyUtils.sv @@ -1,7 +1,7 @@ -grammar silver:extension:strategyattr; +grammar silver:compiler:extension:strategyattr; -import silver:metatranslation; -import silver:modification:copper; -- print keyword +import silver:compiler:metatranslation; +import silver:compiler:modification:copper; -- print keyword -- Debugging abstract production printTerm @@ -13,15 +13,16 @@ top::StrategyExpr ::= top.isTotal = true; top.totalTranslation = Silver_Expr { - core:unsafeTrace( + silver:core:unsafeTrace( $name{top.frame.signature.outputElement.elementName}, - core:print( + silver:core:print( hackUnparse($name{top.frame.signature.outputElement.elementName}) ++ "\n\n", - core:unsafeIO())) + silver:core:unsafeIO())) }; } -- Utilities +-- Note that for the translation to work properly, we need to maintain forward.genName == top.genName abstract production try top::StrategyExpr ::= s::StrategyExpr { @@ -34,7 +35,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production repeatS -- name clash with repeat from core top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "repeat_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_repeat_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> try($StrategyExpr{s} <* $strategyQName{recVarName}) @@ -44,7 +45,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production reduce top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "reduce_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_reduce_rec"; forwards to Silver_StrategyExpr (top.genName) { repeat(rec $name{recVarName} -> some($strategyQName{recVarName}) <+ $StrategyExpr{s}) @@ -54,7 +55,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production bottomUp top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "bottomUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_bottomUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> all($strategyQName{recVarName}) <* $StrategyExpr{s} @@ -64,7 +65,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production topDown top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "topDown_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_topDown_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s} <* all($strategyQName{recVarName}) @@ -74,7 +75,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production downUp top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr { - local recVarName::String = "downUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_downUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s1} <* all($strategyQName{recVarName}) <* $StrategyExpr{s2} @@ -84,7 +85,7 @@ top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr abstract production allBottomUp top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "allBottomUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_allBottomUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> all($strategyQName{recVarName}) <+ $StrategyExpr{s} @@ -94,7 +95,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production allTopDown top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "allTopDown_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_allTopDown_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s} <+ all($strategyQName{recVarName}) @@ -104,7 +105,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production allDownUp top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr { - local recVarName::String = "allDownUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_allDownUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s1} <+ all($strategyQName{recVarName}) <+ $StrategyExpr{s2} @@ -114,7 +115,7 @@ top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr abstract production someBottomUp top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "someBottomUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_someBottomUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> some($strategyQName{recVarName}) <+ $StrategyExpr{s} @@ -124,7 +125,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production someTopDown top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "someTopDown_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_someTopDown_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s} <+ some($strategyQName{recVarName}) @@ -134,7 +135,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production someDownUp top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr { - local recVarName::String = "someDownUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_someDownUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s1} <+ some($strategyQName{recVarName}) <+ $StrategyExpr{s2} @@ -144,7 +145,7 @@ top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr abstract production onceBottomUp top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "onceBottomUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_onceBottomUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> one($strategyQName{recVarName}) <+ $StrategyExpr{s} @@ -154,7 +155,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production onceTopDown top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "onceTopDown_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_onceTopDown_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s} <+ one($strategyQName{recVarName}) @@ -164,7 +165,7 @@ top::StrategyExpr ::= s::StrategyExpr abstract production onceDownUp top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr { - local recVarName::String = "onceDownUp_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_onceDownUp_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> $StrategyExpr{s1} <+ one($strategyQName{recVarName}) <+ $StrategyExpr{s2} @@ -174,7 +175,7 @@ top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr abstract production innermost top::StrategyExpr ::= s::StrategyExpr { - local recVarName::String = "innermost_" ++ toString(genInt()); + local recVarName::String = top.genName ++ "_innermost_rec"; forwards to Silver_StrategyExpr (top.genName) { rec $name{recVarName} -> bottomUp(try($StrategyExpr{s} <* $strategyQName{recVarName})) diff --git a/grammars/silver/compiler/extension/strategyattr/Terminals.sv b/grammars/silver/compiler/extension/strategyattr/Terminals.sv new file mode 100644 index 000000000..c5fa10b46 --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/Terminals.sv @@ -0,0 +1,38 @@ +grammar silver:compiler:extension:strategyattr; + +terminal Strategy_kwd 'strategy' lexer classes {KEYWORD, RESERVED}; +terminal Partial_kwd 'partial' lexer classes {KEYWORD, RESERVED}; + +terminal Sequence_t '<*' precedence = 12, association = left; -- Same as * +terminal Choice_t '<+' precedence = 11, association = left; -- Same as + + +lexer class Strategy dominates StrategyName_t; + +terminal Id_t 'id' lexer classes {KEYWORD, Strategy}; +terminal Fail_t 'fail' lexer classes {KEYWORD, Strategy}; +terminal All_t 'all' lexer classes {KEYWORD, Strategy}; +terminal Some_t 'some' lexer classes {KEYWORD, Strategy}; +terminal One_t 'one' lexer classes {KEYWORD, Strategy}; +terminal Rule_t 'rule' lexer classes {KEYWORD, Strategy}; +terminal Rec_t 'rec' lexer classes {KEYWORD, Strategy}; + +terminal PrintTerm_t 'printTerm' lexer classes {KEYWORD, Strategy}; +terminal Try_t 'try' lexer classes {KEYWORD, Strategy}; +terminal Repeat_t 'repeat' lexer classes {KEYWORD, Strategy}; +terminal Reduce_t 'reduce' lexer classes {KEYWORD, Strategy}; +terminal BottomUp_t 'bottomUp' lexer classes {KEYWORD, Strategy}; +terminal TopDown_t 'topDown' lexer classes {KEYWORD, Strategy}; +terminal DownUp_t 'downUp' lexer classes {KEYWORD, Strategy}; +terminal AllBottomUp_t 'allBottomUp' lexer classes {KEYWORD, Strategy}; +terminal AllTopDown_t 'allTopDown' lexer classes {KEYWORD, Strategy}; +terminal AllDownUp_t 'allDownUp' lexer classes {KEYWORD, Strategy}; +terminal SomeBottomUp_t 'someBottomUp' lexer classes {KEYWORD, Strategy}; +terminal SomeTopDown_t 'someTopDown' lexer classes {KEYWORD, Strategy}; +terminal SomeDownUp_t 'someDownUp' lexer classes {KEYWORD, Strategy}; +terminal OnceBottomUp_t 'onceBottomUp' lexer classes {KEYWORD, Strategy}; +terminal OnceTopDown_t 'onceTopDown' lexer classes {KEYWORD, Strategy}; +terminal OnceDownUp_t 'onceDownUp' lexer classes {KEYWORD, Strategy}; +terminal Innermost_t 'innermost' lexer classes {KEYWORD, Strategy}; +terminal Outermost_t 'outermost' lexer classes {KEYWORD, Strategy}; + +terminal StrategyName_t /[a-z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; diff --git a/grammars/silver/extension/strategyattr/construction/Construction.sv b/grammars/silver/compiler/extension/strategyattr/construction/Construction.sv similarity index 86% rename from grammars/silver/extension/strategyattr/construction/Construction.sv rename to grammars/silver/compiler/extension/strategyattr/construction/Construction.sv index 5300ec7cc..ac4cf220d 100644 --- a/grammars/silver/extension/strategyattr/construction/Construction.sv +++ b/grammars/silver/compiler/extension/strategyattr/construction/Construction.sv @@ -1,11 +1,11 @@ -grammar silver:extension:strategyattr:construction; +grammar silver:compiler:extension:strategyattr:construction; -imports silver:definition:core; -imports silver:extension:strategyattr; -imports silver:extension:silverconstruction; +imports silver:compiler:definition:core; +imports silver:compiler:extension:strategyattr; +imports silver:compiler:extension:silverconstruction; imports silver:reflect; -imports silver:metatranslation; +imports silver:compiler:metatranslation; imports silver:rewrite as s; imports silver:langutil:pp; @@ -27,7 +27,7 @@ top::Expr ::= 'Silver_StrategyExpr' '(' genName::Expr ')' '{' cst::StrategyExpr_ -- in one pass, but we want to keep that code as a generic library as much as possible. cst.givenGenName = ""; forwards to - rewriteWith( + s:rewriteWith( s:allTopDown( rule on AnnoExpr of | annoExpr(n, _, presentAppExpr(e), location=l) when n.name == "genName" -> @@ -60,5 +60,5 @@ aspect production nonterminalAST top::AST ::= prodName::String children::ASTs annotations::NamedASTs { directAntiquoteProductions <- - ["silver:extension:strategyattr:construction:antiquoteStrategyExpr"]; + ["silver:compiler:extension:strategyattr:construction:antiquoteStrategyExpr"]; } diff --git a/grammars/silver/compiler/extension/strategyattr/convenience/Convenience.sv b/grammars/silver/compiler/extension/strategyattr/convenience/Convenience.sv new file mode 100644 index 000000000..dea88df5d --- /dev/null +++ b/grammars/silver/compiler/extension/strategyattr/convenience/Convenience.sv @@ -0,0 +1,31 @@ +grammar silver:compiler:extension:strategyattr:convenience; + +import silver:compiler:extension:strategyattr; +import silver:compiler:extension:convenience; +import silver:compiler:definition:core; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:env; + +concrete production partialStrategyAttributeDclMultiple +top::AGDcl ::= 'partial' 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "partial strategy attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + partialStrategyAttributeDcl($1, $2, $3, a, $5, e, $10, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} + +concrete production totalStrategyAttributeDclMultiple +top::AGDcl ::= 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "strategy attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; + forwards to + appendAGDcl( + totalStrategyAttributeDcl($1, $2, a, $4, e, $9, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), + location=top.location); +} diff --git a/grammars/silver/compiler/extension/templating/StringTemplating.sv b/grammars/silver/compiler/extension/templating/StringTemplating.sv new file mode 100644 index 000000000..d677a7aea --- /dev/null +++ b/grammars/silver/compiler/extension/templating/StringTemplating.sv @@ -0,0 +1,200 @@ +grammar silver:compiler:extension:templating; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:metatranslation; +imports silver:reflect; +imports silver:langutil:pp; +imports silver:langutil:lsp as lsp; + +imports silver:compiler:translation:java:core; + +exports silver:compiler:extension:templating:syntax; + +import silver:util:treeset as ts; + +terminal Template_kwd 's"""' lexer classes {LITERAL, lsp:String_}; +terminal SLTemplate_kwd 's"' lexer classes {LITERAL, lsp:String_}; + +concrete production templateExpr +top::Expr ::= Template_kwd t::TemplateString +layout {} +{ + forwards to foldr1(stringAppendCall(_, _, location=top.location), t.stringTemplate); +} + +concrete production singleLineTemplateExpr +top::Expr ::= SLTemplate_kwd t::SingleLineTemplateString +layout {} +{ + forwards to foldr1(stringAppendCall(_, _, location=top.location), t.stringTemplate); +} + +production stringAppendCall +top::Expr ::= a::Expr b::Expr +{ + top.unparse = s"${a.unparse} ++ ${b.unparse}"; + propagate freeVars; + + -- TODO: We really need eagerness analysis in Silver. + -- Otherwise the translation for a large string template block contains + -- new common.Thunk(new common.Thunk.Evaluable() { public final Object eval() { return ((common.StringCatter)silver.core.PstringAppend.invoke(${a.translation}, ${b.translation}); } }) + -- a ridiculous number of times, when it can just be translated as: + top.translation = s"new common.StringCatter(${a.translation}, ${b.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); + + thread downSubst, upSubst on top, a, b, forward; + + forwards to + mkStrFunctionInvocation( + top.location, "silver:core:stringAppend", + [exprRef(a, location=a.location), exprRef(b, location=b.location)]); +} + +terminal PPTemplate_kwd 'pp"""' lexer classes {LITERAL, lsp:String_}; +terminal SLPPTemplate_kwd 'pp"' lexer classes {LITERAL, lsp:String_}; + +-- These are translated by building a Document value and meta-translating the whole thing into an Expr +concrete production pptemplateExpr +top::Expr ::= PPTemplate_kwd t::TemplateString +layout {} +{ + forwards to translate(top.location, reflect(t.ppTemplate)); +} + +concrete production singleLinepptemplateExpr +top::Expr ::= SLPPTemplate_kwd t::SingleLineTemplateString +layout {} +{ + forwards to translate(top.location, reflect(t.ppTemplate)); +} + +-- Antiquote production used in translating nonwater Exprs that should get directly embedded in the result. +-- See fig 9 of https://doi.org/10.1016/j.cola.2021.101033 (https://www-users.cse.umn.edu/~evw/pubs/kramer21cola/kramer21cola.pdf) +production antiquoteDoc +top::Document ::= e::Expr +{ forwards to error("No forward"); } + +aspect production nonterminalAST +top::AST ::= _ _ _ +{ + directAntiquoteProductions <- ["silver:compiler:extension:templating:antiquoteDoc"]; +} + +synthesized attribute stringTemplate :: [Expr] occurs on TemplateString, SingleLineTemplateString, + TemplateStringBody, SingleLineTemplateStringBody, + TemplateStringBodyItem, SingleLineTemplateStringBodyItem, NonWater; +synthesized attribute ppTemplate :: Document occurs on TemplateString, SingleLineTemplateString, + TemplateStringBody, SingleLineTemplateStringBody, + TemplateStringBodyItem, SingleLineTemplateStringBodyItem, NonWater; + +aspect production templateString +top::TemplateString ::= b::TemplateStringBody _ +{ + top.stringTemplate = b.stringTemplate; + top.ppTemplate = b.ppTemplate; +} + +aspect production templateStringEmpty +top::TemplateString ::= _ +{ + top.stringTemplate = [stringConst(terminal(String_t, "\"\"", top.location), location=top.location)]; + top.ppTemplate = notext(); +} + +aspect production singleLineTemplateString +top::SingleLineTemplateString ::= b::SingleLineTemplateStringBody _ +{ + top.stringTemplate = b.stringTemplate; + top.ppTemplate = b.ppTemplate; +} + +aspect production singleLineTemplateStringEmpty +top::SingleLineTemplateString ::= _ +{ + top.stringTemplate = [stringConst(terminal(String_t, "\"\"", top.location), location=top.location)]; + top.ppTemplate = notext(); +} + +aspect production bodyCons +top::TemplateStringBody ::= h::TemplateStringBodyItem t::TemplateStringBody +{ + top.stringTemplate = h.stringTemplate ++ t.stringTemplate; + top.ppTemplate = cat(h.ppTemplate, t.ppTemplate); +} + +aspect production bodyOne +top::TemplateStringBody ::= h::TemplateStringBodyItem +{ + top.stringTemplate = h.stringTemplate; + top.ppTemplate = h.ppTemplate; +} + +aspect production bodyOneWater +top::TemplateStringBody ::= w::Water +{ + top.stringTemplate = [stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)]; + top.ppTemplate = w.waterDoc; +} + +aspect production singleLineBodyCons +top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem t::SingleLineTemplateStringBody +{ + top.stringTemplate = h.stringTemplate ++ t.stringTemplate; + top.ppTemplate = cat(h.ppTemplate, t.ppTemplate); +} + +aspect production singleLineBodyOne +top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem +{ + top.stringTemplate = h.stringTemplate; + top.ppTemplate = h.ppTemplate; +} + +aspect production singleLineBodyOneWater +top::SingleLineTemplateStringBody ::= w::SingleLineWater +{ + top.stringTemplate = [stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)]; + top.ppTemplate = w.waterDoc; +} + +aspect production itemWaterEscape +top::TemplateStringBodyItem ::= w::Water nw::NonWater +{ + top.stringTemplate = [ + stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)] ++ + nw.stringTemplate; + top.ppTemplate = cat(w.waterDoc, nw.ppTemplate); +} + +aspect production itemEscape +top::TemplateStringBodyItem ::= nw::NonWater +{ + top.stringTemplate = nw.stringTemplate; + top.ppTemplate = nw.ppTemplate; +} + +aspect production singleLineItemWaterEscape +top::SingleLineTemplateStringBodyItem ::= w::SingleLineWater nw::NonWater +{ + top.stringTemplate = [ + stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)] ++ + nw.stringTemplate; + top.ppTemplate = cat(w.waterDoc, nw.ppTemplate); +} + +aspect production singleLineItemEscape +top::SingleLineTemplateStringBodyItem ::= nw::NonWater +{ + top.stringTemplate = nw.stringTemplate; + top.ppTemplate = nw.ppTemplate; +} + +aspect production nonwater +top::NonWater ::= '${' e::Expr '}' +{ + top.stringTemplate = [e]; + top.ppTemplate = antiquoteDoc(mkStrFunctionInvocation(top.location, "silver:langutil:pp:pp", [e])); +} diff --git a/grammars/silver/compiler/extension/templating/syntax/Templating.sv b/grammars/silver/compiler/extension/templating/syntax/Templating.sv new file mode 100644 index 000000000..476dcc2a0 --- /dev/null +++ b/grammars/silver/compiler/extension/templating/syntax/Templating.sv @@ -0,0 +1,235 @@ +grammar silver:compiler:extension:templating:syntax; + +imports silver:compiler:definition:core; +imports silver:langutil:pp; +imports silver:langutil:lsp as lsp; + +terminal TripleQuote /\"\"\"/ lexer classes {LITERAL, lsp:String_}; +terminal DoubleDollar '$$' lexer classes {LITERAL}; +terminal QuoteWater /[^$\r\n\t\"\\]+/ lexer classes {LITERAL, lsp:String_}; +terminal SingleLineQuoteWater /([^$\r\n\t\"\\]|[\\][\"]|[\\][\\]|[\\]b|[\\]r|[\\]f|[\\]t)+/ lexer classes {LITERAL, lsp:String_}; +terminal LiteralNewline /(\n|\r\n)/ lexer classes {LITERAL, lsp:String_}; +terminal LiteralTab /\t/ lexer classes {LITERAL, lsp:String_}; +terminal LiteralQuote /\"/ lexer classes {LITERAL, lsp:String_}; +terminal LiteralBackslash /\\/ lexer classes {LITERAL, lsp:String_}; +terminal LiteralBackslashN /\\n/ lexer classes {LITERAL, lsp:String_}; + +terminal OpenEscape '${'; + +{-- A string without the first triple quote, with escaped expressions within -} +nonterminal TemplateString with location; +{-- A single-line string without the first quote, with escaped expressions within -} +nonterminal SingleLineTemplateString with location; +{-- A list of alternating String/Exprs -} +nonterminal TemplateStringBody with location; +{-- A single-line list of alternating String/Exprs -} +nonterminal SingleLineTemplateStringBody with location; +{-- Either a String or an Expr -} +nonterminal TemplateStringBodyItem with location; +{-- Either a single-line String or an Expr -} +nonterminal SingleLineTemplateStringBodyItem with location; +{-- An escape -} +nonterminal NonWater with location; +{-- List that yields a string -} +nonterminal Water with location, waterString, waterDoc; +{-- List that yields a single-line string -} +nonterminal SingleLineWater with location, waterString, waterDoc; +{-- Components that yield a string -} +nonterminal WaterItem with location, waterString, waterDoc; +{-- Components that yield a single-line string -} +nonterminal SingleLineWaterItem with location, waterString, waterDoc; + +{-- The string corresponding to the water -} +synthesized attribute waterString :: String; +{-- The Document corresponding to the water -} +synthesized attribute waterDoc :: Document; + +concrete production templateString +top::TemplateString ::= b::TemplateStringBody TripleQuote +{ +} + +concrete production templateStringEmpty +top::TemplateString ::= TripleQuote +{ +} + +concrete production singleLineTemplateString +top::SingleLineTemplateString ::= b::SingleLineTemplateStringBody LiteralQuote +{ +} + +concrete production singleLineTemplateStringEmpty +top::SingleLineTemplateString ::= LiteralQuote +{ +} + +concrete production bodyCons +top::TemplateStringBody ::= h::TemplateStringBodyItem t::TemplateStringBody +{ +} + +concrete production bodyOne +top::TemplateStringBody ::= h::TemplateStringBodyItem +{ +} + +concrete production bodyOneWater +top::TemplateStringBody ::= h::Water +{ +} + +concrete production singleLineBodyCons +top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem t::SingleLineTemplateStringBody +{ +} + +concrete production singleLineBodyOne +top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem +{ +} + +concrete production singleLineBodyOneWater +top::SingleLineTemplateStringBody ::= h::SingleLineWater +{ +} + +concrete production itemWaterEscape +top::TemplateStringBodyItem ::= w::Water nw::NonWater +{ +} + +concrete production itemEscape +top::TemplateStringBodyItem ::= nw::NonWater +{ +} + +concrete production singleLineItemWaterEscape +top::SingleLineTemplateStringBodyItem ::= w::SingleLineWater nw::NonWater +{ +} + +concrete production singleLineItemEscape +top::SingleLineTemplateStringBodyItem ::= nw::NonWater +{ +} + +concrete production nonwater +top::NonWater ::= '${' e::Expr '}' +layout {BlockComments, Comments, WhiteSpace} +{ +} + +concrete production waterCons +top::Water ::= h::Water t::WaterItem +{ + top.waterString = h.waterString ++ t.waterString; + top.waterDoc = flatCat(h.waterDoc, t.waterDoc); +} + +concrete production waterOne +top::Water ::= h::WaterItem +{ + top.waterString = h.waterString; + top.waterDoc = h.waterDoc; +} + +concrete production water +top::WaterItem ::= w::QuoteWater +{ + top.waterString = w.lexeme; + top.waterDoc = text(w.lexeme); +} + +concrete production waterDollar +top::WaterItem ::= '$$' +{ + top.waterString = "$"; + top.waterDoc = text("$"); +} + +concrete production waterBackSlash +top::WaterItem ::= LiteralBackslash +{ + -- The reason I decided to make backslashes not "work" is due to + -- dealing with \" Originally, this turned into \\" in the string + -- because the quote got escaped... this of course, was disaster." + top.waterString = "\\\\"; + top.waterDoc = text("\\"); +} + +concrete production waterNewline +top::WaterItem ::= LiteralNewline +{ + -- We always interpret newlines as just \n, even if the source file was \r\n. + top.waterString = "\\n"; + top.waterDoc = realLine(); +} + +concrete production waterTab +top::WaterItem ::= LiteralTab +{ + top.waterString = "\\t"; + top.waterDoc = text("\t"); +} + +concrete production waterQuote +top::WaterItem ::= LiteralQuote +{ + top.waterString = "\\\""; + top.waterDoc = text("\""); +} + +concrete production singleLineWaterCons +top::SingleLineWater ::= h::SingleLineWater t::SingleLineWaterItem +{ + top.waterString = h.waterString ++ t.waterString; + top.waterDoc = flatCat(h.waterDoc, t.waterDoc); +} + +concrete production singleLineWaterOne +top::SingleLineWater ::= h::SingleLineWaterItem +{ + top.waterString = h.waterString; + top.waterDoc = h.waterDoc; +} + +concrete production singleLineWater +top::SingleLineWaterItem ::= w::SingleLineQuoteWater +{ + top.waterString = w.lexeme; + top.waterDoc = text(unescapeString(w.lexeme)); -- Need to interpret escape sequences besides \\ +} + +concrete production singleLineWaterDollar +top::SingleLineWaterItem ::= '$$' +{ + top.waterString = "$"; + top.waterDoc = text("$"); +} + +-- Seperated from singleLineWater since we don't want newlines in text() +concrete production singleLineWaterNewline +top::SingleLineWaterItem ::= LiteralBackslashN +{ + top.waterString = "\\n"; + top.waterDoc = realLine(); +} + +concrete production singleLineWaterBackSlash +top::SingleLineWaterItem ::= LiteralBackslash +{ + -- Same as waterBackSlash + top.waterString = "\\\\"; + top.waterDoc = text("\\"); +} + +function flatCat +Document ::= d1::Document d2::Document +{ + return + case d1, d2 of + | text(s1), text(s2) -> text(s1 ++ s2) + | _, _ -> cat(d1, d2) + end; +} \ No newline at end of file diff --git a/grammars/silver/compiler/extension/testing/EqualityTest.sv b/grammars/silver/compiler/extension/testing/EqualityTest.sv new file mode 100644 index 000000000..de8cae375 --- /dev/null +++ b/grammars/silver/compiler/extension/testing/EqualityTest.sv @@ -0,0 +1,189 @@ +grammar silver:compiler:extension:testing; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:collection; +import silver:compiler:modification:list; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; -- for the "oh no again!" hack below +import silver:compiler:driver:util only RootSpec; -- ditto + +--import silver:compiler:analysis:typechecking:core; + + +terminal EqualityTest_t 'equalityTest' lexer classes {KEYWORD}; + +concrete production equalityTest2_p +ag::AGDcl ::= kwd::'equalityTest' + '(' value::Expr ',' expected::Expr ',' + valueType::TypeExpr ',' testSuite::Name ')' ';' +{ + ag.unparse = "equalityTest (" ++ value.unparse ++ "," ++ expected.unparse ++ ",\n" ++ + " " ++ valueType.unparse ++ ", " ++ testSuite.unparse ++ ");\n"; + + local attribute errCheck1 :: TypeCheck; + local attribute errCheck2 :: TypeCheck; + local attribute errCheck3 :: TypeCheck; + errCheck1 = check(value.typerep, expected.typerep); + errCheck2 = check(value.typerep, valueType.typerep); + errCheck3 = check(expected.typerep, valueType.typerep); + + production attribute localErrors::[Message] with ++; + localErrors := value.errors ++ expected.errors ++ valueType.errors; + localErrors <- + if !errCheck1.typeerror then [] + else [err(value.location, "Type of first and second expressions in equalityTest do not match. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)]; + localErrors <- + if !errCheck2.typeerror then [] + else [err(value.location, "Type of initial expression does not match specified type (3rd argument). Instead they are " ++ errCheck2.leftpp ++ " and " ++ errCheck2.rightpp)]; + localErrors <- + if !errCheck3.typeerror then [] + else [err(value.location, "Type of second expression does not match specified type (3rd argument). Instead they are " ++ errCheck3.leftpp ++ " and " ++ errCheck3.rightpp)]; + + local eqCtx::Context = instContext("silver:core:Eq", valueType.typerep); + eqCtx.env = ag.env; + eqCtx.contextLoc = valueType.location; + eqCtx.contextSource = "equalityTest"; + eqCtx.frame = value.frame; + eqCtx.config = ag.config; + eqCtx.grammarName = ag.grammarName; + eqCtx.compiledGrammars = ag.compiledGrammars; + localErrors <- eqCtx.contextErrors; + + value.downSubst = emptySubst(); + thread downSubst, upSubst on value, expected, errCheck1, errCheck2, errCheck3; + + value.finalSubst = errCheck3.upSubst; + expected.finalSubst = errCheck3.upSubst; + errCheck1.finalSubst = errCheck3.upSubst; + errCheck2.finalSubst = errCheck3.upSubst; + errCheck3.finalSubst = errCheck3.upSubst; + + -- TODO: one of those type error checks above is redundant + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(ag.grammarName, ag.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(ag.grammarName, ag.compiledGrammars)).productionFlowGraphs; + + value.frame = bogusContext(constructAnonymousGraph(value.flowDefs, ag.env, myProds, myFlow), sourceGrammar=ag.grammarName); + expected.frame = bogusContext(constructAnonymousGraph(expected.flowDefs, ag.env, myProds, myFlow), sourceGrammar=ag.grammarName); + + value.isRoot = true; + expected.isRoot = true; + value.originRules = []; + expected.originRules = []; + +{- Causes some circularities with the environment. TODO + forwards to if !errCheck1.typeerror && !errCheck2.typeerror && !errCheck3.typeerror + then appendAGDcl(absProdCS, aspProdCS) + else emptyAGDcl(); +-} + ag.errors := if null(localErrors) then forward.errors else localErrors; + + forwards to appendAGDcl(absProdCS, aspProdCS, location=ag.location); + +{- + local absProdCS :: AGDcl = asAGDcl ( + "abstract production " ++ testName ++ "\n" ++ + "t::Test ::= \n" ++ + "{ \n" ++ + " local attribute value :: %%%Type valueType; \n" ++ + " value = %%%Expr value; \n" ++ + " local attribute expected :: %%%Type valueType; \n" ++ + " expected = %%%Expr expected; \n" ++ + " t.msg = \"Test at " ++ ag.location.unparse ++ " failed. \\n\" ++ \n" ++ + " \"Checking that expression \\n\" ++ \n" ++ + " \" " ++ stringifyString(value.unparse) ++ "\" ++ \n" ++ + " \"\\nshould be same as expression \\n\" ++ \n" ++ + " \" " ++ stringifyString(expected.unparse) ++ "\\n\" ++ \n" ++ + " \"Actual value: \\n \" ++ \n" ++ + " %%%Expr toStringValueExpr ++ \"\\n\" ++ \n" ++ + " \"Expected value: \\n \" ++ \n" ++ + " %%%Expr toStringExpectedExpr ++ \"\\n\" ++ \n" ++ + " \"\";\n" ++ + " t.pass = %%%Expr equalityTestCode; \n" ++ + " forwards to defTest(); \n" ++ + "}" , + cons_CS_env("value", wrapExpr(value), + cons_CS_env("expected", wrapExpr(expected), + cons_CS_env("valueType", wrapType(valueType), + cons_CS_env("testSuite", wrapName(testSuite), + cons_CS_env("toStringValueExpr", + wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,toStringValueExpr)), + cons_CS_env("toStringExpectedExpr", + wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,toStringExpectedExpr)), + cons_CS_env("equalityTestCode", + wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,equalityTestExpr)) , + empty_CS_env()))))))) , 3 ); +-} + + -- TODO: BUG: FIXME: these names should be mangled. I ran into 't' being shadowed in a test I wrote! + local tref :: Name = name("t", ag.location); + local testNameref :: Name = name(testName, ag.location); + local valueref :: Name = name("value", ag.location); + local expectedref :: Name = name("expected", ag.location); + local msgref :: Name = name("msg", ag.location); + local passref :: Name = name("pass", ag.location); + + -- TODO: Rewrite as Silver_AGDcl { ... } + local absProdCS :: AGDcl = + productionDcl('abstract', 'production', testNameref, + productionSignature( + nilConstraint(location=ag.location), '=>', + productionLHS(tref, '::', + nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Test", ag.location), location=ag.location), location=ag.location), location=ag.location), + '::=', productionRHSNil(location=ag.location), location=ag.location), + productionBody('{', foldl(productionStmtsSnoc(_, _, location=ag.location), productionStmtsNil(location=ag.location), [ + localAttributeDcl('local', 'attribute', valueref, '::', valueType, ';', location=ag.location), + valueEq(qNameId(valueref, location=valueref.location), '=', value, ';', location=ag.location), + localAttributeDcl('local', 'attribute', expectedref, '::', valueType, ';', location=ag.location), + valueEq(qNameId(expectedref, location=expectedref.location), '=', expected, ';', location=ag.location), + attributeDef(concreteDefLHS(qNameId(tref, location=tref.location), location=tref.location), '.', qNameAttrOccur(qNameId(msgref, location=msgref.location), location=ag.location), '=', + foldStringExprs([ + strCnst("Test at " ++ ag.location.unparse ++ " failed.\nChecking that expression\n " ++ + stringifyString(value.unparse) ++ "\nshould be same as expression\n " ++ + stringifyString(expected.unparse) ++ "\nActual value:\n "), + Silver_Expr { silver:testing:showTestValue(value) }, + strCnst("\nExpected value: \n "), + Silver_Expr { silver:testing:showTestValue(expected) }, + strCnst("\n")]), ';', location=ag.location), + attributeDef(concreteDefLHS(qNameId(tref, location=tref.location), location=tref.location), '.', qNameAttrOccur(qNameId(passref, location=passref.location), location=ag.location), '=', + Silver_Expr { value == expected }, ';', location=ag.location), + forwardsTo('forwards', 'to', mkStrFunctionInvocation(ag.location, "defTest", []), ';', location=ag.location)]), '}', location=ag.location), location=ag.location); + +{- + local aspProdCS :: AGDcl = asAGDcl ( + "aspect production %%%Name testSuite \n" ++ + "t ::= \n" ++ + "{ testsToPerform <- [ " ++ testName ++ "() ]; } " , + cons_CS_env("testSuite", wrapName(testSuite), empty_CS_env()) , 4 ); +-} + + local aspProdCS :: AGDcl = + aspectProductionDcl('aspect', 'production', qNameId(testSuite, location=ag.location), + aspectProductionSignature( + aspectProductionLHSId(tref, location=ag.location), + '::=', aspectRHSElemNil(location=ag.location), location=ag.location), + productionBody('{', + productionStmtsSnoc( + productionStmtsNil(location=ag.location), + valContainsAppend( + qName(ag.location, "testsToPerform"), + '<-', + fullList('[', + exprsSingle( + applicationEmpty( + baseExpr(qNameId(testNameref, location=ag.location), location=ag.location), '(', ')', location=ag.location), location=ag.location), + ']', location=ag.location), + ';', location=ag.location), location=ag.location), '}', location=ag.location), location=ag.location); + + local testName :: String = "generatedTest" ++ "_" ++ + substitute(":","_",ag.grammarName) ++ "_" ++ + substitute(".","_",kwd.filename) ++ "_" ++ + toString(kwd.line) ++ "_" ++ + toString(kwd.column); +} + diff --git a/grammars/silver/extension/testing/Helper.sv b/grammars/silver/compiler/extension/testing/Helper.sv similarity index 77% rename from grammars/silver/extension/testing/Helper.sv rename to grammars/silver/compiler/extension/testing/Helper.sv index 7acdb4318..57239f0bd 100644 --- a/grammars/silver/extension/testing/Helper.sv +++ b/grammars/silver/compiler/extension/testing/Helper.sv @@ -1,12 +1,12 @@ -grammar silver:extension:testing; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:concrete_syntax; -import silver:definition:type; -import silver:definition:type:syntax; -import silver:modification:collection; -import silver:extension:list; +grammar silver:compiler:extension:testing; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:collection; +import silver:compiler:modification:list; -- Expression creating functions diff --git a/grammars/silver/extension/testing/MainTestSuite.sv b/grammars/silver/compiler/extension/testing/MainTestSuite.sv similarity index 76% rename from grammars/silver/extension/testing/MainTestSuite.sv rename to grammars/silver/compiler/extension/testing/MainTestSuite.sv index a1c6a83ca..efce45377 100644 --- a/grammars/silver/extension/testing/MainTestSuite.sv +++ b/grammars/silver/compiler/extension/testing/MainTestSuite.sv @@ -1,14 +1,14 @@ -grammar silver:extension:testing; +grammar silver:compiler:extension:testing; -import silver:definition:core; -import silver:definition:env; -import silver:definition:concrete_syntax; -import silver:definition:type; -import silver:definition:type:syntax; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; -import silver:modification:ffi; -import silver:modification:collection; -import silver:extension:list; +import silver:compiler:modification:ffi; +import silver:compiler:modification:collection; +import silver:compiler:modification:list; terminal MainTestSuite_t 'mainTestSuite' lexer classes {KEYWORD}; terminal MakeTestSuite_t 'makeTestSuite' lexer classes {KEYWORD}; @@ -20,14 +20,15 @@ top::AGDcl ::= 'makeTestSuite' nme::IdLower_t ';' local sig :: ProductionSignature = productionSignature( + nilConstraint(location=top.location), '=>', productionLHS(name("t", top.location), '::', - nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "TestSuite", top.location), location=top.location), botlNone(location=top.location), location=top.location), location=top.location), + nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "TestSuite", top.location), location=top.location), location=top.location), location=top.location), '::=', productionRHSNil(location=top.location), location=top.location); local bod :: [ProductionStmt] = [forwardsTo('forwards', 'to', mkStrFunctionInvocation(top.location, "testsAsNT", [mkNameExpr("testsToPerform", top.location)]), ';', location=top.location), collectionAttributeDclProd('production', 'attribute', name("testsToPerform", top.location), '::', - listTypeExpr('[', nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Test", top.location), location=top.location), botlNone(location=top.location), location=top.location), ']', location=top.location), + listTypeExpr('[', nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Test", top.location), location=top.location), location=top.location), ']', location=top.location), 'with', plusplusOperator('++', location=top.location), ';', location=top.location), valContainsBase(qName(top.location, "testsToPerform"), ':=', emptyList('[',']', location=top.location), ';', location=top.location) ]; @@ -58,12 +59,13 @@ top::AGDcl ::= 'mainTestSuite' nme::IdLower_t ';' functionDcl( -- function main 'function', name("main", top.location), - -- IOVal ::= args::[String] mainIO::IO + -- IOVal ::= args::[String] mainIO::IOToken functionSignature( + nilConstraint(location=top.location), '=>', functionLHS( - nominalTypeExpr( - qNameTypeId(terminal(IdUpper_t, "IOVal", top.location), location=top.location), - botlSome('<', typeListSingle(integerTypeExpr('Integer', location=top.location), location=top.location), '>', location=top.location), location=top.location), location=top.location), + appTypeExpr( + nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "IOVal", top.location), location=top.location), location=top.location), + bTypeList('<', typeListSingle(integerTypeExpr('Integer', location=top.location), location=top.location), '>', location=top.location), location=top.location), location=top.location), '::=', productionRHSCons( productionRHSElemType(listTypeExpr('[', stringTypeExpr('String', location=top.location), ']', location=top.location), location=top.location), @@ -81,7 +83,7 @@ top::AGDcl ::= 'mainTestSuite' nme::IdLower_t ';' -- local testResults :: TestSuite; localAttributeDcl( 'local', 'attribute', name("testResults", top.location), '::', - nominalTypeExpr( qNameTypeId(terminal(IdUpper_t,"TestSuite", top.location), location=top.location), botlNone(location=top.location), location=top.location), ';', location=top.location), + nominalTypeExpr( qNameTypeId(terminal(IdUpper_t,"TestSuite", top.location), location=top.location), location=top.location), ';', location=top.location), -- testResults = name() valueEq( qName(top.location, "testResults"), '=', applicationEmpty( baseExpr( qNameId(nameIdLower(nme, location=top.location), location=top.location), location=top.location), @@ -95,9 +97,9 @@ top::AGDcl ::= 'mainTestSuite' nme::IdLower_t ';' returnDef('return', mkStrFunctionInvocation(top.location, "ioval", [ - mkStrFunctionInvocation(top.location, "exit", + mkStrFunctionInvocation(top.location, "exitT", [ attrAcc("testResults","numFailed", top.location), - mkStrFunctionInvocation(top.location, "print", + mkStrFunctionInvocation(top.location, "printT", [ foldStringExprs( [ strCnst("\n\n"), strCnst("============================================================\n"), @@ -105,9 +107,9 @@ top::AGDcl ::= 'mainTestSuite' nme::IdLower_t ';' attrAcc("testResults","msg", top.location), strCnst("\n\n"), strCnst("Passed "), - mkStrFunctionInvocation(top.location, "toStringFromInteger", [ attrAcc("testResults","numPassed", top.location) ]), + Silver_Expr { silver:core:integerToString(testResults.numPassed) }, strCnst(" tests out of "), - mkStrFunctionInvocation(top.location, "toStringFromInteger", [ attrAcc("testResults","numTests", top.location) ]), + Silver_Expr { silver:core:integerToString(testResults.numTests) }, strCnst("\n"), strCnst("============================================================\n") ]), @@ -126,14 +128,14 @@ top::AGDcl ::= 'mainTestSuite' nme::IdLower_t ';' {- function main -IO ::= args::String mainIO::IO +IOToken ::= args::String mainIO::IOToken { local testResults :: TestSuite = core_tests(); testResults.ioIn = mainIO; return - exit( testResults.numTests - testResults.numPassed, - print("\n\n" ++ + exitT( testResults.numTests - testResults.numPassed, + printT("\n\n" ++ "============================================================\n" ++ "Test results: \n" ++ testResults.msg ++ "\n\n" ++ diff --git a/grammars/silver/compiler/extension/testing/WrongCode.sv b/grammars/silver/compiler/extension/testing/WrongCode.sv new file mode 100644 index 000000000..daab64784 --- /dev/null +++ b/grammars/silver/compiler/extension/testing/WrongCode.sv @@ -0,0 +1,94 @@ +grammar silver:compiler:extension:testing; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; + +terminal WrongCode_kwd 'wrongCode' lexer classes {KEYWORD}; +terminal WarnCode_kwd 'warnCode' lexer classes {KEYWORD}; +terminal NoWarnCode_kwd 'noWarnCode' lexer classes {KEYWORD}; +terminal WrongFlowCode_kwd 'wrongFlowCode' lexer classes {KEYWORD}; + +function containsMessage +Boolean ::= text::String severity::Integer msgs::[Message] +{ + return any(map((\x::Message -> x.severity==severity && indexOf(text, x.output)!=-1), msgs)); +} + +concrete production wrongDecl +top::AGDcl ::= 'wrongCode' s::String_t '{' ags::AGDcls '}' +{ + top.unparse = "wrongCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; + + top.errors := + if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 2, ags.errors) + then [err(top.location, "Wrong code did not raise an error containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors + else []; + + -- do extend its environment with its defs + ags.env = occursEnv(ags.occursDefs, newScopeEnv(ags.defs, top.env)); + + forwards to emptyAGDcl(location=top.location); +} + +concrete production warnDecl +top::AGDcl ::= 'warnCode' s::String_t '{' ags::AGDcls '}' +{ + top.unparse = "warnCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; + + top.errors := + if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 1, ags.errors) + then [err(top.location, "Warn code did not raise a warning containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors + else []; + + forwards to makeAppendAGDclOfAGDcls(ags); + -- Forward to the decls so that we can use the stuff declared with warnings in other tests +} + +{-- + -Check that code does *NOT* include a particular type of warning. + -This is used to ensure warnings are only generated on code where they should be generated. + -We don't need a version for no errors, since errors will cause failures anyway. + - + - @param s The string which should NOT be contained in any warning messages from this code block + - @param ags The code block which we are checking +-} +concrete production noWarnDecl +top::AGDcl ::= 'noWarnCode' s::String_t '{' ags::AGDcls '}' +{ + top.unparse = "noWarnCode " ++ s.lexeme ++ " {" ++ ags.unparse ++ "}"; + + {- + I think we want the errors from ags in any case. This production + is essentially requiring that the code is correct, so we want to + know that the reason there is no warning is because the code was + written correctly, not because it had a worse error in it. + -} + top.errors := + ags.errors ++ + if containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 1, ags.errors) + then [err(top.location, "No-warn code raised a warning containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] + else []; + + forwards to makeAppendAGDclOfAGDcls(ags); + -- Forward to the decls so that we can use the stuff declared without warnings in other tests +} + +concrete production wrongFlowDecl +top::AGDcl ::= 'wrongFlowCode' s::String_t '{' ags::AGDcls '}' +{ + top.unparse = "wrongFlowCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; + + top.errors := + if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 2, ags.errors) + then [err(top.location, "Wrong code did not raise an error containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors + else []; + + -- do extend its environment with its defs + ags.env = occursEnv(ags.occursDefs, newScopeEnv(ags.defs, top.env)); + + -- let's ALSO propagate up flow info, so these kinds of errors are checked/caught + top.flowDefs := ags.flowDefs; + + forwards to emptyAGDcl(location=top.location); +} + diff --git a/grammars/silver/compiler/extension/treegen/Arbitrary.sv b/grammars/silver/compiler/extension/treegen/Arbitrary.sv new file mode 100644 index 000000000..3efddb49b --- /dev/null +++ b/grammars/silver/compiler/extension/treegen/Arbitrary.sv @@ -0,0 +1,323 @@ +grammar silver:compiler:extension:treegen; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:definition:concrete_syntax:ast; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:extension:convenience; +imports silver:compiler:modification:list; +imports silver:compiler:extension:tuple; +imports silver:compiler:modification:lambda_fn; +imports silver:compiler:modification:let_fix; +imports silver:compiler:metatranslation; + +import silver:util:treemap as tm; + +terminal Generator_t 'generator' lexer classes {KEYWORD}; + +concrete production generatorDcl +top::AGDcl ::= 'generator' n::Name '::' t::TypeExpr '{' grammars::GeneratorComponents '}' +{ + top.unparse = s"generator ${n.unparse} :: ${t.unparse} { ${grammars.unparse} }"; + + -- Compute the defs exported by the specified grammars + local med::ModuleExportedDefs = + moduleExportedDefs( + top.location, top.compiledGrammars, top.grammarDependencies, + grammars.moduleNames, []); + production specEnv::Decorated Env = newScopeEnv(med.defs, emptyEnv()); + + -- Override defs to suppress production attributes from flowing up as a paDef, + -- to avoid a circularity as what production attributes are generated depends + -- on the environment. Practically this means that we can't aspect a generator + -- function and refer to its production attributes, which is probably good anyway. + top.defs := + case forward of + | functionDcl(_, _, ns, _) -> [funDef(top.grammarName, n.location, ns.namedSignature)] + | _ -> error("forward should be a function") + end; + + -- Build the syntax AST from the specified grammars to extract lexical precedence info + production syntax::SyntaxRoot = cstRoot( + n.name, t.typerep.typeName, foldr(consSyntax, nilSyntax(), med.syntaxAst), + nothing(), [], [], location=top.location, sourceGrammar=top.grammarName); + + production attribute implicitImports::[String] with ++; + implicitImports := []; + top.moduleNames <- implicitImports; + local extraMED::ModuleExportedDefs = + moduleExportedDefs( + top.location, top.compiledGrammars, top.grammarDependencies, + "silver:core" :: implicitImports, []); + + -- Generator components must be imported for the translation here, + -- but an AGDcl can't (currently) forward to an import ModuleStmt - + -- resorting to a slight interfering workaround for now. + propagate moduleNames; + forward.env = occursEnv(extraMED.occursDefs, newScopeEnv(extraMED.defs, specEnv)); + + -- Implicitly import the random library + implicitImports <- ["silver:util:random"]; + + -- We also depend on the silver:regex library + implicitImports <- ["silver:regex"]; + + forwards to Silver_AGDcl { + function $Name{n} + silver:core:RandomGen<$TypeExpr{t}> ::= minDepth::Integer maxDepth::Integer + { + $ProductionStmt{ + foldr( + productionStmtAppend(_, _, location=top.location), + errorProductionStmt([], location=top.location), -- TODO: No nullProductionStmt? + map(genNtLocalDecl(top.location, forward.env, specEnv, _), map((.fullName), syntax.allNonterminals)) ++ + map(genTermLocalDecl(top.location, forward.env, specEnv, syntax.dominatingTerminals, _), map((.fullName), syntax.allTerminals)))} + return $Expr{genForType(top.location, forward.env, specEnv, Silver_Expr { 0 }, t.typerep)}; + } + }; + + -- Uncomment for debugging + --top.errors := unsafeTracePrint(forward.errors, forward.unparse); +} + +nonterminal GeneratorComponents with config, grammarName, location, unparse, errors, moduleNames, compiledGrammars, grammarDependencies; +nonterminal GeneratorComponent with config, grammarName, location, unparse, errors, moduleNames, compiledGrammars, grammarDependencies; + +propagate errors, moduleNames on GeneratorComponents, GeneratorComponent; + +concrete production nilGeneratorComponent +top::GeneratorComponents ::= +{ + top.unparse = ""; +} + +concrete production consGeneratorComponent +top::GeneratorComponents ::= c1::GeneratorComponent c2::GeneratorComponents +{ + top.unparse = c1.unparse ++ c2.unparse; +} + +concrete production generatorComponent +top::GeneratorComponent ::= m::ModuleName ';' +{ + top.unparse = m.unparse ++ ";"; +} + +-- Generate the expression for constructing a type +function genForType +Expr ::= loc::Location env::Decorated Env specEnv::Decorated Env depth::Expr t::Type +{ + return + case t of + -- Monomorphic nonterminals that don't have an explicit Arbitrary instance, + -- call the appropriate local generator function. + | nonterminalType(ntName, [], _) + when (getTypeDcl(ntName, specEnv), getInstanceDcl("silver:util:random:Arbitrary", t, env)) + matches (dcl :: _, []) -> Silver_Expr { $Name{name("gen_" ++ substitute(":", "_", ntName), loc)}($Expr{depth}) } + + | terminalType(tName) + when (getTypeDcl(tName, specEnv), getInstanceDcl("silver:util:random:Arbitrary", t, env)) + matches (dcl :: _, []) -> Silver_Expr { $Name{name("gen_" ++ substitute(":", "_", tName), loc)}($Expr{depth}) } + + -- Lists are handled specially here, to allow recusively generating for + -- e.g. lists of nonterminals in a production RHS. + | appType(listCtrType(), elemT) -> + Silver_Expr { + silver:core:bind(random, \ len::Integer -> + silver:core:sequence( + \ depth::Integer -> $Expr{genForType(loc, env, specEnv, Silver_Expr { depth - 1 }, elemT)}, + silver:core:take( + silver:core:toInteger(len * silver:core:toFloat($Expr{depth}))), + silver:core:reverse(silver:core:range(0, $Expr{depth})))) + } + + -- Primitives and polymorphic nonterminals (e.g. Pair for tuples) are + -- handled by the Arbitrary type class. + | _ -> Silver_Expr { $Name{name("silver:util:random:genArb", loc)}($Expr{depth}) } + end; +} + +-- Determine whether we can generate an arbitrary value for some type. +function isTypeGeneratable +Boolean ::= env::Decorated Env specEnv::Decorated Env t::Type +{ + return + case t of + | nonterminalType(ntName, [], _) when getTypeDcl(ntName, specEnv) matches _ :: _ -> true + | terminalType(ntName) when getTypeDcl(ntName, specEnv) matches _ :: _ -> true + | appType(listCtrType(), elemT) -> isTypeGeneratable(env, specEnv, elemT) + | _ -> !null(getInstanceDcl("silver:util:random:Arbitrary", t, env)) + end; +} + +-- Determine whether we can generate an arbitrary value for some production - +-- i.e. that it is monomorphic and all the RHS types are generatable. +function isProdGeneratable +Boolean ::= env::Decorated Env specEnv::Decorated Env p::ValueDclInfo +{ + local prodType::Type = p.typeScheme.typerep; + return + null(p.typeScheme.boundVars) && -- Only consider generating for monomorphic productions, for now + all(map(isTypeGeneratable(env, specEnv, _), prodType.inputTypes ++ map(snd, prodType.namedTypes))); +} + +-- Used to sort productions according to their number of nonterminal children +function nonterminalArity +Integer ::= t::Type +{ + return length(filter((.isNonterminal), t.inputTypes)); +} +function prodDclInfoNumChildLte +Boolean ::= l::ValueDclInfo r::ValueDclInfo +{ + return nonterminalArity(l.typeScheme.typerep) <= nonterminalArity(r.typeScheme.typerep); +} +function prodDclInfoNumChildEq +Boolean ::= l::ValueDclInfo r::ValueDclInfo +{ + return nonterminalArity(l.typeScheme.typerep) == nonterminalArity(r.typeScheme.typerep); +} +function prodDclInfoNumChildNonzero +Boolean ::= v::ValueDclInfo +{ + return nonterminalArity(v.typeScheme.typerep) > 0; +} + +-- splits where operator becomes false in list +function takeWhile2 +[a] ::= f::(Boolean ::= a a) l::[a] +{ + return if null(l) then [] + else if null(tail(l)) then l + else if f(head(l), head(tail(l))) then head(l) :: takeWhile2(f, tail(l)) + else [head(l)]; +} + +-- local genExpr::(RandomGen ::= Integer) = \ depth::Integer -> ...; +function genNtLocalDecl +ProductionStmt ::= loc::Location env::Decorated Env specEnv::Decorated Env nt::String +{ + -- All productions that are generatable for nt, sorted by arity + local prods :: [ValueDclInfo] = + filter(isProdGeneratable(env, specEnv, _), + sortBy(prodDclInfoNumChildLte, + getKnownProds(nt, specEnv))); + + local num_lowest_arity :: Integer = length(takeWhile2(prodDclInfoNumChildEq, prods)); + local num_nonzero_arity :: Integer = length(filter(prodDclInfoNumChildNonzero, prods)); + + local result::Expr = + if null(prods) + -- TODO: This could be a compile-time error, in theory + then Silver_Expr { error($Expr{stringConst(terminal(String_t, "\"no generatable productions for nonterminal " ++ nt ++ "\""), location=loc)}) } + else Silver_Expr { + silver:core:bind( + if depth >= maxDepth + -- Exclude all but the lowest-arity productions + then randomRange(0, $Expr{intConst(terminal(Int_t, toString(num_lowest_arity - 1)), location=loc)}) + else if depth < minDepth + -- Exclude all arity-0 productions + then randomRange( + $Expr{intConst(terminal(Int_t, toString(if num_nonzero_arity == length(prods) then 0 else num_nonzero_arity)), location=loc)}, + $Expr{intConst(terminal(Int_t, toString(length(prods) - 1)), location=loc)}) + -- All productions + else randomRange(0, $Expr{intConst(terminal(Int_t, toString(length(prods) - 1)), location=loc)}), + \ i::Integer -> $Expr{generateExprChain(loc, env, specEnv, nt, 0, prods)}) + }; + + return + Silver_ProductionStmt { + local $name{"gen_" ++ substitute(":", "_", nt)}::(silver:core:RandomGen<$TypeExpr{nominalTypeExpr(qName(loc, nt).qNameType, location=loc)}> ::= Integer) = + \ depth::Integer -> $Expr{result}; + }; +} + +function genTermLocalDecl +ProductionStmt ::= loc::Location env::Decorated Env specEnv::Decorated Env dominatingTerminals::EnvTree t::String +{ + local te::TypeExpr = nominalTypeExpr(qName(loc, t).qNameType, location=loc); + + -- Filter out cantidate lexemes by checking if they match dominating terminal regexes. + -- TODO: This a approach is somewhat somewhat inefficient, and fails with + -- infinite recursion if a terminal is totally dominated by another. + -- For example: + -- terminal A 'x'; + -- terminal B 'x' dominates A; + -- nonterminal C; + -- concrete production d + -- C ::= A {} + -- However this almost certainly points to a major problem with the grammar, + -- since any productions that mention A can never match any strings. + -- Is there a better approach involving regex subtraction as a primitive? + -- Also note that this does not consider disambiguation functions, and may + -- generate trees that could have arisen from different ambigous parses of the + -- same source. + local termDominated::Expr = + foldr( + or(_, '||', _, location=loc), + falseConst('false', location=loc), + map( + \ term::Decorated SyntaxDcl -> Silver_Expr { + silver:regex:matches($Expr{translate(loc, reflect(term.terminalRegex))}, term.lexeme) + }, + searchEnvTree(t, dominatingTerminals))); + return + Silver_ProductionStmt { + local $name{"gen_" ++ substitute(":", "_", t)}::(silver:core:RandomGen<$TypeExpr{te}> ::= Integer) = + \ depth::Integer -> + silver:core:bind( + silver:core:bind(silver:util:random:genArb(depth), genArbTerminal($TypeExpr{te}, _)), + \ term::$TypeExpr{te} -> + if $Expr{termDominated} then $name{"gen_" ++ substitute(":", "_", t)}(depth) else pure(term)); + }; +} + +{- + +We got the id 'Expr' incoming. + +We should look up 'Expr' and get a list of productions, from that we get the indices. + +We then map genForType over the list of productions and generate a big if-then-else tree based on the randomRange() + +Note that this expects lst to be non-empty! + +-} + +function generateExprChain +Expr ::= loc::Location env::Decorated Env specEnv::Decorated Env nt::String index::Integer lst::[ValueDclInfo] +{ + local prod::ValueDclInfo = head(lst); + local prodType::Type = prod.typeScheme.typerep; + local args::[(String, Type)] = + zipWith(pair, map(\ i::Integer -> "a" ++ toString(i), range(0, length(prodType.inputTypes))), prodType.inputTypes) ++ + prodType.namedTypes; + local argGenExprs::[Expr] = + map(genForType(loc, env, specEnv, Silver_Expr { depth + 1 }, _), map(snd, args)); + local genRes::Expr = + mkFullFunctionInvocation( + loc, Silver_Expr { $name{prod.fullName} }, + map(\ i::Integer -> Silver_Expr { $name{"a" ++ toString(i)} }, range(0, length(prodType.inputTypes))), + map(\ a::String -> (a, Silver_Expr { $name{a} }), map(fst, prodType.namedTypes))); + local lambdaChain::Expr = + foldr( + \ arg::(String, Type) res::Expr -> + Silver_Expr { \ $name{arg.1}::$TypeExpr{typerepTypeExpr(arg.2, location=loc)} -> $Expr{res} }, + genRes, args); + local genProd::Expr = + if null(argGenExprs) + then Silver_Expr { silver:core:pure($Expr{genRes}) } + else foldl( + \ e1::Expr e2::Expr -> Silver_Expr { silver:core:ap($Expr{e1}, $Expr{e2}) }, + Silver_Expr { silver:core:map($Expr{lambdaChain}, $Expr{head(argGenExprs)}) }, + tail(argGenExprs)); + + return if null(tail(lst)) then genProd + else Silver_Expr { + if i == $Expr{intConst(terminal(Int_t, toString(index)), location=loc)} + then $Expr{genProd} + else $Expr{generateExprChain(loc, env, specEnv, nt, index + 1, tail(lst))} + }; +} diff --git a/grammars/silver/compiler/extension/treegen/TerminalGen.sv b/grammars/silver/compiler/extension/treegen/TerminalGen.sv new file mode 100644 index 000000000..d75020061 --- /dev/null +++ b/grammars/silver/compiler/extension/treegen/TerminalGen.sv @@ -0,0 +1,54 @@ +grammar silver:compiler:extension:treegen; + +imports silver:core hiding empty, alt; +imports silver:regex; +imports silver:compiler:metatranslation; +imports silver:reflect; + +import silver:util:treeset as set; -- needed for freeVars equation + +terminal GenArbTerminal_t 'genArbTerminal' lexer classes {KEYWORD, RESERVED}; + +-- Note that this construct ignores lexical dominates/submits to relationships +-- with other terminals, since we don't have a parser spec/syntax AST to extract +-- that information. +concrete production genArbTerminalNoLocExpr +top::Expr ::= 'genArbTerminal' '(' te::TypeExpr ',' '_' ')' +{ + top.unparse = s"genArbTerminal(${te.unparse}, _)"; + propagate freeVars; + + local regex::Regex = + case getTypeDcl(te.typerep.typeName, top.env) of + | termDcl(_, r, _, _) :: _ -> r + | _ -> empty() + end; + local genRepeatProb::Float = + case getTypeDcl(te.typerep.typeName, top.env) of + | termDcl(_, _, _, just(grp)) :: _ -> grp + | _ -> 0.75 + end; + + forwards to + if null(getAttrDcl("silver:regex:genArbMatch", top.env)) + then errorExpr([err(top.location, "Generation of arbitrary terminal values requires import of silver:regex")], location=top.location) + else Silver_Expr { + let genLexeme::RandomGen = + decorate $Expr{translate(top.location, reflect(new(regex)))} with { + starProb = $Expr{floatConst(terminal(Float_t, toString(genRepeatProb)), location=top.location)}; + altCountIn = 0; + }.genArbMatch + in \ loc::silver:core:Location -> silver:core:map(\ lexeme::String -> terminal($TypeExpr{te}, lexeme, loc), genLexeme) + end + }; +} + +concrete production genArbTerminalExpr +top::Expr ::= 'genArbTerminal' '(' te::TypeExpr ',' loc::Expr ')' +{ + top.unparse = s"genArbTerminal(${te.unparse}, ${loc.unparse})"; + forwards to + mkFunctionInvocation(top.location, + genArbTerminalNoLocExpr('genArbTerminal', '(', te, ',', '_', ')', location=top.location), + [loc]); +} diff --git a/grammars/silver/extension/treegen/TestFor.sv b/grammars/silver/compiler/extension/treegen/TestFor.sv similarity index 87% rename from grammars/silver/extension/treegen/TestFor.sv rename to grammars/silver/compiler/extension/treegen/TestFor.sv index ea6ba7606..70eece4ce 100644 --- a/grammars/silver/extension/treegen/TestFor.sv +++ b/grammars/silver/compiler/extension/treegen/TestFor.sv @@ -1,12 +1,16 @@ -import silver:extension:testing; +import silver:compiler:extension:testing; +-- TODO: Resurrect this at some point + +{- terminal TestFor_T 'testFor'; concrete production testforagdcl top::AGDcl ::= 'testFor' testSuite::Name ':' n::Name '::' id::QName ',' e::Expr ';' { + top.unparse = s"testFor ${testSuite.unparse} : ${n.unparse} :: ${id.unparse}, ${e.unparse};"; top.defs := []; top.moduleNames := []; top.flowDefs := []; @@ -16,7 +20,7 @@ top::AGDcl ::= 'testFor' testSuite::Name ':' n::Name '::' id::QName ',' e::Expr -- all known productions, including forwarding ones - local prods :: [DclInfo] = getKnownProds(id.lookupType.fullName, top.env); + local prods :: [ValueDclInfo] = getKnownProds(id.lookupType.fullName, top.env); local l :: Location = top.location; local generatedName :: String = "checkPropOn" ++ id.name ++ toString(genInt()); @@ -30,10 +34,11 @@ top::AGDcl ::= 'testFor' testSuite::Name ':' n::Name '::' id::QName ',' e::Expr local sig :: FunctionSignature = functionSignature( + nilConstraint(location=l), '=>', functionLHS(typerepTypeExpr(boolType(), location=l), location=l), '::=', productionRHSCons( - productionRHSElem(n, '::', typerepTypeExpr(id.lookupType.typerep, location=l), location=l), + productionRHSElem(n, '::', typerepTypeExpr(id.lookupType.typeScheme.typerep, location=l), location=l), productionRHSNil(location=l), location=l), location=l); @@ -57,13 +62,14 @@ top::AGDcl ::= 'testFor' testSuite::Name ':' n::Name '::' id::QName ',' e::Expr } function generateTestFor -AGDcl ::= d::DclInfo testfunname::String l::Location testSuite::Name +AGDcl ::= d::ValueDclInfo testfunname::String l::Location testSuite::Name { local generatedName :: String = "genSpecificProduction" ++ substring(lastIndexOf(":", d.fullName) + 1, length(d.fullName), d.fullName) ++ toString(genInt()); local sig :: FunctionSignature = -- id ::= functionSignature( + nilConstraint(location=l), '=>', functionLHS(typerepTypeExpr(boolType(), location=l), location=l), '::=', productionRHSNil(location=l), @@ -100,6 +106,6 @@ AGDcl ::= d::DclInfo testfunname::String l::Location testSuite::Name ] ); } - +-} diff --git a/grammars/silver/compiler/extension/tuple/PatternMatching.sv b/grammars/silver/compiler/extension/tuple/PatternMatching.sv new file mode 100644 index 000000000..96f430d8b --- /dev/null +++ b/grammars/silver/compiler/extension/tuple/PatternMatching.sv @@ -0,0 +1,34 @@ +-- Pattern matching on tuples +nonterminal TuplePatternList with location, unparse; + +concrete production emptyTuplePattern +top::Pattern ::= '(' ')' +{ + top.unparse = "()"; + forwards to Silver_Pattern { silver:core:unit() }; +} + +concrete production tuplePattern +top::Pattern ::= '(' ts::TuplePatternList ')' +{ + top.unparse = s"(${ts.unparse})"; + forwards to ts.asTuplePattern; +} + +-- Used to convert the comma-separated list of patterns +-- that make up the tuple into a pair pattern: +synthesized attribute asTuplePattern::Pattern occurs on TuplePatternList; + +concrete production patternTuple_two +top::TuplePatternList ::= fst::Pattern ',' snd::Pattern +{ + top.unparse = fst.unparse ++ ", " ++ snd.unparse; + top.asTuplePattern = Silver_Pattern { silver:core:pair($Pattern{fst}, $Pattern{snd}) }; +} + +concrete production patternTuple_more +top::TuplePatternList ::= fst::Pattern ',' snd::TuplePatternList +{ + top.unparse = fst.unparse ++ ", " ++ snd.unparse; + top.asTuplePattern = Silver_Pattern { silver:core:pair($Pattern{fst}, $Pattern{snd.asTuplePattern}) }; +} \ No newline at end of file diff --git a/grammars/silver/compiler/extension/tuple/README.md b/grammars/silver/compiler/extension/tuple/README.md new file mode 100644 index 000000000..6c9fdb674 --- /dev/null +++ b/grammars/silver/compiler/extension/tuple/README.md @@ -0,0 +1,29 @@ +# Tuple Syntax + +Tuples use a comma "`,`" to seperate individual tuple elements. They are implemented inductively using the construction of ordered pairs, e.g. we forward a tuple `(a, b, c, d)` to `Pair>>`. + +## Implementation + +We add a production `tupleExpr` on the `Expr` nonterminal and define a new nonterminal `TupleList`, whose associated productions are responsible for extracting tuple elements and inductively constructing them as pair expressions. Similarly, the production `tuplePattern` on `Pattern` enables pattern matching on tuples, in conjunction with the new nonterminal `TuplePatternList`. + +In order to declare a tuple type as some `t::(a,b)` rather than `t::Pair`, we add a new production `tupleTypeExpr` on the `TypeExpr` nonterminal. This works much like `tupleExpr` and `tuplePattern`, and defines a new nonterminal `ListofTypeExprs`. + +In sum, the new nonterminals include: + +* `TupleList` +* `TuplePatternList` +* `ListOfTypeExprs` + +For the selector syntax, we add a production `selector` which forwards to a function `select`. This returns the tuple element at the specified access index, an `Expr`. + +### Type + +We want to properly express error messages on tuples, e.g. rather than printing some "... has actual type `Pair >`," we would like to notify the user "... has actual type `(Integer, String, Integer)`." In order to do this, we introduce a new synthesized attribute `tupleElems` of type `[Type]` that occurs on `Type`; this will be a list of all the distinct types in the tuple. For example, a tuple of type + + (Integer, String, Integer) + +would have a `tupleElems` of `[Integer, String, Integer]`. To construct our `tupleElems`, we aspect the `appType` production for constructor types; for any `Pair` type, its type elements are appropriately extracted and appended to tupleElems. A single element `e` can be thought of as a singleton tuple `(e)`, and thus a default production is defined such that for any type its `tupleElems` is assigned to the singleton list of that type. + +The production `tupleType` is responsible for converting the `tupleElems` of a given tuple into the appropriate pretty print for the tuple type. It then forwards elements of `tupleElems` to the `Pair` type. + +`tupleType` is used by the productions `tupleTypeExpr` and `tupleExpr` to construct the tuple type representation for a given tuple. \ No newline at end of file diff --git a/grammars/silver/compiler/extension/tuple/Tuple.sv b/grammars/silver/compiler/extension/tuple/Tuple.sv new file mode 100644 index 000000000..aa3be23e3 --- /dev/null +++ b/grammars/silver/compiler/extension/tuple/Tuple.sv @@ -0,0 +1,96 @@ +-- Note: We consider only tuples containing two or more elements +-- The empty tuple () forwards to unit + +grammar silver:compiler:extension:tuple; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:definition:type; + +imports silver:compiler:extension:patternmatching; + +terminal IntConst /[0-9]+/; + +nonterminal TupleList with location, unparse, translation; + +-- used to convert the comma-separated list of expressions +-- that make up the tuple into a pair expression: +synthesized attribute translation :: Expr; + +concrete production emptyTuple +top::Expr ::= '(' ')' +{ + top.unparse = "()"; + top.typerep = tupleType([]); + forwards to Silver_Expr { silver:core:unit() }; +} + +concrete production tupleExpr +top::Expr ::= '(' tl::TupleList ')' +{ + top.unparse = "(" ++ tl.unparse ++ ")"; + + -- computing specialized tupleType from forward.typerep + -- is cleaner than performing type checking on TupleList + -- (performSubstitution is needed because forward.typerep + -- isn't instantiated into a chain of pairs until upSubst is applied) + top.typerep = tupleType(performSubstitution(forward.typerep, forward.upSubst).tupleElems); + + forwards to tl.translation; +} + +-- selects tuple element at index a +concrete production selector +top::Expr ::= tuple::Expr '.' a::IntConst +{ + + -- Forward gets the substitution context of the tuple + propagate downSubst, upSubst, freeVars; + + local accessIndex::Integer = toInteger(a.lexeme); + + top.unparse = tuple.unparse ++ "." ++ a.lexeme; + + -- Ensure that we extract the tupleElems from the underlying chain of pair types if the tuple type is decorated. + local ty :: Type = performSubstitution(tuple.typerep, tuple.upSubst); + local len::Integer = length((if ty.isDecorated then ty.decoratedType else ty).tupleElems); + + forwards to if (accessIndex > len || accessIndex < 1) then + errorExpr([err(top.location, "Invalid tuple selector index.")], location=top.location) + -- exprRef prevents exponential type checking + else select(exprRef(tuple, location=top.location), 1, accessIndex, len); + +} + +function select +-- i is the current index, a is the desired access index +-- len is the total length of the tuple +Expr ::= exp::Expr i::Integer a::Integer len::Integer + { + return + if i == a then + if a == len then + -- only if the access index is the length of the + -- tuple do we simply return the expression itself + Silver_Expr { $Expr{exp} } + else Silver_Expr { $Expr{exp}.fst } + else select(Silver_Expr{ $Expr{exp}.snd }, i + 1, a, len); +} + +-- TupleList cases: +-- There are two elements in the tuple +concrete production tupleList_2Elements +top::TupleList ::= fst::Expr ',' snd::Expr +{ + top.unparse = fst.unparse ++ ", " ++ snd.unparse; + top.translation = Silver_Expr { silver:core:pair($Expr{fst}, $Expr{snd}) }; +} + +-- There are more than two elements in the tuple +concrete production tupleList_nElements +top::TupleList ::= fst::Expr ',' snd::TupleList +{ + top.unparse = fst.unparse ++ ", " ++ snd.unparse; + top.translation = Silver_Expr { silver:core:pair($Expr{fst}, $Expr{snd.translation}) }; +} \ No newline at end of file diff --git a/grammars/silver/compiler/extension/tuple/Type.sv b/grammars/silver/compiler/extension/tuple/Type.sv new file mode 100644 index 000000000..f0c7f90ef --- /dev/null +++ b/grammars/silver/compiler/extension/tuple/Type.sv @@ -0,0 +1,114 @@ +grammar silver:compiler:extension:tuple; + +imports silver:compiler:modification:list; + +nonterminal ListOfTypeExprs with location, unparse, te_translation; + +-- Used to convert the comma-separated list of TypeExprs +-- that make up the tuple type expression into a +-- pair type expression: +synthesized attribute te_translation :: TypeExpr; + +-- A list of the types that make up any given tuple: +synthesized attribute tupleElems :: [Type] occurs on Type; + +-- One can think of any single type t as a single element tuple type (t) +-- Default production assigns the tupleElems for (t) as [t]; a +-- list containing just the type of t +-- This default avoids aspecting other types +aspect default production +top::Type ::= +{ + top.tupleElems = [top]; +} + +-- For any Pair (or nested Pair) type, accumulate its tupleElems +-- as the fst and snd types that make up that Pair +aspect production appType +top::Type ::= c::Type a::Type +{ + top.tupleElems = + -- c.argTypes should only have a single element + case c.baseType of + | nonterminalType("silver:core:Pair", [starKind(), starKind()], false) -> c.argTypes ++ a.tupleElems + | _ -> [top] + end; + +} + +-- Avoid specializing possibly-decorated types +aspect production ntOrDecType +top::Type ::= _ _ _ +{ + top.tupleElems = [top]; +} + +-- Aspect productions needed to avoid discarding +-- the forwarding list type when we extract tupleElems +aspect production listType +top::Type ::= _ +{ + top.tupleElems = [top]; +} + +aspect production listCtrType +top::Type ::= +{ + top.tupleElems = [top]; +} + +-- accepts a [Type] (will be tupleElems here) +-- prints tuples w/ correct type syntax +-- forwards to Pair type +abstract production tupleType +top::Type ::= ts::[Type] +{ + top.defaultSpecialization = top; + + -- to avoid transforming away the tupleType and turning it back + -- into a chain of Pairs when performing substitutions + top.substituted = tupleType(map (\ t::Type -> decorate t with {substitution = top.substitution;}.substituted, ts)); + top.flatRenamed = tupleType(map (\ t::Type -> decorate t with {substitution = top.substitution;}.flatRenamed, ts)); + + -- elements of ts need the boundVariables from the top because typepp attribute has a dependency on boundVariables + top.typepp = "(" ++ implode(", ", map(prettyTypeWith(_, top.boundVariables), ts)) ++ ")"; + + forwards to case ts of + | [] -> nonterminalType("silver:core:Unit", [], false) + | [t] -> t + | t1::t1s -> appType(appType(nonterminalType("silver:core:Pair", [starKind(), starKind()], false), t1), tupleType(t1s)) + end; + +} + +-- Tuple TypeExpr + +concrete production emptyTupleTypeExpr +top::TypeExpr ::= '(' ')' +{ + top.unparse = "()"; + top.typerep = tupleType([]); + forwards to Silver_TypeExpr { silver:core:Unit }; +} + +concrete production tupleTypeExpr +top::TypeExpr ::= '(' tes::ListOfTypeExprs ')' +{ + top.unparse = "(" ++ tes.unparse ++ ")"; + top.typerep = tupleType(forward.typerep.tupleElems); + forwards to tes.te_translation; +} + +concrete production tupleTypeExpr2 +top::ListOfTypeExprs ::= te1::TypeExpr ',' te2::TypeExpr +{ + top.unparse = te1.unparse ++ "," ++ te2.unparse; + top.te_translation = Silver_TypeExpr {silver:core:Pair<$TypeExpr{te1} $TypeExpr{te2}>}; +} + +concrete production tupleTypeExprn +top::ListOfTypeExprs ::= te::TypeExpr ',' tes::ListOfTypeExprs +{ + top.unparse = te.unparse ++ "," ++ tes.unparse; + top.te_translation = Silver_TypeExpr {silver:core:Pair<$TypeExpr{te} $TypeExpr{tes.te_translation}>}; +} diff --git a/grammars/silver/compiler/host/Project.sv b/grammars/silver/compiler/host/Project.sv new file mode 100644 index 000000000..a5ed604ea --- /dev/null +++ b/grammars/silver/compiler/host/Project.sv @@ -0,0 +1,59 @@ +grammar silver:compiler:host; + +{- Silver is built as an extensible language with a core "host" language and a + - number of extensions and modifications containing additional features. + - However many of these extensions we typically always want to include by + - when building extended versions of Silver, and it becomes cumbersome to list + - them repeatedly. + - Thus we provide this grammar that exports all the components of the + - "default" Silver host language in one place. + - Note that this list may grow over time. + -} + +-- The "core" host language: +exports silver:compiler:host:core; + +-- Modifications to Silver = optional features that are not pure extensions. +-- These are explicitly annotated as "options" within the core host language +exports silver:compiler:modification:let_fix; +exports silver:compiler:modification:lambda_fn; +exports silver:compiler:modification:collection; +exports silver:compiler:modification:primitivepattern; +exports silver:compiler:modification:autocopyattr; +exports silver:compiler:modification:ffi; +exports silver:compiler:modification:copper; +exports silver:compiler:modification:defaultattr; +-- slight hacks, for the moment +exports silver:compiler:modification:copper_mda; + +-- Pure extensions to Silver +exports silver:compiler:extension:doc; +exports silver:compiler:extension:convenience; +exports silver:compiler:modification:list; -- Not really a pure extension, yuck. +exports silver:compiler:extension:easyterminal; +exports silver:compiler:extension:deprecation; +exports silver:compiler:extension:testing; +exports silver:compiler:extension:auto_ast; +exports silver:compiler:extension:templating; +exports silver:compiler:extension:patternmatching; +exports silver:compiler:extension:treegen; +exports silver:compiler:extension:doc; +exports silver:compiler:extension:autoattr; +exports silver:compiler:extension:strategyattr; +exports silver:compiler:extension:do_notation; +exports silver:compiler:extension:rewriting; +exports silver:compiler:extension:silverconstruction; +exports silver:compiler:extension:astconstruction; +exports silver:compiler:extension:constructparser; +exports silver:compiler:extension:tuple; +exports silver:compiler:extension:regex; +exports silver:compiler:extension:convenienceaspects; +exports silver:compiler:extension:attrsection; +exports silver:compiler:extension:implicit_monads; + +-- Other generally useful stuff: +exports silver:compiler:translation:java; +exports silver:compiler:driver; +exports silver:compiler:analysis:warnings:flow; +exports silver:compiler:analysis:warnings:exporting; +exports silver:compiler:langserver; diff --git a/grammars/silver/compiler/host/core/Project.sv b/grammars/silver/compiler/host/core/Project.sv new file mode 100644 index 000000000..224c52bb9 --- /dev/null +++ b/grammars/silver/compiler/host/core/Project.sv @@ -0,0 +1,25 @@ +grammar silver:compiler:host:core; + +{- + - This file contains exports for the "core" Silver host language, excluding + - all extensions and modifications. + - Note that the "default" host version of Silver specified in silver:compiler:host is + - still required to build this. + -} + +-- concrete syntax from these grammars +exports silver:compiler:definition:core; +exports silver:compiler:definition:concrete_syntax; +exports silver:compiler:definition:type:syntax; +exports silver:compiler:definition:flow:syntax; +exports silver:regex:concrete_syntax; + +-- symbols +exports silver:compiler:analysis:typechecking:core; + +--We wish regex to remain a generic grammar, so we resolve the conflict here! +-- Regexes end with /. Escape it if you want it. +disambiguate RegexChar_t, RegexSlash_t +{ + pluck RegexSlash_t; +} diff --git a/grammars/silver/compiler/langserver/Project.sv b/grammars/silver/compiler/langserver/Project.sv new file mode 100644 index 000000000..df57783b2 --- /dev/null +++ b/grammars/silver/compiler/langserver/Project.sv @@ -0,0 +1,9 @@ +grammar silver:compiler:langserver; + +-- This grammar contains helper functions and attributes used in the Silver LSP implementation. +-- For simplicitly, this grammar is imported in the build of the normal compiler for now. + +imports silver:compiler:host; +imports silver:compiler:definition:env; + +imports silver:util:treemap as map; diff --git a/grammars/silver/compiler/langserver/ReferenceLocations.sv b/grammars/silver/compiler/langserver/ReferenceLocations.sv new file mode 100644 index 000000000..94d1685a6 --- /dev/null +++ b/grammars/silver/compiler/langserver/ReferenceLocations.sv @@ -0,0 +1,246 @@ +grammar silver:compiler:langserver; + +monoid attribute valueRefLocs::[(Location, ValueDclInfo)]; +monoid attribute typeRefLocs::[(Location, TypeDclInfo)]; +monoid attribute attributeRefLocs::[(Location, AttributeDclInfo)]; + +attribute valueRefLocs, typeRefLocs, attributeRefLocs occurs on + RootSpec, Grammar, Root, NameList, AGDcls, AGDcl, + ProductionSignature, FunctionSignature, AspectProductionSignature, AspectFunctionSignature, + ConstraintList, Constraint, ProductionLHS, FunctionLHS, AspectProductionLHS, AspectFunctionLHS, + ProductionRHS, AspectRHS, ProductionRHSElem, AspectRHSElem, + TypeExpr, Signature, SignatureLHS, TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs, + ProductionBody, ProductionStmts, ProductionStmt, DefLHS, + ClassBody, ClassBodyItem, InstanceBody, InstanceBodyItem, + Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr, + PrimPatterns, PrimPattern, ProdNameList; + +propagate valueRefLocs, typeRefLocs, attributeRefLocs on + RootSpec, Grammar, Root, NameList, AGDcls, AGDcl, + ProductionSignature, FunctionSignature, AspectProductionSignature, AspectFunctionSignature, + ConstraintList, Constraint, ProductionLHS, FunctionLHS, AspectProductionLHS, AspectFunctionLHS, + ProductionRHS, AspectRHS, ProductionRHSElem, AspectRHSElem, + TypeExpr, Signature, SignatureLHS, TypeExprs, BracketedTypeExprs, BracketedOptTypeExprs, + ProductionBody, ProductionStmts, ProductionStmt, DefLHS, + ClassBody, ClassBodyItem, InstanceBody, InstanceBodyItem, + Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr, + PrimPatterns, PrimPattern, ProdNameList; + +aspect valueRefLocs on NameList using <- of +| nameListCons(q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| nameListOne(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +aspect typeRefLocs on NameList using <- of +| nameListCons(q, _, _) -> if q.lookupType.found then [(q.location, q.lookupType.dcl)] else [] +| nameListOne(q) -> if q.lookupType.found then [(q.location, q.lookupType.dcl)] else [] +end; + +aspect attributeRefLocs on NameList using <- of +| nameListCons(q, _, _) -> if q.lookupAttribute.found then [(q.location, q.lookupAttribute.dcl)] else [] +| nameListOne(q) -> if q.lookupAttribute.found then [(q.location, q.lookupAttribute.dcl)] else [] +end; + +attribute typeRefLocs occurs on QNameType; +aspect typeRefLocs on top::QNameType using := of +| _ -> if top.lookupType.found then [(top.location, top.lookupType.dcl)] else [] +end; + +attribute attributeRefLocs occurs on QNameAttrOccur; +aspect attributeRefLocs on top::QNameAttrOccur using := of +| qNameAttrOccur(at) -> if top.found then [(at.location, top.attrDcl)] else [] +end; + +aspect valueRefLocs on AGDcl using <- of +| aspectProductionDcl(_, _, q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| aspectFunctionDcl(_, _, q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +aspect valueRefLocs on AGDcl using := of +| propagateOnNTListDcl(_, _, ps) -> ps.valueRefLocs + -- Exclude mempty/append declarations from forwarding +| tcMonoidAttributeDcl(_, _, _, _, _, te, _) -> te.valueRefLocs +| strategyAttributeDcl(_, _, _, _, e) -> e.valueRefLocs +end; + +aspect typeRefLocs on AGDcl using <- of +| defaultAttributionDcl(_, _, nt, _) -> if nt.lookupType.found then [(nt.location, nt.lookupType.dcl)] else [] +end; + +aspect typeRefLocs on AGDcl using := of +| propagateOnNTListDcl(_, nts, _) -> nts.typeRefLocs +| strategyAttributeDcl(_, _, _, _, e) -> e.typeRefLocs +end; + +aspect attributeRefLocs on AGDcl using <- of +| defaultAttributionDcl(at, _, _, _) -> if at.lookupAttribute.found then [(at.location, at.lookupAttribute.dcl)] else [] +end; + +aspect attributeRefLocs on AGDcl using := of + -- Only the listed attributes +| propagateOnNTListDcl(ats, _, _) -> ats.attributeRefLocs +| strategyAttributeDcl(_, _, _, _, e) -> e.attributeRefLocs +end; + +aspect attributeRefLocs on Constraint using <- of +| inhOccursConstraint(_, at, _, _, _, _) -> if at.lookupAttribute.found then [(at.location, at.lookupAttribute.dcl)] else [] +| synOccursConstraint(_, at, _, _, _, _, _) -> if at.lookupAttribute.found then [(at.location, at.lookupAttribute.dcl)] else [] +| annoOccursConstraint(_, at, _, _, _, _) -> if at.lookupAttribute.found then [(at.location, at.lookupAttribute.dcl)] else [] +end; + +aspect valueRefLocs on ProductionStmt using := of +| propagateOneAttr(_) -> [] +end; + +aspect attributeRefLocs on ProductionStmt using := of +| propagateOneAttr(at) -> if at.lookupAttribute.found then [(at.location, at.lookupAttribute.dcl)] else [] +end; + +aspect typeRefLocs on ProductionStmt using := of +| propagateOneAttr(_) -> [] +end; + +aspect valueRefLocs on DefLHS using <- of +| lhsDefLHS(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| childDefLHS(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| localDefLHS(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +aspect valueRefLocs on Expr using <- of +| baseExpr(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +aspect valueRefLocs on Expr using := of +| access(q, _, _) -> q.valueRefLocs +end; + +aspect attributeRefLocs on Expr using := of +| access(_, _, a) -> a.attributeRefLocs +end; + +aspect attributeRefLocs on AnnoExpr using <- of +| annoExpr(q, _, _) -> if q.lookupAttribute.found then [(q.location, q.lookupAttribute.dcl)] else [] +end; + +aspect valueRefLocs on PrimPattern using <- of +| prodPatternNormal(q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| prodPatternGadt(q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +aspect valueRefLocs on ProdNameList using <- of +| prodNameListCons(q, _, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| prodNameListOne(q) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +end; + +attribute valueRefLocs, typeRefLocs, attributeRefLocs occurs on StrategyExpr, StrategyExprs; +flowtype valueRefLocs {decorate, flowEnv, compiledGrammars} on StrategyExpr, StrategyExprs; +flowtype typeRefLocs {decorate, flowEnv, compiledGrammars} on StrategyExpr, StrategyExprs; +flowtype attributeRefLocs {decorate, flowEnv, compiledGrammars} on StrategyExpr, StrategyExprs; +propagate valueRefLocs, typeRefLocs on StrategyExpr, StrategyExprs; +propagate attributeRefLocs on StrategyExpr, StrategyExprs + excluding partialRef, totalRef; + +aspect valueRefLocs on top::StrategyExpr using <- of +| prodTraversal(q, _) -> if q.lookupValue.found then [(q.location, q.lookupValue.dcl)] else [] +| rewriteRule(_, _, _) -> checkExpr.valueRefLocs +end; + +aspect typeRefLocs on top::StrategyExpr using <- of +| rewriteRule(_, _, _) -> checkExpr.typeRefLocs +end; + +aspect attributeRefLocs on top::StrategyExpr using <- of +| rewriteRule(_, _, _) -> checkExpr.attributeRefLocs +end; + +aspect attributeRefLocs on StrategyExpr using := of +| partialRef(a) -> if attrDclFound then [(a.location, attrDcl)] else [] +| totalRef(a) -> if attrDclFound then [(a.location, attrDcl)] else [] +end; + +synthesized attribute valueFileRefLocs::map:Map; +synthesized attribute typeFileRefLocs::map:Map; +synthesized attribute attributeFileRefLocs::map:Map; + +attribute valueFileRefLocs, typeFileRefLocs, attributeFileRefLocs occurs on Compilation; + +aspect production compilation +top::Compilation ::= g::Grammars r::Grammars _ _ +{ + top.valueFileRefLocs = buildFileRefs((.valueRefLocs), g.grammarList); + top.typeFileRefLocs = buildFileRefs((.typeRefLocs), g.grammarList); + top.attributeFileRefLocs = buildFileRefs((.attributeRefLocs), g.grammarList); +} + +function buildFileRefs +annotation sourceGrammar occurs on a => +map:Map ::= accessor::([(Location, a)] ::= Decorated RootSpec) rs::[Decorated RootSpec] +{ + return directBuildTree(flatMap(\ r::Decorated RootSpec -> + map(\ item::(Location, a) -> + (r.grammarSource ++ item.1.filename, item.1, head(map:lookup(item.2.sourceGrammar, r.compiledGrammars)), item.2), + accessor(r)), + rs)); +} + +attribute valueRefLocs, typeRefLocs, attributeRefLocs occurs on InterfaceItems, InterfaceItem; +propagate valueRefLocs, typeRefLocs, attributeRefLocs on InterfaceItems; + +aspect default production +top::InterfaceItem ::= +{ + top.valueRefLocs := []; + top.typeRefLocs := []; + top.attributeRefLocs := []; +} + +abstract production refLocInterfaceItem +top::InterfaceItem ::= values::[(Location, ValueDclInfo)] types::[(Location, TypeDclInfo)] attrs::[(Location, AttributeDclInfo)] +{ + top.isEqual = true; -- Don't rebuild downstream grammars when referenced locations change + top.valueRefLocs := values; + top.typeRefLocs := types; + top.attributeRefLocs := attrs; +} + +aspect function packInterfaceItems +InterfaceItems ::= r::Decorated RootSpec +{ + interfaceItems <- [ + refLocInterfaceItem(r.valueRefLocs, r.typeRefLocs, r.attributeRefLocs) + ]; +} + +function lookupPos +[a] ::= line::Integer col::Integer items::[(Location, a)] +{ + return map(snd, filter( + \ item::(Location, a) -> + item.1.line <= line && item.1.endLine >= line && item.1.column <= col && item.1.endColumn >= col, + items)); +} + +function updateLocPath +Location ::= p::String l::Location +{ + return loc(p, l.line, l.column, l.endLine, l.endColumn, l.index, l.endIndex); +} + +function lookupDeclLocation +annotation sourceGrammar occurs on a, +annotation sourceLocation occurs on a => +[Location] ::= fileName::String line::Integer col::Integer decls::map:Map +{ + return map(\ item::(Decorated RootSpec, a) -> + updateLocPath(item.1.grammarSource ++ item.2.sourceLocation.filename, item.2.sourceLocation), + lookupPos(line, col, map:lookup(fileName, decls))); +} + +function findDeclLocation +[Location] ::= fileName::String line::Integer col::Integer c::Decorated Compilation +{ + return + lookupDeclLocation(fileName, line, col, c.valueFileRefLocs) ++ + lookupDeclLocation(fileName, line, col, c.typeFileRefLocs) ++ + lookupDeclLocation(fileName, line, col, c.attributeFileRefLocs); +} diff --git a/grammars/silver/compiler/metatranslation/Translation.sv b/grammars/silver/compiler/metatranslation/Translation.sv new file mode 100644 index 000000000..fe01f6eeb --- /dev/null +++ b/grammars/silver/compiler/metatranslation/Translation.sv @@ -0,0 +1,370 @@ +grammar silver:compiler:metatranslation; + +imports silver:reflect; +imports silver:langutil:pp; +imports silver:core; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type:syntax; +imports silver:compiler:modification:list; +imports silver:compiler:extension:patternmatching; + +function translate +Expr ::= loc::Location ast::AST +{ + ast.givenLocation = loc; + return ast.translation; +} + +function translatePattern +Pattern ::= loc::Location ast::AST +{ + ast.givenLocation = loc; + return ast.patternTranslation; +} + +synthesized attribute translation::a; +synthesized attribute patternTranslation::a; +synthesized attribute foundLocation::Maybe; +autocopy attribute givenLocation::Location; + +flowtype translation {givenLocation} on AST, ASTs, NamedASTs, NamedAST; +flowtype patternTranslation {givenLocation} on AST, ASTs; +flowtype foundLocation {} on ASTs, NamedASTs, NamedAST; + +attribute givenLocation, translation, patternTranslation occurs on AST; + +aspect production nonterminalAST +top::AST ::= prodName::String children::ASTs annotations::NamedASTs +{ + production givenLocation::Location = + fromMaybe(top.givenLocation, orElse(children.foundLocation, annotations.foundLocation)); + + production attribute antiquoteTranslation::Maybe with orElse; + antiquoteTranslation := nothing(); + + -- "Direct" antiquote productions + production attribute directAntiquoteProductions::[String] with ++; + directAntiquoteProductions := []; + antiquoteTranslation <- + if contains(prodName, directAntiquoteProductions) + then + let wrapped::AST = + case children of + | consAST(a, nilAST()) -> a + | consAST( + terminalAST(_, _, _), + consAST( + terminalAST(_, _, _), + consAST( + a, + consAST( + terminalAST(_, _, _), + nilAST())))) -> a + | _ -> error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") + end + in + case reify(wrapped) of + | right(e) -> just(e) + | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") + end + end + else nothing(); + + -- "Collection" antiquote productions + -- Key: antiquote production name + -- Value: (nonterminal short name, cons production name, append production name) + production attribute collectionAntiquoteProductions::[(String, String, String, String)] with ++; + collectionAntiquoteProductions := []; + antiquoteTranslation <- + do { + -- (antiquote production name, antiquote expr AST, rest AST) + antiquote::(String, AST, Decorated AST with {givenLocation}) <- + case children of + | consAST( + nonterminalAST(n, consAST(a, _), _), + consAST(rest, nilAST())) -> just((n, a, let decRest :: Decorated AST with {givenLocation} = rest in decRest end)) -- let is a workaround for type inference bug with case + | _ -> nothing() + end; + -- (nonterminal short name, cons production name, append production name) + trans::(String, String, String) <- + lookup(antiquote.1, collectionAntiquoteProductions); + if prodName == trans.2 then just(unit()) else nothing(); -- require prodName == trans.2 + return + case reify(antiquote.2) of + | right(e) -> + mkStrFunctionInvocation( + givenLocation, trans.3, [e, antiquote.3.translation]) + | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") + end; + }; + antiquoteTranslation <- + do { + -- (nonterminal short name, cons production name, append production name) + trans::(String, String, String) <- + lookup(prodName, collectionAntiquoteProductions); + return + errorExpr([err(givenLocation, s"$$${trans.1} may only occur as a member of ${trans.1}")], location=givenLocation); + }; + + antiquoteTranslation <- + if contains(prodName, patternAntiquoteProductions) + then just(errorExpr([err(givenLocation, "Pattern antiquote is invalid in expression context")], location=givenLocation)) + else nothing(); + + top.translation = + fromMaybe( + mkFullFunctionInvocation( + givenLocation, + baseExpr(qName(givenLocation, prodName), location=givenLocation), + children.translation, + annotations.translation), + antiquoteTranslation); + + production attribute patternAntiquoteTranslation::Maybe with orElse; + patternAntiquoteTranslation := nothing(); + + production attribute patternAntiquoteProductions::[String] with ++; + patternAntiquoteProductions := []; + patternAntiquoteTranslation <- + if contains(prodName, patternAntiquoteProductions) + then + let wrapped::AST = + case children of + | consAST(a, nilAST()) -> a + | consAST(terminalAST(_, _, _), consAST(a, nilAST())) -> a + | consAST( + terminalAST(_, _, _), + consAST( + terminalAST(_, _, _), + consAST( + a, + consAST( + terminalAST(_, _, _), + nilAST())))) -> a + | _ -> error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") + end + in + case reify(wrapped) of + | right(p) -> just(p) + | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") + end + end + else nothing(); + + patternAntiquoteTranslation <- + if contains(prodName, directAntiquoteProductions ++ map(fst, collectionAntiquoteProductions)) + then just(errorPattern([err(givenLocation, "Expression antiquote is invalid in pattern context")], location=givenLocation)) + else nothing(); + + -- Note that we intentionally ignore annotations here + top.patternTranslation = + fromMaybe( + prodAppPattern( + qName(givenLocation, prodName), + '(', + children.patternTranslation, + ')', + location=givenLocation), + patternAntiquoteTranslation); + + children.givenLocation = givenLocation; + annotations.givenLocation = givenLocation; +} + +aspect production terminalAST +top::AST ::= terminalName::String lexeme::String location::Location +{ + local locationAST::AST = reflect(new(location)); + locationAST.givenLocation = top.givenLocation; + + top.translation = + terminalConstructor( + 'terminal', '(', + nominalTypeExpr( + makeQNameType(terminalName, top.givenLocation), + location=top.givenLocation), + ',', + stringConst( + terminal(String_t, s"\"${escapeString(lexeme)}\"", top.givenLocation), + location=top.givenLocation), + ',', + locationAST.translation, + ')', location=top.givenLocation); + + -- TODO: What to do here- warn about this maybe? + -- Shouldn't really be an issue unless matching against concrete syntax containing non-fixed terminals + top.patternTranslation = wildcPattern('_', location=top.givenLocation); +} + +aspect production listAST +top::AST ::= vals::ASTs +{ + top.translation = + fullList( + '[', + foldr( + exprsCons(_, ',', _, location=top.givenLocation), + exprsEmpty(location=top.givenLocation), + vals.translation), + ']', + location=top.givenLocation); + top.patternTranslation = + listPattern('[', vals.patternTranslation, ']', location=top.givenLocation); +} + +aspect production stringAST +top::AST ::= s::String +{ + top.translation = + stringConst( + terminal(String_t, s"\"${escapeString(s)}\"", top.givenLocation), + location=top.givenLocation); + top.patternTranslation = + strPattern( + terminal(String_t, s"\"${escapeString(s)}\"", top.givenLocation), + location=top.givenLocation); +} + +aspect production integerAST +top::AST ::= i::Integer +{ + top.translation = + intConst(terminal(Int_t, toString(i), top.givenLocation), location=top.givenLocation); + top.patternTranslation = + intPattern(terminal(Int_t, toString(i), top.givenLocation), location=top.givenLocation); +} + +aspect production floatAST +top::AST ::= f::Float +{ + top.translation = + floatConst(terminal(Float_t, toString(f), top.givenLocation), location=top.givenLocation); + top.patternTranslation = + fltPattern(terminal(Float_t, toString(f), top.givenLocation), location=top.givenLocation); +} + +aspect production booleanAST +top::AST ::= b::Boolean +{ + top.translation = + if b + then trueConst('true', location=top.givenLocation) + else falseConst('false', location=top.givenLocation); + top.patternTranslation = + if b + then truePattern('true', location=top.givenLocation) + else falsePattern('false', location=top.givenLocation); +} + +aspect production anyAST +top::AST ::= x::a +{ + top.translation = + case reflectTypeName(x) of + just(n) -> error(s"Can't translate anyAST (type ${n})") + | nothing() -> error("Can't translate anyAST") + end; + top.patternTranslation = + case reflectTypeName(x) of + just(n) -> error(s"Can't translate anyAST (type ${n})") + | nothing() -> error("Can't translate anyAST") + end; +} + +attribute givenLocation, translation<[Expr]>, patternTranslation, foundLocation occurs on ASTs; + +aspect production consAST +top::ASTs ::= h::AST t::ASTs +{ + top.translation = h.translation :: t.translation; + top.patternTranslation = + patternList_more(h.patternTranslation, ',', t.patternTranslation, location=top.givenLocation); + top.foundLocation = + -- Try to reify the last child as a location + case t of + | nilAST() -> + case reify(h) of + | right(l) -> just(l) + | left(_) -> nothing() + end + | _ -> t.foundLocation + end; +} + +aspect production nilAST +top::ASTs ::= +{ + top.translation = []; + top.patternTranslation = patternList_nil(location=top.givenLocation); + top.foundLocation = nothing(); +} + +attribute givenLocation, translation<[(String, Expr)]>, foundLocation occurs on NamedASTs; + +aspect production consNamedAST +top::NamedASTs ::= h::NamedAST t::NamedASTs +{ + top.translation = h.translation :: t.translation; + top.foundLocation = orElse(h.foundLocation, t.foundLocation); +} + +aspect production nilNamedAST +top::NamedASTs ::= +{ + top.translation = []; + top.foundLocation = nothing(); +} + +attribute givenLocation, translation<(String, Expr)>, foundLocation occurs on NamedAST; + +aspect production namedAST +top::NamedAST ::= n::String v::AST +{ + top.translation = + -- hack to get annotation shortname + (last(explode(":", n)), v.translation); + top.foundLocation = + if n == "silver:core:location" + then + case reify(v) of + | right(l) -> just(l) + | left(msg) -> error(s"Error in reifying location:\n${msg}") + end + else nothing(); +} + +-- the functions below are directly referenced in reflection code in silver:compiler:extensions:silverconstruction +-- so make sure you grep for that if you change/move them. + +function makeName +Name ::= n::String loc::Location +{ + return + if isUpper(head(explode("", n))) + then nameIdUpper(terminal(IdUpper_t, n, loc), location=loc) + else nameIdLower(terminal(IdLower_t, n, loc), location=loc); +} + +function makeQName +QName ::= n::String loc::Location +{ + local ns::[Name] = map(makeName(_, loc), explode(":", n)); + return + foldr( + qNameCons(_, ':', _, location=loc), + qNameId(last(ns), location=loc), + init(ns)); +} + +function makeQNameType +QNameType ::= n::String loc::Location +{ + local ns::[String] = explode(":", n); + return + foldr( + qNameTypeCons(_, ':', _, location=loc), + qNameTypeId(terminal(IdUpper_t, last(ns), loc), location=loc), + map(makeName(_, loc), init(ns))); +} diff --git a/grammars/silver/modification/autocopyattr/AutoCopy.sv b/grammars/silver/compiler/modification/autocopyattr/AutoCopy.sv similarity index 94% rename from grammars/silver/modification/autocopyattr/AutoCopy.sv rename to grammars/silver/compiler/modification/autocopyattr/AutoCopy.sv index 8261f7d99..9e264a456 100644 --- a/grammars/silver/modification/autocopyattr/AutoCopy.sv +++ b/grammars/silver/compiler/modification/autocopyattr/AutoCopy.sv @@ -1,6 +1,4 @@ -grammar silver:modification:autocopyattr; - -import silver:util; +grammar silver:compiler:modification:autocopyattr; terminal AutoCopy_kwd 'autocopy' lexer classes {KEYWORD}; diff --git a/grammars/silver/compiler/modification/autocopyattr/DclInfo.sv b/grammars/silver/compiler/modification/autocopyattr/DclInfo.sv new file mode 100644 index 000000000..c78fbb8ab --- /dev/null +++ b/grammars/silver/compiler/modification/autocopyattr/DclInfo.sv @@ -0,0 +1,36 @@ +grammar silver:compiler:modification:autocopyattr; + +synthesized attribute isAutocopy :: Boolean occurs on AttributeDclInfo; + +aspect default production +top::AttributeDclInfo ::= +{ + top.isAutocopy = false; +} + +abstract production autocopyDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type +{ + top.fullName = fn; + propagate compareKey; + + top.typeScheme = polyType(bound, ty); + + top.isInherited = true; + top.isAutocopy = true; + + -- the core dispatchers + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: should probably be an error handler! + top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); +} + +-- Defs: + +function autocopyDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type +{ + return attrDef(defaultEnvItem(autocopyDcl(fn,bound,ty, sourceGrammar=sg, sourceLocation=sl))); +} + diff --git a/grammars/silver/compiler/modification/autocopyattr/Project.sv b/grammars/silver/compiler/modification/autocopyattr/Project.sv new file mode 100644 index 000000000..bda4ec708 --- /dev/null +++ b/grammars/silver/compiler/modification/autocopyattr/Project.sv @@ -0,0 +1,10 @@ +grammar silver:compiler:modification:autocopyattr; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:core; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; + +exports silver:compiler:modification:autocopyattr:java with silver:compiler:translation:java:core; +exports silver:compiler:modification:autocopyattr:convenience with silver:compiler:extension:convenience; + diff --git a/grammars/silver/compiler/modification/autocopyattr/convenience/Convenience.sv b/grammars/silver/compiler/modification/autocopyattr/convenience/Convenience.sv new file mode 100644 index 000000000..c741d09dd --- /dev/null +++ b/grammars/silver/compiler/modification/autocopyattr/convenience/Convenience.sv @@ -0,0 +1,18 @@ +grammar silver:compiler:modification:autocopyattr:convenience; + +import silver:compiler:modification:autocopyattr; +import silver:compiler:extension:convenience; +import silver:compiler:definition:core; +import silver:compiler:definition:concrete_syntax; +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:type; +import silver:compiler:definition:env; + +concrete production attributeDclAutoMultiple +top::AGDcl ::= 'autocopy' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' +{ + top.unparse = "autocopy attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; + forwards to appendAGDcl(attributeDclAuto($1, $2, a, tl, $5, te, $10, location=a.location), + makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), location=top.location); +} + diff --git a/grammars/silver/compiler/modification/autocopyattr/java/Autocopy.sv b/grammars/silver/compiler/modification/autocopyattr/java/Autocopy.sv new file mode 100644 index 000000000..9cf48c409 --- /dev/null +++ b/grammars/silver/compiler/modification/autocopyattr/java/Autocopy.sv @@ -0,0 +1,50 @@ +grammar silver:compiler:modification:autocopyattr:java; +import silver:compiler:modification:autocopyattr; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type:syntax; +import silver:compiler:definition:type; + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; + + + +aspect production attributeDclAuto +top::AGDcl ::= 'autocopy' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + local attribute className :: String; + className = "D" ++ a.name; + + top.genFiles := [pair(className ++ ".java", + +"package " ++ makeName(top.grammarName) ++ ";\n\n" ++ + +"import java.util.*;\n\n" ++ + +"public class " ++ className ++ " extends common.Decorator {\n\n" ++ + +"public static final " ++ className ++ " singleton = new " ++ className ++ "();\n\n" ++ + +"\tpublic void decorate(common.RTTIManager.Prodleton production) {\n" ++ +"\t\tdecorateAutoCopy(production, \"" ++ fName ++ "\");\n" ++ +"\t}\n" ++ +"}\n")]; +} + +aspect production attributionDcl +top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' +{ + top.setupInh <- + if at.lookupAttribute.dcl.isAutocopy then + "\t\t" ++ makeNTName(nt.lookupType.fullName) ++ ".decorators.add(" ++ makeDecoratorClassName(at.lookupAttribute.fullName) ++ ".singleton);\n" + else ""; +} + +function makeDecoratorClassName +String ::= s::String +{ + return substituteLast(".", ".D", makeName(s)); +} + diff --git a/grammars/silver/compiler/modification/collection/Collection.sv b/grammars/silver/compiler/modification/collection/Collection.sv new file mode 100644 index 000000000..5ed3513b6 --- /dev/null +++ b/grammars/silver/compiler/modification/collection/Collection.sv @@ -0,0 +1,426 @@ +grammar silver:compiler:modification:collection; + +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:list; + +--import silver:compiler:analysis:typechecking:core; +import silver:compiler:driver:util; +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; +import silver:compiler:translation:java:core; + +nonterminal NameOrBOperator with config, location, grammarName, compiledGrammars, flowEnv, productionFlowGraphs, errors, env, unparse, operation, operatorForType; +nonterminal Operation with compareTo, isEqual; +propagate compareTo, isEqual on Operation excluding functionOperation; + +synthesized attribute operation :: Operation; +inherited attribute operatorForType :: Type; + +concrete production exprOperator +top::NameOrBOperator ::= e::Expr +{ + top.unparse = e.unparse; + + top.operation = functionOperation(e, e.translation, false); + + top.errors := e.errors; + + local checkOperationType :: TypeCheck = + check(e.typerep, appTypes(functionType(2, []), [top.operatorForType, top.operatorForType, top.operatorForType])); + + e.downSubst = emptySubst(); + checkOperationType.downSubst = e.upSubst; + checkOperationType.finalSubst = checkOperationType.upSubst; + e.finalSubst = checkOperationType.finalSubst; + + top.errors <- + if !checkOperationType.typeerror then [] + else [err(top.location, e.unparse ++ " must be of type " ++ checkOperationType.rightpp ++ + " instead it is of type " ++ checkOperationType.leftpp)]; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); + + e.frame = bogusContext(myFlowGraph, sourceGrammar=top.grammarName); + e.originRules = []; + e.isRoot = false; +} + +concrete production plusplusOperator +top::NameOrBOperator ::= '++' +{ + top.unparse = "++"; + + top.operation = case top.operatorForType of + | stringType() -> plusPlusOperationString() + | listType(_) -> plusPlusOperationList() + | _ -> error("INTERNAL ERROR: operation attribute demanded for ++ that isn't string or list.") + end; + top.errors := case top.operatorForType of + | stringType() -> [] + | listType(_) -> [] + | _ -> [err(top.location, "++ operator will only work for collections of type list or String")] + end; +} + +concrete production borOperator +top::NameOrBOperator ::= '||' +{ + top.unparse = "||"; + + top.operation = borOperation(); + top.errors := case top.operatorForType of + | boolType() -> [] + | _ -> [err(top.location, "|| operator will only work for collections of type Boolean")] + end; +} +concrete production bandOperator +top::NameOrBOperator ::= '&&' +{ + top.unparse = "&&"; + + top.operation = bandOperation(); + top.errors := case top.operatorForType of + | boolType() -> [] + | _ -> [err(top.location, "&& operator will only work for collections of type Boolean")] + end; +} + +concrete production addOperator +top::NameOrBOperator ::= '+' +{ + top.unparse = "+"; + + top.operation = addOperation(); + top.errors := case top.operatorForType of + | intType() -> [] + | _ -> [err(top.location, "+ operator will only work for collections of type Integer")] + end; +} + +concrete production mulOperator +top::NameOrBOperator ::= '*' +{ + top.unparse = "*"; + + top.operation = addOperation(); + top.errors := case top.operatorForType of + | intType() -> [] + | _ -> [err(top.location, "* operator will only work for collections of type Integer")] + end; +} + +-- This would be much nicer if we could pass the Decorated Expr here, +-- but this nonterminal must be serializable as part of the environment. +abstract production functionOperation +top::Operation ::= e::Expr eTrans::String trackConstruction::Boolean +{ top.isEqual = + case top.compareTo of + | functionOperation(_, et, tc) -> et == eTrans && tc == trackConstruction + | _ -> false + end; +} +abstract production plusPlusOperationString +top::Operation ::= +{} +abstract production plusPlusOperationList +top::Operation ::= +{} +abstract production borOperation +top::Operation ::= +{} +abstract production bandOperation +top::Operation ::= +{} +abstract production addOperation +top::Operation ::= +{} +abstract production mulOperation +top::Operation ::= +{} + +--- Declarations --------------------------------------------------------------- +concrete production collectionAttributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + q.operatorForType = te.typerep; + + top.defs := [synColDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep, q.operation)]; + + propagate errors, flowDefs; + + top.errors <- tl.errorsTyVars; + top.errors <- te.errorsKindStar; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; +} + +concrete production collectionAttributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ a.name; + + tl.initialEnv = top.env; + tl.env = tl.envBindingTyVars; + te.env = tl.envBindingTyVars; + + q.operatorForType = te.typerep; + + top.defs := [inhColDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep, q.operation)]; + + propagate errors, flowDefs; + + top.errors <- tl.errorsTyVars; + top.errors <- te.errorsKindStar; + + top.errors <- + if length(getAttrDclAll(fName, top.env)) > 1 + then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] + else []; +} + + +concrete production collectionAttributeDclProd +top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + top.unparse = "production attribute " ++ a.name ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; + + top.productionAttributes := [localColDef(top.grammarName, a.location, fName, te.typerep, q.operation)]; + + production attribute fName :: String; + fName = top.frame.fullName ++ ":local:" ++ top.grammarName ++ ":" ++ a.name; + + top.defs := []; + + q.operatorForType = te.typerep; + top.errors <- q.errors; + + forwards to productionAttributeDcl($1, $2, a, $4, te, $8, location=top.location); +} + +--- The use semantics ---------------------------------------------------------- + +-- ERROR ON VALUE DEFS: +abstract production errorCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.errors <- [err(top.location, "The ':=' and '<-' operators can only be used for collections. " ++ val.name ++ " is not a collection.")]; + + -- TODO: this production also produces an error message, so we'll produce two errors for one flaw. + -- We don't want to use := for the errors, because we'd miss any errors in e, and we don't want to repeat + -- it because that will produce duplicate trees. + forwards to errorValueDef(val, e, location=top.location); +} +abstract production errorColNormalValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.errors <- [err(top.location, val.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")]; + + -- TODO: same problem + forwards to errorValueDef(val, e, location=top.location); +} + +-- NON-ERRORS for PRODUCTIONS + +abstract production baseCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.unparse = "\t" ++ val.unparse ++ " := " ++ e.unparse ++ ";"; + + e.isRoot = false; + + e.downSubst = top.downSubst; + -- the real type checking is done by the forward, but we must ensure things are tied up nicely + -- otherwise we don't specialize ntOrDecs in OUR e + forward.downSubst = unifyCheck(val.lookupValue.typeScheme.monoType, e.typerep, e.upSubst); + + forwards to localValueDef(val, e, location=top.location); +} +abstract production appendCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.unparse = "\t" ++ val.unparse ++ " <- " ++ e.unparse ++ ";"; + + e.isRoot = false; + + e.downSubst = top.downSubst; + -- the real type checking is done by the forward, but we must ensure things are tied up nicely + -- otherwise we don't specialize ntOrDecs in OUR e + forward.downSubst = unifyCheck(val.lookupValue.typeScheme.monoType, e.typerep, e.upSubst); + + forwards to localValueDef(val, e, location=top.location); +} + +-- NON-ERRORS for SYN ATTRS + +abstract production synBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; + + top.errors := e.errors; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + e.isRoot = false; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} +abstract production synAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; + + top.errors := e.errors; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + e.isRoot = false; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} + +-- NON-ERRORS for INHERITED ATTRS + +abstract production inhBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; + + top.errors := e.errors; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + e.isRoot = false; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} +abstract production inhAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; + + top.errors := e.errors; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + thread downSubst, upSubst on top, e, errCheck1, top; + + e.isRoot = false; + + errCheck1 = check(attr.typerep, e.typerep); + top.errors <- + if errCheck1.typeerror + then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] + else []; +} + +-- The use syntax -------------------------------------------------------------- + +terminal Contains_t '<-' lexer classes {SPECOP}; +terminal BaseContains_t ':=' lexer classes {SPECOP}; + +concrete production attrContainsAppend +top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur '<-' e::Expr ';' +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; + + -- defs must stay here explicitly, because we dispatch on types in the forward here! + top.productionAttributes := []; + top.defs := []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + forwards to + (if !dl.found || !attr.found + then errorAttributeDef(dl.errors ++ attr.errors, _, _, _, location=_) + else attr.attrDcl.attrAppendDefDispatcher)(dl, attr, e, top.location); +} + +concrete production attrContainsBase +top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur ':=' e::Expr ';' +{ + top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; + + -- defs must stay here explicitly, because we dispatch on types in the forward here! + top.productionAttributes := []; + top.defs := []; + + dl.defLHSattr = attr; + attr.attrFor = dl.typerep; + + forwards to + (if !dl.found || !attr.found + then errorAttributeDef(dl.errors ++ attr.errors, _, _, _, location=_) + else attr.attrDcl.attrBaseDefDispatcher)(dl, attr, e, top.location); +} + +concrete production valContainsAppend +top::ProductionStmt ::= val::QName '<-' e::Expr ';' +{ + top.unparse = val.unparse ++ " <- " ++ e.unparse ++ ";"; + + top.errors <- val.lookupValue.errors; + + top.productionAttributes := []; + top.defs := []; + + forwards to + (if null(val.lookupValue.dcls) + then errorValueDef(_, _, location=_) + else val.lookupValue.dcl.appendDefDispatcher)(val, e, top.location); +} + +concrete production valContainsBase +top::ProductionStmt ::= val::QName ':=' e::Expr ';' +{ + top.unparse = val.unparse ++ " := " ++ e.unparse ++ ";"; + + top.errors <- val.lookupValue.errors; + + top.productionAttributes := []; + top.defs := []; + + forwards to + (if null(val.lookupValue.dcls) + then errorValueDef(_, _, location=_) + else val.lookupValue.dcl.baseDefDispatcher)(val, e, top.location); +} + diff --git a/grammars/silver/compiler/modification/collection/DclInfo.sv b/grammars/silver/compiler/modification/collection/DclInfo.sv new file mode 100644 index 000000000..150ef6faa --- /dev/null +++ b/grammars/silver/compiler/modification/collection/DclInfo.sv @@ -0,0 +1,125 @@ +grammar silver:compiler:modification:collection; + +attribute operation, attrBaseDefDispatcher, attrAppendDefDispatcher occurs on AttributeDclInfo; +attribute operation, baseDefDispatcher, appendDefDispatcher occurs on ValueDclInfo; + +synthesized attribute attrBaseDefDispatcher :: (ProductionStmt ::= PartiallyDecorated DefLHS PartiallyDecorated QNameAttrOccur Expr Location); +synthesized attribute attrAppendDefDispatcher :: (ProductionStmt ::= PartiallyDecorated DefLHS PartiallyDecorated QNameAttrOccur Expr Location); + +synthesized attribute baseDefDispatcher :: (ProductionStmt ::= PartiallyDecorated QName Expr Location); +synthesized attribute appendDefDispatcher :: (ProductionStmt ::= PartiallyDecorated QName Expr Location); + +aspect default production +top::AttributeDclInfo ::= +{ + top.operation = error("Internal compiler error: must be defined for all collection attribute declarations"); + + top.attrBaseDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, "The ':=' operator can only be used for collections. " ++ attr.name ++ " is not a collection.")], dl, attr, e, location=l); + top.attrAppendDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, "The '<-' operator can only be used for collections. " ++ attr.name ++ " is not a collection.")], dl, attr, e, location=l); +} + +aspect default production +top::ValueDclInfo ::= +{ + top.operation = error("Internal compiler error: must be defined for all collection attribute declarations"); + + top.baseDefDispatcher = errorCollectionValueDef(_, _, location=_); + top.appendDefDispatcher = errorCollectionValueDef(_, _, location=_); +} + +abstract production synCollectionDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type o::Operation +{ + top.fullName = fn; + propagate compareTo, compareKey; + top.isEqual = + top.compareKey == top.compareTo.compareKey && + fn == top.compareTo.fullName && + top.typeScheme == top.compareTo.typeScheme && + o.isEqual; + + top.typeScheme = polyType(bound, ty); + top.isSynthesized = true; + top.operation = o; + + top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); + top.attrDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, attr.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.attrBaseDefDispatcher = synBaseColAttributeDef(_, _, _, location=_); + top.attrAppendDefDispatcher = synAppendColAttributeDef(_, _, _, location=_); +} +abstract production inhCollectionDcl +top::AttributeDclInfo ::= fn::String bound::[TyVar] ty::Type o::Operation +{ + top.fullName = fn; + propagate compareTo, compareKey; + top.isEqual = + top.compareKey == top.compareTo.compareKey && + fn == top.compareTo.fullName && + top.typeScheme == top.compareTo.typeScheme && + o.isEqual; + + top.typeScheme = polyType(bound, ty); + top.isInherited = true; + top.operation = o; + + top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); + top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: above should probably be an error handler! + top.attrDefDispatcher = + \ dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr l::Location -> + errorAttributeDef([err(l, attr.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); + top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); + + top.attrBaseDefDispatcher = inhBaseColAttributeDef(_, _, _, location=_); + top.attrAppendDefDispatcher = inhAppendColAttributeDef(_, _, _, location=_); +} + +abstract production localCollectionDcl +top::ValueDclInfo ::= fn::String ty::Type o::Operation +{ + top.fullName = fn; + propagate compareTo, isEqual; + + top.typeScheme = monoType(ty); + top.operation = o; + + top.refDispatcher = localReference(_, location=_); + top.defDispatcher = errorColNormalValueDef(_, _, location=_); + top.defLHSDispatcher = localDefLHS(_, location=_); + + top.baseDefDispatcher = baseCollectionValueDef(_, _, location=_); + top.appendDefDispatcher = appendCollectionValueDef(_, _, location=_); + + top.substitutedDclInfo = localCollectionDcl(fn, performRenaming(ty, top.givenSubstitution), o, sourceGrammar=top.sourceGrammar, sourceLocation=top.sourceLocation); + + -- TODO: attrOccursIndex + -- We shouldn't be forwarding here + forwards to localDcl(fn,ty,sourceGrammar=top.sourceGrammar,sourceLocation=top.sourceLocation); +} + + +-- Defs +function synColDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation +{ + return attrDef(defaultEnvItem(synCollectionDcl(fn,bound,ty,o,sourceGrammar=sg,sourceLocation=sl))); +} +function inhColDef +Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation +{ + return attrDef(defaultEnvItem(inhCollectionDcl(fn,bound,ty,o,sourceGrammar=sg,sourceLocation=sl))); +} +function localColDef +Def ::= sg::String sl::Location fn::String ty::Type o::Operation +{ + return valueDef(defaultEnvItem(localCollectionDcl(fn,ty,o,sourceGrammar=sg,sourceLocation=sl))); +} + diff --git a/grammars/silver/compiler/modification/collection/Project.sv b/grammars/silver/compiler/modification/collection/Project.sv new file mode 100644 index 000000000..2d43a3ae9 --- /dev/null +++ b/grammars/silver/compiler/modification/collection/Project.sv @@ -0,0 +1,8 @@ +grammar silver:compiler:modification:collection; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:core; +imports silver:compiler:definition:type; + +exports silver:compiler:modification:collection:java with silver:compiler:translation:java:core; + diff --git a/grammars/silver/compiler/modification/collection/java/Collection.sv b/grammars/silver/compiler/modification/collection/java/Collection.sv new file mode 100644 index 000000000..ba6d36906 --- /dev/null +++ b/grammars/silver/compiler/modification/collection/java/Collection.sv @@ -0,0 +1,258 @@ +grammar silver:compiler:modification:collection:java; +import silver:compiler:modification:collection; + + +import silver:compiler:definition:core; +import silver:compiler:definition:env; + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; + +{- + The initialization order is a bit scattered. There a several problems. + + ONE: Grammars can have cyclic dependencies. As a result, + we can never rely on the declaration, or the base (:=), appearing before + a contribution (<-). + + TWO: Production bodies are unordered. So even within one block of code, + it's quite possible for an assignment to preceed a declaration. + Or a contribution to preceed a base. + + For LOCALS, it's okay to create the CA object at declaration with setupInh. + The array was created a couple of lines up. + + For SYN, it might be okay to? I'm not sure. Playing it safe for now. + + For INH, you can't for sure use setupInh. You might be defining an inherited + attribute on a local that hasn't had it's inherited array created yet. + e.g. + x.inh := ... + local attribute x :: .... + N.B. that's an ordinary local, we're talking about inherited collections here, + not local collections. +-} + +inherited attribute leftOpTranslation::String occurs on Operation; +inherited attribute rightOpTranslation::String occurs on Operation; + +attribute translation occurs on Operation; + +aspect production functionOperation +top::Operation ::= e::Expr eTrans::String trackConstruction::Boolean +{ + top.translation = s"${eTrans}.invoke(context.originCtx, new Object[]{${top.leftOpTranslation}, ${top.rightOpTranslation}}, null)"; +} +-- (if tracked then newConstructionOriginUsingCtxRef++"," else "") +aspect production plusPlusOperationString +top::Operation ::= +{ + top.translation = s"new common.StringCatter(${top.leftOpTranslation}, ${top.rightOpTranslation})"; +} +aspect production plusPlusOperationList +top::Operation ::= +{ + top.translation = s"common.AppendCell.append(${top.leftOpTranslation}, ${top.rightOpTranslation})"; +} +aspect production borOperation +top::Operation ::= +{ + top.translation = s"(${top.leftOpTranslation} || ${top.rightOpTranslation})"; +} +aspect production bandOperation +top::Operation ::= +{ + top.translation = s"(${top.leftOpTranslation} && ${top.rightOpTranslation})"; +} +aspect production addOperation +top::Operation ::= +{ + top.translation = s"(${top.leftOpTranslation} + ${top.rightOpTranslation})"; +} +aspect production mulOperation +top::Operation ::= +{ + top.translation = s"(${top.leftOpTranslation} * ${top.rightOpTranslation})"; +} + +--- Declarations --------------------------------------------------------------- + +aspect production collectionAttributeDclProd +top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + local attribute ugh_dcl_hack :: ValueDclInfo; + ugh_dcl_hack = head(getValueDclAll(fName, top.env)); -- TODO + + -- Unlike synthesized and inherited attributes, locals can cheat because we know exactly + -- when the array we're indexing into was created: a couple of statements up from + -- exactly here. + + -- So we'll create the collection attribute object here, and not worry. + + local o :: Operation = q.operation; + o.leftOpTranslation = s"(${te.typerep.transType})result"; + o.rightOpTranslation = s"(${te.typerep.transType})this.getPieces().get(i).eval(context)"; + + top.setupInh <- s""" + ${top.frame.className}.localAttributes[${ugh_dcl_hack.attrOccursIndex}] = new common.CollectionAttribute() { + @SuppressWarnings("unchecked") + public Object eval(common.DecoratedNode context) { + common.OriginContext originCtx = context.originCtx; + common.Lazy base = this.getBase(); + if (base != null) { + ${te.typerep.transType} result = (${te.typerep.transType})base.eval(context); + for (int i = 0; i < this.getPieces().size(); i++) { + result = ${o.translation}; + } + return result; + } else { + throw new common.exceptions.MissingDefinitionException("Production attribute '${a.name}' in '${top.frame.fullName}' has no base definition"); + } + } + }; +"""; +} + +aspect production collectionAttributeDclSyn +top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + local attribute className :: String; + className = "CA" ++ a.name; + + local o :: Operation = q.operation; + o.leftOpTranslation = s"(${te.typerep.transType})result"; + o.rightOpTranslation = s"(${te.typerep.transType})this.getPieces().get(i).eval(context)"; + + top.genFiles := [pair(className ++ ".java", +s""" +package ${makeName(top.grammarName)}; + +public class ${className} extends common.CollectionAttribute { + + public ${className}(final int index) { + super(index); + } + + @SuppressWarnings("unchecked") + public Object eval(common.DecoratedNode context) { + common.OriginContext originCtx = context.originCtx; + ${te.typerep.transType} result = (${te.typerep.transType})this.getBase().eval(context); + for (int i = 0; i < this.getPieces().size(); i++) { + result = ${o.translation}; + } + return result; + } + +} +""")]; +} + +aspect production collectionAttributeDclInh +top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' +{ + local attribute className :: String; + className = "CA" ++ a.name; + + local o :: Operation = q.operation; + o.leftOpTranslation = s"(${te.typerep.transType})result"; + o.rightOpTranslation = s"(${te.typerep.transType})this.getPieces().get(i).eval(context)"; + + top.genFiles := [pair(className ++ ".java", +s""" +package ${makeName(top.grammarName)}; + +public class ${className} extends common.CollectionAttribute { + + public ${className}() { + super(); + } + + @SuppressWarnings("unchecked") + public Object eval(common.DecoratedNode context) { + common.OriginContext originCtx = context.originCtx; + ${te.typerep.transType} result = (${te.typerep.transType})this.getBase().eval(context); + for (int i = 0; i < this.getPieces().size(); i++) { + result = ${o.translation}; + } + return result; + } + +} +""")]; +} + +--- Use semantics translation -------------------------------------------------- + +---------- LOCALS --- +aspect production baseCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + -- for locals, the CA object was created already + top.translation = s""" + // ${val.unparse} := ${e.unparse} + ((common.CollectionAttribute)${top.frame.className}.localAttributes[${val.lookupValue.dcl.attrOccursIndex}]).setBase(${wrapLazy(e)}); +"""; +} +aspect production appendCollectionValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + -- for locals, the CA object was created already + top.translation = s""" + // ${val.unparse} <- ${e.unparse} + ((common.CollectionAttribute)${top.frame.className}.localAttributes[${val.lookupValue.dcl.attrOccursIndex}]).addPiece(${wrapLazy(e)}); +"""; +} + +---------- SYNTHESIZED ---- +aspect production synBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- := -} e::Expr +{ + top.translation = s""" + // ${dl.unparse}.${attr.unparse} := ${e.unparse} + if (${dl.translation}[${attr.attrOccursIndex}] == null) + ${dl.translation}[${attr.attrOccursIndex}] = new ${makeCAClassName(attr.attrDcl.fullName)}(${attr.attrOccursIndex}); + ((common.CollectionAttribute)${dl.translation}[${attr.attrOccursIndex}]).setBase(${wrapLazy(e)}); +"""; +} +aspect production synAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- <- -} e::Expr +{ + top.translation = s""" + // ${dl.unparse}.${attr.unparse} <- ${e.unparse} + if (${dl.translation}[${attr.attrOccursIndex}] == null) + ${dl.translation}[${attr.attrOccursIndex}] = new ${makeCAClassName(attr.attrDcl.fullName)}(${attr.attrOccursIndex}); + ((common.CollectionAttribute)${dl.translation}[${attr.attrOccursIndex}]).addPiece(${wrapLazy(e)}); +"""; +} + +---------- INHERITED ---- +aspect production inhBaseColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- := -} e::Expr +{ + top.translation = s""" + // ${dl.unparse}.${attr.unparse} := ${e.unparse} + if (${dl.translation}[${attr.attrOccursIndex}] == null) + ${dl.translation}[${attr.attrOccursIndex}] = new ${makeCAClassName(attr.attrDcl.fullName)}(); + ((common.CollectionAttribute)${dl.translation}[${attr.attrOccursIndex}]).setBase(${wrapLazy(e)}); +"""; +} +aspect production inhAppendColAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur {- <- -} e::Expr +{ + top.translation = s""" + // ${dl.unparse}.${attr.unparse} <- ${e.unparse} + if (${dl.translation}[${attr.attrOccursIndex}] == null) + ${dl.translation}[${attr.attrOccursIndex}] = new ${makeCAClassName(attr.attrDcl.fullName)}(); + ((common.CollectionAttribute)${dl.translation}[${attr.attrOccursIndex}]).addPiece(${wrapLazy(e)}); +"""; +} + + +function makeCAClassName +String ::= s::String +{ + return substituteLast(".", ".CA", makeName(s)); +} + diff --git a/grammars/silver/compiler/modification/copper/ActionCode.sv b/grammars/silver/compiler/modification/copper/ActionCode.sv new file mode 100644 index 000000000..1dadf7462 --- /dev/null +++ b/grammars/silver/compiler/modification/copper/ActionCode.sv @@ -0,0 +1,152 @@ +grammar silver:compiler:modification:copper; + +terminal Action_kwd 'action' lexer classes {MODIFIER}; + +concrete production concreteProductionDclAction +top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody 'action' acode::ActionCode_c +{ + top.unparse = forward.unparse ++ "action " ++ acode.unparse; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + + top.syntaxAst := + [ syntaxProduction(ns.namedSignature, + foldr(consProductionMod, nilProductionMod(), prodAction(acode.actionCode) :: pm.productionModifiers), + location=top.location, sourceGrammar=top.grammarName) + ]; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); + + ns.signatureName = fName; + ns.env = newScopeEnv(ns.defs, top.env); + pm.productionSig = ns.namedSignature; + pm.env = newScopeEnv(ns.actionDefs, top.env); + acode.frame = reduceActionContext(ns.namedSignature, myFlowGraph, sourceGrammar=top.grammarName); + acode.env = newScopeEnv(productionActionVars ++ acode.defs ++ ns.actionDefs, top.env); + + top.errors <- acode.errors; + + -- note that we're not merging the typing contexts between action blocks and productions + -- this seems reasonable since inference should never have effects across this border... + + forwards to concreteProductionDcl($1, $2, id, ns, pm, body, location=top.location); +} action { + insert semantic token IdFnProdDcl_t at id.location; + sigNames = []; +} + + +nonterminal ActionCode_c with location,config,unparse,actionCode,env,defs,grammarName,errors,frame, compiledGrammars, flowEnv, flowDefs; + +synthesized attribute actionCode :: String; + +concrete production actionCode_c +top::ActionCode_c ::= '{' stmts::ProductionStmts '}' +{ + top.unparse = "{\n" ++ stmts.unparse ++ "}\n"; + top.defs := flatMap(hackTransformLocals, stmts.defs); + propagate flowDefs; + + top.actionCode = + -- action code translation goes in the env/syntax AST, so we might demand it + -- when writing interface files in the presence of errors. + if !null(top.errors) then "" + else flatMap(hacklocaldeclarations, stmts.defs) ++ stmts.translation; + + top.errors := stmts.errors; + top.errors <- if top.frame.permitPluck && !stmts.containsPluck then + [err(top.location, "Disambiguation function without pluck")] else []; + + stmts.downSubst = emptySubst(); + stmts.originRules = []; +} + + +-- Support code to check the validity of disambiguation blocks. True if any elements +-- contained in the snoc-list (so this statement or before) are a pluck. Handles +-- raising errors if there are statements after a pluck. +synthesized attribute containsPluck :: Boolean occurs on ProductionStmts, ProductionStmt; +flowtype containsPluck {forward} on ProductionStmts, ProductionStmt; + +aspect containsPluck on ProductionStmts of +| productionStmtsSnoc(h, t) -> t.containsPluck || h.containsPluck +| productionStmtsNil() -> false +end; + +aspect containsPluck on ProductionStmt of +| pluckDef(_, _, _) -> true + + -- Only guaranteed to pluck a terminal if both th and el contain a pluck +| ifElseStmt(_, _, _, _, th, _, el) -> th.containsPluck && el.containsPluck + + -- Required by MWDA +| attributeDef(_, _, _, _, _, _) -> false +| errorAttributeDef(_, _, _, _) -> false +| valueEq(_, _, _, _) -> false + +| _ -> false +end; + +aspect errors on top::ProductionStmts using <- of +| productionStmtsSnoc(h, t) -> + if top.frame.permitPluck && h.containsPluck then [err(t.location, "Statement after pluck")] else [] +end; + +-- TODO hacky. ideally we'd do this where local attributes are declared, not here. +function hacklocaldeclarations +String ::= d::Def +{ + return + case d of + | valueDef(item) -> item.dcl.typeScheme.monoType.transType ++ " " ++ makeCopperName(item.dcl.fullName) ++ ";\n" + | _ -> "" -- TODO: possibly error?? + end; +} + +function hackTransformLocals +[Def] ::= d::Def +{ + return + case d of + | valueDef(item) when item.dcl matches localDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl) -> [parserLocalDef(sg,sl,fn,ty)] + | _ -> [] -- TODO: possibly error?? + end; +} + +-------------------------------------------------------------------------------- +-- Making children available in production action blocks + +-- We don't care about the LHS. + +synthesized attribute actionDefs :: [Def] occurs on ProductionSignature, ProductionRHS, ProductionRHSElem; + +flowtype actionDefs {decorate} on ProductionRHSElem; + +aspect production productionSignature +top::ProductionSignature ::= cl::ConstraintList '=>' lhs::ProductionLHS '::=' rhs::ProductionRHS +{ + top.actionDefs = rhs.actionDefs; +} + +aspect production productionRHSNil +top::ProductionRHS ::= +{ + top.actionDefs = []; +} + +aspect production productionRHSCons +top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS +{ + top.actionDefs = h.actionDefs ++ t.actionDefs; +} + +aspect production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + top.actionDefs = [actionChildDef(top.grammarName, t.location, id.name, t.typerep)]; +} diff --git a/grammars/silver/compiler/modification/copper/BlockContext.sv b/grammars/silver/compiler/modification/copper/BlockContext.sv new file mode 100644 index 000000000..095b82ced --- /dev/null +++ b/grammars/silver/compiler/modification/copper/BlockContext.sv @@ -0,0 +1,60 @@ +grammar silver:compiler:modification:copper; + +-- hack for all uses of this stuff in this grammar. note s on imports +imports silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; +imports silver:compiler:driver:util only RootSpec; +imports silver:compiler:definition:origins; + +attribute permitActions, permitPluck occurs on BlockContext; + +{-- + - Actions include parser attribute manipulation. print statement. + -} +synthesized attribute permitActions :: Boolean; +synthesized attribute permitPluck :: Boolean; + +aspect default production +top::BlockContext ::= +{ + top.permitActions = false; + top.permitPluck = false; +} + +{-- Terminal shift, parser attribute initialization -} +abstract production actionContext +top::BlockContext ::= g::ProductionGraph +{ + top.fullName = "__action__"; -- Used as part of naming locals... maybe we should fix that? TODO + top.signature = bogusNamedSignature(); + top.flowGraph = g; + top.lhsNtName = "::nolhs"; -- unfortunately this is sometimes accessed, and a dummy value works okay + + top.lazyApplication = false; + top.permitActions = true; + --top.permitProductionAttributes = false; -- denied by default + top.permitLocalAttributes = true; + -- TODO: signature? We DO have such info, but unclear what answer should be given... + top.originsContextSource = useBogusInfo("PARSERACTION_CONTEXT"); +} + +{-- Disambiguation groups -} +abstract production disambiguationContext +top::BlockContext ::= g::ProductionGraph +{ + top.permitPluck = true; + top.originsContextSource = useBogusInfo("PARSERACTION_CONTEXT"); + forwards to actionContext(g, sourceGrammar=top.sourceGrammar); +} + +{-- Production reduce actions -} +abstract production reduceActionContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.fullName = sig.fullName; + top.signature = sig; -- TODO: figure out if this is ever used for actions? + top.className = makeProdName(top.fullName); -- child references in production actions use it + top.originsContextSource = useBogusInfo("PARSERACTION_CONTEXT"); + + forwards to actionContext(g, sourceGrammar=top.sourceGrammar); +} + diff --git a/grammars/silver/compiler/modification/copper/BuildProcess.sv b/grammars/silver/compiler/modification/copper/BuildProcess.sv new file mode 100644 index 000000000..a45ed09fc --- /dev/null +++ b/grammars/silver/compiler/modification/copper/BuildProcess.sv @@ -0,0 +1,175 @@ +grammar silver:compiler:modification:copper; + +import silver:compiler:definition:concrete_syntax:copper as copper; +import silver:compiler:driver; +import silver:compiler:translation:java:driver; +import silver:reflect:nativeserialize; +import silver:util:cmdargs; + +{---------------------------------} +{- Define the --copperdump flag. -} +{---------------------------------} + +synthesized attribute forceCopperDump :: Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= _ +{ + top.forceCopperDump = false; +} + +abstract production copperdumpFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.forceCopperDump = true; + forwards to rest; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--copperdump", paramString=nothing(), + help="force Copper to dump parse table information", + flagParser=flag(copperdumpFlag))]; +} + +{--------------------------------------} +{- Define the --copper-xml-dump flag. -} +{--------------------------------------} + +synthesized attribute copperXmlDump::Boolean occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= _ +{ top.copperXmlDump = false; } + +production copperXmlDumpFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.copperXmlDump = true; + forwards to rest; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ + flagSpec(name="--copper-xml-dump", paramString=nothing(), + help="dump the specification being passed to Copper as XML", + flagParser=flag(copperXmlDumpFlag))]; +} + +{--------------------------------} +{- Request building of parsers. -} +{--------------------------------} + +-- Skips parser specs from SILVER_HOST_GEN +-- The way that feature works, they shouldn't need regeneration. +function obtainParserSpecs +[ParserSpec] ::= g::Decorated RootSpec benv::BuildEnv +{ + return if g.generateLocation != benv.silverGen then [] + else g.parserSpecs; +} + +aspect production compilation +top::Compilation ::= g::Grammars _ _ benv::BuildEnv +{ + -- Add the Copper compiler to the CLASSPATH. In theory, this is only + -- necessary when building Silver (or other programs that invoke the Copper + -- compiler directly), and could be replaced with the Copper runtime + -- otherwise. If we re-do the build system, we could make the Copper compiler + -- JAR not include the runtime, link against the runtime here, and make + -- importing the silver:compiler:definition:concrete_syntax:copper grammar + -- add the Copper compiler back. + classpathRuntime <- ["${sh}/jars/CopperCompiler.jar"]; + + -- Get the parsers. + production allParsers :: [ParserSpec] = + flatMap(obtainParserSpecs(_, benv), grammarsRelevant); + + -- Don't delete generated parsers from past builds + keepFiles <- + flatMap(\ spec::ParserSpec -> + let baseName::String = benv.silverGen ++ "src/" ++ grammarToPath(spec.sourceGrammar) ++ makeParserName(spec.fullName) + in [baseName ++ ".copperdump", baseName ++ ".java"] + end, + allParsers); + + -- Generate the .java files. + top.postOps <- + map(buildParserAction(_, g.compiledGrammars, benv.silverGen, top.config), allParsers); +} + +{------------------------------} +{- Build the parsers to Java. -} +{------------------------------} + +@{- Writes a parser out to a file. + - + - We create a separate GrammarAction rather than building this into genJava + - as an optimization. The build process iteratively rebuilds all dependent + - grammars whenever an interface file change, which may happen for a number of + - reasons (e.g. defining a new attribute) that do not affect the concrete + - syntax used to build a parser in a rebuilt grammar. + - To avoid this, we cache the concrete syntax AST used to build the parser, + - and skip parser generation if the syntax AST is unchanged. + -} +abstract production buildParserAction +top::DriverAction ::= spec::ParserSpec compiledGrammars::EnvTree silverGen::String cmdArgs::Decorated CmdArgs +{ + spec.compiledGrammars = compiledGrammars; + + local specCstAst::SyntaxRoot = spec.cstAst; + local outDir::String = silverGen ++ "src/" ++ grammarToPath(spec.sourceGrammar); + local parserName::String = makeParserName(spec.fullName); + local dumpFile::String = outDir ++ parserName ++ ".copperdump"; + + + -- cmdArgs _could_ be top.config, if the driver were to decorate DriverAction + -- with config. However, the driver doesn't, and it seems like it'd be a pain + -- to make it do so. + local buildGrammar::IO = + if null(specCstAst.cstErrors) then do { + if cmdArgs.noJavaGeneration then do { + -- Skip translating to Java. + return 0; + } else do { + mkdir(outDir); + eprintln("Generating parser " ++ spec.fullName ++ "."); + ret::Integer <- copper:compileParserBean(specCstAst.copperParser, + makeName(spec.sourceGrammar), parserName, false, + outDir ++ parserName ++ ".java", cmdArgs.forceCopperDump, + parserName ++ ".html", cmdArgs.copperXmlDump); + when_(ret == 0, + case nativeSerialize(new(specCstAst)) of + | left(e) -> error("BUG: specCstAst was not serializable; hopefully this was caused by the most recent change to the copper modification: " ++ e) + | right(dump) -> writeBinaryFile(dumpFile, dump) + end); + return ret; + }; + } else do { + eprintln("CST errors while generating parser " ++ spec.fullName ++ ":\n" ++ + implode("\n", specCstAst.cstErrors)); + return 1; + }; + + top.run = do { + dumpFileExists :: Boolean <- isFile(dumpFile); + if !cmdArgs.doClean && dumpFileExists then do { + dumpFileContents::ByteArray <- readBinaryFile(dumpFile); + let dumpMatched::Either = map(eq(specCstAst, _), nativeDeserialize(dumpFileContents)); + if dumpMatched == right(true) && !cmdArgs.forceCopperDump then do { + eprintln("Parser " ++ spec.fullName ++ " is up to date."); + return 0; + } else do { + buildGrammar; + }; + } else do { + buildGrammar; + }; + }; + + top.order = 7; +} diff --git a/grammars/silver/modification/copper/CustomLayout.sv b/grammars/silver/compiler/modification/copper/CustomLayout.sv similarity index 97% rename from grammars/silver/modification/copper/CustomLayout.sv rename to grammars/silver/compiler/modification/copper/CustomLayout.sv index cd5923c2f..64c55751c 100644 --- a/grammars/silver/modification/copper/CustomLayout.sv +++ b/grammars/silver/compiler/modification/copper/CustomLayout.sv @@ -1,4 +1,4 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; -- It would be nice if this weren't a keyword, but... terminal Layout_kwd 'layout' lexer classes {KEYWORD,RESERVED}; diff --git a/grammars/silver/compiler/modification/copper/DclInfo.sv b/grammars/silver/compiler/modification/copper/DclInfo.sv new file mode 100644 index 000000000..c1971005a --- /dev/null +++ b/grammars/silver/compiler/modification/copper/DclInfo.sv @@ -0,0 +1,111 @@ +grammar silver:compiler:modification:copper; + +monoid attribute superClasses::[String] occurs on ValueDclInfo; + +aspect default production +top::ValueDclInfo ::= +{ + top.superClasses := []; +} + +{-- + - Reference to something declared as "parser attribute foo ..." + -} +abstract production parserAttrDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + top.refDispatcher = parserAttributeReference(_, location=_); + top.defDispatcher = parserAttributeValueDef(_, _, location=_); + top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); +} + +{-- + - The names of possible pluckable terminals are jammed in the environment using this dcl. + -} +abstract production pluckTermDcl +top::ValueDclInfo ::= fn::String +{ + top.fullName = fn; + propagate isEqual; + + -- TODO: Still needs work to prevent returning terminals + -- that are not part of the disambiguation set. + top.typeScheme = monoType(terminalIdType()); + + top.refDispatcher = pluckTerminalReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} + +{-- + - Reference to a lexer class declaration. Has its own namespace in the environment, for now. + -} +abstract production lexerClassDcl +top::ValueDclInfo ::= fn::String superClasses::[String] +{ + top.fullName = fn; + propagate isEqual; + top.superClasses := superClasses; + + -- If we made lexer classes proper types, it might simplify a lot of code. + -- We wouldn't need a separate namespace, they could just be in the type namespace. + -- Currently referencing a lexer class gives a list of its member's TerminalIds. + top.typeScheme = monoType(listType(terminalIdType())); + top.refDispatcher = lexerClassReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} + +{-- + - lexeme/filename/line/column. Used in terminal and production action code. + -} +abstract production termAttrValueDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + top.refDispatcher = termAttrValueReference(_, location=_); + top.defDispatcher = termAttrValueValueDef(_, _, location=_); + top.defLHSDispatcher = errorDefLHS(_, location=_); +} + +{-- + - Reference to production's children from production action code. + -} +abstract production actionChildDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + top.refDispatcher = actionChildReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); + top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); -- TODO: specialize this +} + +{-- + - Reference to a local variable ("local foo :: Type = ...") inside an action block. + -} +abstract production parserLocalDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + -- TODO: use specialized ones that give better errors messages! + top.refDispatcher = parserAttributeReference(_, location=_); + top.defDispatcher = parserAttributeValueDef(_, _, location=_); + top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); +} diff --git a/grammars/silver/modification/copper/DisambiguationGroup.sv b/grammars/silver/compiler/modification/copper/DisambiguationGroup.sv similarity index 75% rename from grammars/silver/modification/copper/DisambiguationGroup.sv rename to grammars/silver/compiler/modification/copper/DisambiguationGroup.sv index a612bdf86..f56fdc0ef 100644 --- a/grammars/silver/modification/copper/DisambiguationGroup.sv +++ b/grammars/silver/compiler/modification/copper/DisambiguationGroup.sv @@ -1,4 +1,4 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; terminal Disambiguation_kwd 'disambiguate' lexer classes {KEYWORD}; @@ -21,8 +21,10 @@ top::AGDcl ::= 'disambiguate' terms::TermList acode::ActionCode_c local myFlowGraph :: ProductionGraph = constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - acode.frame = disambiguationContext(myFlowGraph); + acode.frame = disambiguationContext(myFlowGraph, sourceGrammar=top.grammarName); - top.syntaxAst := [syntaxDisambiguationGroup(fName, terms.termList, false, acode.actionCode)]; + top.syntaxAst := + [ syntaxDisambiguationGroup(fName, terms.termList, false, acode.actionCode, + location=top.location, sourceGrammar=top.grammarName) + ]; } - diff --git a/grammars/silver/compiler/modification/copper/Env.sv b/grammars/silver/compiler/modification/copper/Env.sv new file mode 100644 index 000000000..ee8756df6 --- /dev/null +++ b/grammars/silver/compiler/modification/copper/Env.sv @@ -0,0 +1,160 @@ +grammar silver:compiler:modification:copper; + +-------------------------------------------------------------------------------- +-- Defs.sv + +synthesized attribute lexerClassList :: [EnvItem] occurs on Defs, Def; + +aspect production nilDefs +top::Defs ::= +{ + top.lexerClassList = []; +} + +aspect production consDefs +top::Defs ::= e1::Def e2::Defs +{ + top.lexerClassList = e1.lexerClassList ++ e2.lexerClassList; +} + +aspect default production +top::Def ::= +{ + top.lexerClassList = []; +} + +abstract production lxrClsDef +top::Def ::= d::EnvItem +{ + propagate filterItems, filterIncludeOnly, filterIncludeHiding, withRenames, renamed, pfx, prepended, compareTo, isEqual; + top.lexerClassList = [d]; + top.valueList = [d]; +} + +function parserAttrDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(parserAttrDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} + +function pluckTermDef +Def ::= sg::String sl::Location fn::String +{ + return valueDef(defaultEnvItem(pluckTermDcl(fn,sourceGrammar=sg,sourceLocation=sl))); +} + +function lexerClassDef +Def ::= sg::String sl::Location fn::String sc::[String] +{ + return lxrClsDef(defaultEnvItem(lexerClassDcl(fn,sc,sourceGrammar=sg,sourceLocation=sl))); +} + +function termAttrValueDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(termAttrValueDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} + +function actionChildDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(actionChildDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} + +function parserLocalDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(parserLocalDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} + +-------------------------------------------------------------------------------- +-- Env.sv + +synthesized attribute lexerClassTree :: EnvTree occurs on Env; + +aspect production i_emptyEnv +top::Env ::= +{ + top.lexerClassTree = emptyEnvTree(); +} + +aspect production i_appendEnv +top::Env ::= e1::Decorated Env e2::Decorated Env +{ + top.lexerClassTree = appendEnvTree(e1.lexerClassTree, e2.lexerClassTree); +} + +aspect production i_newScopeEnv +top::Env ::= d::Defs e::Decorated Env +{ + top.lexerClassTree = consEnvTree(d.lexerClassList, e.lexerClassTree); +} + +aspect production i_occursEnv +top::Env ::= _ e::Decorated Env +{ + top.lexerClassTree = e.lexerClassTree; +} + +function getLexerClassDcl +[ValueDclInfo] ::= search::String e::Decorated Env +{ + return searchEnvTree(search, e.lexerClassTree); +} + +function expandTransitiveSuperClasses +[String] ::= seen::[String] toExpand::[String] e::Decorated Env +{ + return + case toExpand of + | [] -> seen + | c :: cs -> + if contains(c, seen) + then expandTransitiveSuperClasses(seen, cs, e) + else expandTransitiveSuperClasses( + c :: seen, flatMap((.superClasses), getLexerClassDcl(c, e)) ++ cs, e) + end; +} + +-------------------------------------------------------------------------------- +-- QName.sv + +synthesized attribute lookupLexerClass :: Decorated QNameLookup occurs on QName; + +aspect production qNameId +top::QName ::= id::Name +{ + top.lookupLexerClass = decorate customLookup("lexer class", getLexerClassDcl(top.name, top.env), top.name, top.location) with {}; +} + +aspect production qNameCons +top::QName ::= id::Name ':' qn::QName +{ + top.lookupLexerClass = decorate customLookup("lexer class", getLexerClassDcl(top.name, top.env), top.name, top.location) with {}; +} + +aspect production qNameError +top::QName ::= msg::[Message] +{ + top.lookupLexerClass = decorate errorLookup(msg) with {}; +} + + +-------------------------------------------------------------------------------- + +-- Some pre-defined variables in certain contexts + +global i_lexemeVariable :: [Def] = + [termAttrValueDef("DBGtav", bogusLoc(), "lexeme", stringType())]; +global i_shiftableVariable :: [Def] = + [termAttrValueDef("DBGtav", bogusLoc(), "shiftable", listType(terminalIdType()))]; +global i_locVariables :: [Def] = [ + termAttrValueDef("DBGtav", bogusLoc(), "filename", stringType()), + termAttrValueDef("DBGtav", bogusLoc(), "line", intType()), + termAttrValueDef("DBGtav", bogusLoc(), "column", intType())]; + +global terminalActionVars :: [Def] = i_lexemeVariable ++ i_locVariables; +global productionActionVars :: [Def] = i_locVariables; +global disambiguationActionVars :: [Def] = i_lexemeVariable ++ i_locVariables; +global disambiguationClassActionVars :: [Def] = i_lexemeVariable ++ i_shiftableVariable ++ i_locVariables; + diff --git a/grammars/silver/compiler/modification/copper/Expr.sv b/grammars/silver/compiler/modification/copper/Expr.sv new file mode 100644 index 000000000..94fbf977f --- /dev/null +++ b/grammars/silver/compiler/modification/copper/Expr.sv @@ -0,0 +1,135 @@ +grammar silver:compiler:modification:copper; + +import silver:util:treeset as ts; + +terminal DisambiguationFailure_t 'disambiguationFailure' lexer classes {KEYWORD, RESERVED}; + +concrete production failureTerminalIdExpr +top::Expr ::= 'disambiguationFailure' +{ + top.unparse = "disambiguationFailure"; + propagate freeVars; + top.errors := []; + top.typerep = terminalIdType(); + + top.translation = "(-1)"; + top.lazyTranslation = top.translation; + + top.upSubst = top.downSubst; +} + +abstract production actionChildReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := []; -- Should only ever be in scope when valid + + top.typerep = q.lookupValue.typeScheme.monoType; + + top.translation = "((" ++ top.typerep.transType ++ ")((common.Node)RESULTfinal).getChild(" ++ top.frame.className ++ ".i_" ++ q.lookupValue.fullName ++ "))"; + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} + +abstract production pluckTerminalReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := []; -- Should only be referenceable from a context where its valid. + + -- TODO: It would be nice to have a more specific type here, see comment below. + top.typerep = terminalIdType(); + + top.translation = makeCopperName(q.lookupValue.fullName); -- Value right here? + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} + +-- TODO: Distinct from pluckTerminalReference (since this can occur in any action block and +-- reference any terminal), but maybe it shouldn't be? These productions do almost the same +-- thing. Also having type classes would let us use a more specific type than generic TerminalId, +-- and pluckTerminalReference wouldn't need to cheat with a fresh type. +abstract production terminalIdReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := if !top.frame.permitActions + then [err(top.location, "References to terminal identifiers can only be made in action blocks")] + else []; + + top.typerep = terminalIdType(); + + top.translation = s"Terminals.${makeCopperName(q.lookupValue.fullName)}.num()"; + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} + +abstract production lexerClassReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := if !top.frame.permitActions + then [err(top.location, "References to lexer class members can only be made in action blocks")] + else []; + + -- TODO: This should be a more specific type with type classes + top.typerep = listType(terminalIdType()); + + top.translation = makeCopperName(q.lookupValue.fullName); + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} + +abstract production parserAttributeReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := if !top.frame.permitActions + then [err(top.location, "References to parser attributes can only be made in action blocks")] + else []; + + top.typerep = q.lookupValue.typeScheme.monoType; + + top.translation = + s"""(${makeCopperName(q.lookupValue.fullName)} == null? (${top.typerep.transType})common.Util.error("Uninitialized parser attribute ${q.name}") : ${makeCopperName(q.lookupValue.fullName)})"""; + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} + +abstract production termAttrValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + top.freeVars := ts:fromList([q.name]); + + top.errors := []; -- Should only ever be in scope in action blocks + + top.typerep = q.lookupValue.typeScheme.monoType; + + -- Yeah, it's a big if/then/else block, but these are all very similar and related. + top.translation = + if q.name == "lexeme" then "new common.StringCatter(lexeme)" else + if q.name == "shiftable" then "shiftableList" else + if q.name == "line" then "virtualLocation.getLine()" else + if q.name == "column" then "virtualLocation.getColumn()" else + if q.name == "filename" then "new common.StringCatter(virtualLocation.getFileName())" else + error("unknown actionTerminalReference " ++ q.name); -- should never be called, but here for safety + top.lazyTranslation = top.translation; -- never, but okay! + + top.upSubst = top.downSubst; +} diff --git a/grammars/silver/compiler/modification/copper/LexerClass.sv b/grammars/silver/compiler/modification/copper/LexerClass.sv new file mode 100644 index 000000000..7bc6609e2 --- /dev/null +++ b/grammars/silver/compiler/modification/copper/LexerClass.sv @@ -0,0 +1,124 @@ +grammar silver:compiler:modification:copper; + +terminal Lexer_kwd 'lexer' lexer classes {KEYWORD}; +terminal Extends_kwd 'extends' lexer classes {MODIFIER}; + +terminal IdLexerClass_t '' lexer classes {IDENTIFIER, lsp:Class}; +terminal IdLexerClassDcl_t '' lexer classes {IDENTIFIER, lsp:Class, lsp:Declaration}; + +concrete production lexerClassDclEmpty +top::AGDcl ::= 'lexer' 'class' id::Name ';' +{ + forwards to lexerClassDecl($1, $2, id, lexerClassModifiersNone(location=$4.location), $4, location=top.location); +} action { + insert semantic token IdLexerClassDcl_t at id.location; +} + +concrete production lexerClassDecl +top::AGDcl ::= 'lexer' 'class' id::Name modifiers::LexerClassModifiers ';' +{ + top.unparse = "lexer class " ++ id.name ++ modifiers.unparse ++ ";"; + + production attribute fName :: String; + fName = top.grammarName ++ ":" ++ id.name; + + top.defs := [lexerClassDef(top.grammarName, id.location, fName, modifiers.superClasses)]; + + top.errors <- if length(getLexerClassDcl(fName, top.env)) > 1 + then [err(id.location, "Lexer class '" ++ fName ++ "' is already bound.")] + else []; + + top.errors := modifiers.errors; + + top.syntaxAst := + [ syntaxLexerClass(fName, + foldr(consLexerClassMod, nilLexerClassMod(), modifiers.lexerClassModifiers), + location=top.location, sourceGrammar=top.grammarName)]; +} action { + insert semantic token IdLexerClassDcl_t at id.location; +} + +nonterminal LexerClassModifiers with config, location, unparse, lexerClassModifiers, superClasses, errors, env, grammarName, compiledGrammars, flowEnv; +closed nonterminal LexerClassModifier with config, location, unparse, lexerClassModifiers, superClasses, errors, env, grammarName, compiledGrammars, flowEnv; + +monoid attribute lexerClassModifiers :: [SyntaxLexerClassModifier]; + +propagate errors on LexerClassModifiers, LexerClassModifier; +propagate lexerClassModifiers, superClasses on LexerClassModifiers; + +abstract production lexerClassModifiersNone +top::LexerClassModifiers ::= +{ + top.unparse = ""; +} +concrete production lexerClassModifierSingle +top::LexerClassModifiers ::= tm::LexerClassModifier +{ + top.unparse = tm.unparse; +} +concrete production lexerClassModifiersCons +top::LexerClassModifiers ::= h::LexerClassModifier ',' t::LexerClassModifiers +{ + top.unparse = h.unparse ++ " " ++ t.unparse; +} + +aspect default production +top::LexerClassModifier ::= +{ + top.superClasses := []; +} + +concrete production lexerClassModifierExtends +top::LexerClassModifier ::= 'extends' cls::LexerClasses +{ + top.unparse = "extends " ++ cls.unparse; + + top.lexerClassModifiers := + [ lexerClassExtends(cls.lexerClasses, location=top.location, + sourceGrammar=top.grammarName) + ]; + top.superClasses := cls.lexerClasses; +} + +concrete production lexerClassModifierDominates +top::LexerClassModifier ::= 'dominates' terms::TermPrecs +{ + top.unparse = "dominates " ++ terms.unparse; + + top.lexerClassModifiers := + [ lexerClassDominates(terms.precTermList, location=top.location, + sourceGrammar=top.grammarName) + ]; +} + +concrete production lexerClassModifierSubmitsTo +top::LexerClassModifier ::= 'submits' 'to' terms::TermPrecs +{ + top.unparse = "submits to " ++ terms.unparse; + + top.lexerClassModifiers := + [ lexerClassSubmits(terms.precTermList, location=top.location, + sourceGrammar=top.grammarName) + ]; +} + +concrete production lexerClassModifierDisambiguate +top::LexerClassModifier ::= 'disambiguate' acode::ActionCode_c +{ + top.unparse = "disambiguate " ++ acode.unparse; + + top.lexerClassModifiers := + [ lexerClassDisambiguate(acode.actionCode, location=top.location, + sourceGrammar=top.grammarName) + ]; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); + + acode.env = newScopeEnv(disambiguationClassActionVars ++ acode.defs, top.env); + acode.frame = disambiguationContext(myFlowGraph, sourceGrammar=top.grammarName); +} diff --git a/grammars/silver/modification/copper/ParserAttributes.sv b/grammars/silver/compiler/modification/copper/ParserAttributes.sv similarity index 77% rename from grammars/silver/modification/copper/ParserAttributes.sv rename to grammars/silver/compiler/modification/copper/ParserAttributes.sv index 5338f065c..721e37baf 100644 --- a/grammars/silver/modification/copper/ParserAttributes.sv +++ b/grammars/silver/compiler/modification/copper/ParserAttributes.sv @@ -1,4 +1,4 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; concrete production attributeDclParser top::AGDcl ::= 'parser' 'attribute' a::Name '::' te::TypeExpr 'action' acode::ActionCode_c ';' @@ -10,11 +10,11 @@ top::AGDcl ::= 'parser' 'attribute' a::Name '::' te::TypeExpr 'action' acode::Ac top.defs := [parserAttrDef(top.grammarName, a.location, fName, te.typerep)]; + propagate errors; top.errors <- if length(getValueDclAll(fName, top.env)) > 1 then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] else []; - - top.errors := te.errors ++ acode.errors; + top.errors <- te.errorsKindStar; -- oh no again! local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; @@ -23,10 +23,13 @@ top::AGDcl ::= 'parser' 'attribute' a::Name '::' te::TypeExpr 'action' acode::Ac local myFlowGraph :: ProductionGraph = constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - acode.frame = actionContext(myFlowGraph); + acode.frame = actionContext(myFlowGraph, sourceGrammar=top.grammarName); acode.env = newScopeEnv(acode.defs, top.env); - top.syntaxAst := [syntaxParserAttribute(fName, te.typerep, acode.actionCode)]; + top.syntaxAst := + [ syntaxParserAttribute(fName, te.typerep, acode.actionCode, + location=top.location, sourceGrammar=top.grammarName) + ]; } concrete production attributeAspectParser @@ -39,11 +42,10 @@ top::AGDcl ::= 'aspect' 'parser' 'attribute' a::QName 'action' acode::ActionCode top.defs := []; + propagate errors; top.errors <- if null(a.lookupValue.dcls) then [err(a.location, "Undefined attribute '" ++ a.name ++ "'.")] else []; - - top.errors := acode.errors; -- oh no again! local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; @@ -52,9 +54,11 @@ top::AGDcl ::= 'aspect' 'parser' 'attribute' a::QName 'action' acode::ActionCode local myFlowGraph :: ProductionGraph = constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - acode.frame = actionContext(myFlowGraph); + acode.frame = actionContext(myFlowGraph, sourceGrammar=top.grammarName); acode.env = newScopeEnv(acode.defs, top.env); - top.syntaxAst := [syntaxParserAttributeAspect(fName, acode.actionCode)]; + top.syntaxAst := + [ syntaxParserAttributeAspect(fName, acode.actionCode, + location=top.location, sourceGrammar=top.grammarName) + ]; } - diff --git a/grammars/silver/modification/copper/ParserDcl.sv b/grammars/silver/compiler/modification/copper/ParserDcl.sv similarity index 86% rename from grammars/silver/modification/copper/ParserDcl.sv rename to grammars/silver/compiler/modification/copper/ParserDcl.sv index d1dc16ca4..5684b016e 100644 --- a/grammars/silver/modification/copper/ParserDcl.sv +++ b/grammars/silver/compiler/modification/copper/ParserDcl.sv @@ -1,8 +1,8 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; -import silver:driver:util only computeDependencies; +import silver:compiler:driver:util only computeDependencies; -terminal Parser_kwd 'parser' lexer classes {KEYWORD}; -- not RESERVED? +terminal Parser_kwd 'parser' lexer classes {KEYWORD,RESERVED}; -- TODO: You know, maybe parser specs should get moved over here as well. @@ -29,14 +29,17 @@ top::AGDcl ::= 'parser' n::Name '::' t::TypeExpr '{' m::ParserComponents '}' production fName :: String = top.grammarName ++ ":" ++ n.name; production namedSig :: NamedSignature = - namedSignature(fName, - [namedSignatureElement("stringToParse", stringType()), - namedSignatureElement("filenameToReport", stringType())], - namedSignatureElement("__func__lhs", nonterminalType("core:ParseResult", [t.typerep])), - []); + namedSignature(fName, nilContext(), + foldNamedSignatureElements([ + namedSignatureElement("stringToParse", stringType()), + namedSignatureElement("filenameToReport", stringType())]), + namedSignatureElement("__func__lhs", appType(nonterminalType("silver:core:ParseResult", [starKind()], false), t.typerep)), + nilNamedSignatureElement()); production spec :: ParserSpec = - parserSpec(top.location, top.grammarName, fName, t.typerep.typeName, m.moduleNames, m.customLayout, m.terminalPrefixes, m.grammarTerminalPrefixes, m.syntaxAst); + parserSpec(fName, t.typerep.typeName, m.moduleNames, m.customLayout, + m.terminalPrefixes, m.grammarTerminalPrefixes, m.syntaxAst, + sourceGrammar=top.grammarName, location=top.location); spec.compiledGrammars = top.compiledGrammars; top.parserSpecs := [spec]; -- Note that this is undecorated. diff --git a/grammars/silver/modification/copper/Prefix.sv b/grammars/silver/compiler/modification/copper/Prefix.sv similarity index 87% rename from grammars/silver/modification/copper/Prefix.sv rename to grammars/silver/compiler/modification/copper/Prefix.sv index b0c31ac55..23e757a0d 100644 --- a/grammars/silver/modification/copper/Prefix.sv +++ b/grammars/silver/compiler/modification/copper/Prefix.sv @@ -1,11 +1,10 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; -import core:monad; -import silver:definition:regex; -import silver:extension:easyterminal; -- only Terminal_t, EasyTerminalRef; +import silver:regex; +import silver:compiler:extension:easyterminal; -- only Terminal_t, EasyTerminalRef; -terminal Prefix_t 'prefix' lexer classes {KEYWORD, RESERVED}; +terminal Prefix_t 'prefix' lexer classes {MODIFIER, RESERVED}; concrete production prefixParserComponentModifier top::ParserComponentModifier ::= 'prefix' ts::TerminalPrefixItems 'with' s::TerminalPrefix @@ -31,6 +30,8 @@ top::TerminalPrefix ::= s::QName top.unparse = s.unparse; top.errors <- s.lookupType.errors; top.terminalPrefix = makeCopperName(s.lookupType.fullName); +} action { + insert semantic token IdType_t at s.baseNameLoc; } concrete production newTermModifiersTerminalPrefix @@ -46,9 +47,11 @@ top::TerminalPrefix ::= r::RegExpr tm::TerminalModifiers end; local terminalFullName::String = top.grammarName ++ ":" ++ terminalName; top.syntaxAst <- - [syntaxTerminal( - terminalFullName, regex, - foldr(consTerminalMod, nilTerminalMod(), tm.terminalModifiers))]; + [ syntaxTerminal( + terminalFullName, regex, + foldr(consTerminalMod, nilTerminalMod(), tm.terminalModifiers), + location=top.location, sourceGrammar=top.grammarName) + ]; top.genFiles <- terminalTranslation(terminalName, top.grammarName, tm.lexerClasses); top.terminalPrefix = makeCopperName(terminalFullName); } @@ -67,7 +70,7 @@ top::TerminalPrefix ::= t::String_t forwards to newTermModifiersTerminalPrefix( -- We pass the string prefix as a regex that does not contain the prefix separator - regExpr('/', regexLiteral(substring(1, length(t.lexeme) - 1, t.lexeme)), '/', location=top.location), + regExpr(regexLiteral(substring(1, length(t.lexeme) - 1, t.lexeme)), location=top.location), -- Specify which terminals this prefix prefixes. This is used to find the separator to -- append to the regex when normalizing the CST AST terminalModifierSingle( @@ -133,6 +136,8 @@ top::TerminalPrefixItem ::= t::QName top.unparse = t.unparse; top.errors := t.lookupType.errors; top.prefixItemNames = [t.lookupType.fullName]; +} action { + insert semantic token IdType_t at t.baseNameLoc; } concrete production easyTerminalRefTerminalPrefixItem @@ -164,6 +169,7 @@ top::ParserComponent ::= 'prefer' t::QName 'over' ts::TermList ';' pluckTAction.frame = error("Not needed"); pluckTAction.downSubst = emptySubst(); pluckTAction.finalSubst = emptySubst(); + pluckTAction.originRules = []; local tName::String = t.lookupType.dcl.fullName; top.syntaxAst <- @@ -176,8 +182,11 @@ top::ParserComponent ::= 'prefer' t::QName 'over' ts::TermList ';' \ tsNames::[String] -> syntaxDisambiguationGroup( s"Prefer_${toString(top.location.line)}_${tName}__${implode("__", tsNames)}", - tName :: tsNames, false, pluckTAction.translation), + tName :: tsNames, false, pluckTAction.translation, + location=top.location, sourceGrammar=top.grammarName), tail(powerSet(ts.termList))); +} action { + insert semantic token IdType_t at t.baseNameLoc; } -- Prefix separator @@ -188,7 +197,11 @@ top::LexerClassModifier ::= 'prefix' 'separator' s::String_t { top.unparse = s"prefix separator ${s.lexeme}"; - top.lexerClassModifiers := [lexerClassPrefixSeperator(substring(1, length(s.lexeme) - 1, s.lexeme))]; + top.lexerClassModifiers := + [ lexerClassPrefixSeperator( + substring(1, length(s.lexeme) - 1, s.lexeme), + location=top.location, sourceGrammar=top.grammarName) + ]; } {- Not supported due to ambiguity with modifiers on prefix terminal defined diff --git a/grammars/silver/modification/copper/ProductionStmt.sv b/grammars/silver/compiler/modification/copper/ProductionStmt.sv similarity index 76% rename from grammars/silver/modification/copper/ProductionStmt.sv rename to grammars/silver/compiler/modification/copper/ProductionStmt.sv index 571444119..746a53c75 100644 --- a/grammars/silver/modification/copper/ProductionStmt.sv +++ b/grammars/silver/compiler/modification/copper/ProductionStmt.sv @@ -1,9 +1,15 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; terminal Pluck_kwd 'pluck' lexer classes {KEYWORD,RESERVED}; terminal Print_kwd 'print' lexer classes {KEYWORD,RESERVED}; terminal PushToken_kwd 'pushToken' lexer classes {KEYWORD,RESERVED}; +terminal Insert_kwd 'insert' lexer classes {KEYWORD}; +terminal Semantic_kwd 'semantic' lexer classes {KEYWORD}; +terminal Token_kwd 'token' lexer classes {KEYWORD}; +terminal At_kwd 'at' lexer classes {KEYWORD}; +disambiguate Insert_kwd, IdLower_t { pluck Insert_kwd; } + concrete production namePrint top::Name ::= 'print' { forwards to name("print", top.location); } @@ -29,20 +35,22 @@ top::ProductionStmt ::= 'pluck' e::Expr ';' then [err(top.location, "'pluck' allowed only in disambiguation-group parser actions.")] else []; + e.originRules = []; + e.isRoot = true; + local tyCk :: TypeCheck = check(e.typerep, terminalIdType()); - tyCk.downSubst = e.upSubst; tyCk.finalSubst = top.finalSubst; top.errors <- if tyCk.typeerror then [err(top.location, "'pluck' expects one of the terminals it is disambiguating between. Instead it received "++tyCk.leftpp)] else []; + thread downSubst, upSubst on top, e, tyCk, top; + + -- TODO: Enforce that the plucked terminal is one of those that are being disambiguated between. -- Currently all that is checked is that it is a terminal. - - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; } concrete production printStmt @@ -60,9 +68,10 @@ top::ProductionStmt ::= 'print' e::Expr ';' local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; + e.originRules = []; + e.isRoot = true; + + thread downSubst, upSubst on top, e, errCheck1, top; errCheck1 = check(e.typerep, stringType()); top.errors <- @@ -78,7 +87,7 @@ top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' } abstract production parserAttributeValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr { top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; @@ -92,11 +101,12 @@ top::ProductionStmt ::= val::Decorated QName e::Expr local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; + thread downSubst, upSubst on top, e, errCheck1, top; + + e.originRules = []; + e.isRoot = true; - errCheck1 = check(e.typerep, val.lookupValue.typerep); + errCheck1 = check(e.typerep, val.lookupValue.typeScheme.monoType); top.errors <- if errCheck1.typeerror then [err(top.location, "Parser attribute " ++ val.name ++ " has type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] @@ -118,9 +128,10 @@ top::ProductionStmt ::= 'pushToken' '(' val::QName ',' lexeme::Expr ')' ';' local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - lexeme.downSubst = top.downSubst; - errCheck1.downSubst = lexeme.upSubst; - top.upSubst = errCheck1.upSubst; + lexeme.originRules = []; + lexeme.isRoot = false; + + thread downSubst, upSubst on top, lexeme, errCheck1, top; errCheck1 = check(lexeme.typerep, stringType()); top.errors <- @@ -129,6 +140,35 @@ top::ProductionStmt ::= 'pushToken' '(' val::QName ',' lexeme::Expr ')' ';' else []; } +concrete production insertSemanticTokenStmt +top::ProductionStmt ::= 'insert' 'semantic' 'token' n::QNameType 'at' loc::Expr ';' +{ + top.unparse = "\t" ++ "insert semantic token " ++ n.unparse ++ " at " ++ loc.unparse ++ ";"; + + propagate errors; + top.errors <- + if !top.frame.permitActions + then [err(top.location, "Semantic tokens may only be inserted in action blocks")] + else []; + + top.translation = s"""insertToken(new ${makeTerminalName(n.lookupType.fullName)}(new common.StringCatter(""), ${loc.translation}));"""; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + loc.originRules = []; + loc.isRoot = false; + + thread downSubst, upSubst on top, loc, errCheck1, top; + + errCheck1 = check(loc.typerep, nonterminalType("silver:core:Location", [], false)); + top.errors <- + if errCheck1.typeerror + then [err(loc.location, s"Semantic token position expected a ${errCheck1.rightpp}, but got ${errCheck1.leftpp}")] + else []; +} action { + insert semantic token IdType_t at n.location; +} + concrete production blockStmt top::ProductionStmt ::= '{' stmts::ProductionStmts '}' { @@ -160,11 +200,12 @@ top::ProductionStmt ::= 'if' '(' condition::Expr ')' th::ProductionStmt 'else' e top.translation = s"if(${condition.translation}) {${th.translation}} else {${el.translation}}"; + condition.originRules = []; + condition.isRoot = false; + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - condition.downSubst = top.downSubst; - errCheck1.downSubst = condition.upSubst; - top.upSubst = errCheck1.upSubst; + thread downSubst, upSubst on top, condition, errCheck1, top; th.downSubst = top.downSubst; th.finalSubst = th.upSubst; @@ -188,7 +229,7 @@ top::ProductionStmt ::= 'if' '(' condition::Expr ')' th::ProductionStmt abstract production parserAttributeDefLHS -top::DefLHS ::= q::Decorated QName +top::DefLHS ::= q::PartiallyDecorated QName { top.name = q.name; top.unparse = q.unparse; @@ -203,11 +244,11 @@ top::DefLHS ::= q::Decorated QName top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.typerep = q.lookupValue.typerep; + top.typerep = q.lookupValue.typeScheme.monoType; } abstract production termAttrValueValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr { top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; @@ -229,11 +270,12 @@ top::ProductionStmt ::= val::Decorated QName e::Expr local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; + thread downSubst, upSubst on top, e, errCheck1, top; + + e.originRules = []; + e.isRoot = true; - errCheck1 = check(e.typerep, val.lookupValue.typerep); + errCheck1 = check(e.typerep, val.lookupValue.typeScheme.monoType); top.errors <- if errCheck1.typeerror then [err(top.location, "Terminal attribute " ++ val.name ++ " has type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] diff --git a/grammars/silver/compiler/modification/copper/Project.sv b/grammars/silver/compiler/modification/copper/Project.sv new file mode 100644 index 000000000..2683ba890 --- /dev/null +++ b/grammars/silver/compiler/modification/copper/Project.sv @@ -0,0 +1,17 @@ +grammar silver:compiler:modification:copper; + +imports silver:langutil:lsp as lsp; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:definition:concrete_syntax:ast; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:modification:list; + +--imports silver:compiler:analysis:typechecking:core; + +imports silver:compiler:translation:java:core; +imports silver:compiler:translation:java:type; diff --git a/grammars/silver/modification/copper/TermList.sv b/grammars/silver/compiler/modification/copper/TermList.sv similarity index 89% rename from grammars/silver/modification/copper/TermList.sv rename to grammars/silver/compiler/modification/copper/TermList.sv index 192fab9f4..35b10cfcf 100644 --- a/grammars/silver/modification/copper/TermList.sv +++ b/grammars/silver/compiler/modification/copper/TermList.sv @@ -1,4 +1,4 @@ -grammar silver:modification:copper; +grammar silver:compiler:modification:copper; -- This structure mirrors the structure of TermPrecList. -- TermPrecList notes that it is poorly structured, perhaps @@ -7,7 +7,7 @@ grammar silver:modification:copper; nonterminal TermList with config, grammarName, unparse, location, termList, defs, errors, env; -monoid attribute termList :: [String] with [], ++; +monoid attribute termList :: [String]; propagate errors, termList on TermList; @@ -15,12 +15,16 @@ concrete production termListOne terms::TermList ::= t::QName { forwards to termList(t,termListNull(location=t.location), location=t.location); +} action { + insert semantic token IdType_t at t.baseNameLoc; } concrete production termListCons terms::TermList ::= t::QName ',' terms_tail::TermList { forwards to termList(t,terms_tail,location=terms.location); +} action { + insert semantic token IdType_t at t.baseNameLoc; } diff --git a/grammars/silver/compiler/modification/copper/TerminalDcl.sv b/grammars/silver/compiler/modification/copper/TerminalDcl.sv new file mode 100644 index 000000000..d2232aaa8 --- /dev/null +++ b/grammars/silver/compiler/modification/copper/TerminalDcl.sv @@ -0,0 +1,188 @@ +grammar silver:compiler:modification:copper; + +terminal Dominates_t 'dominates' lexer classes {MODIFIER}; +terminal Submits_t 'submits' lexer classes {MODIFIER}; +terminal Classes_kwd 'classes' lexer classes {MODIFIER}; + +monoid attribute lexerClasses :: [String]; +attribute lexerClasses occurs on TerminalModifier, TerminalModifiers; +propagate lexerClasses on TerminalModifiers, TerminalModifier; + +concrete production terminalModifierDominates +top::TerminalModifier ::= 'dominates' terms::TermPrecs +{ + top.unparse = "dominates { " ++ terms.unparse ++ " } "; + + top.terminalModifiers := [termDominates(terms.precTermList)]; + propagate errors; +} + +concrete production terminalModifierSubmitsTo +top::TerminalModifier ::= 'submits' 'to' terms::TermPrecs +{ + top.unparse = "submits to { " ++ terms.unparse ++ " } " ; + + top.terminalModifiers := [termSubmits(terms.precTermList)]; + propagate errors; +} + +concrete production terminalModifierClassSpec +top::TerminalModifier ::= 'lexer' 'classes' cl::LexerClasses +{ + top.unparse = "lexer classes { " ++ cl.unparse ++ " } " ; + + top.terminalModifiers := [termClasses(cl.lexerClasses)]; + propagate errors; +} + +concrete production terminalModifierActionCode +top::TerminalModifier ::= 'action' acode::ActionCode_c +{ + top.unparse = "action " ++ acode.unparse; + + top.terminalModifiers := [termAction(acode.actionCode)]; + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); + + acode.frame = actionContext(myFlowGraph, sourceGrammar=top.grammarName); + acode.env = newScopeEnv(terminalActionVars ++ acode.defs, top.env); + + propagate errors; +} + +monoid attribute precTermList :: [String]; + +nonterminal TermPrecs with config, grammarName, unparse, location, precTermList, errors, env; +propagate errors, precTermList on TermPrecs; + +concrete production termPrecsOne +top::TermPrecs ::= t::QName +{ + forwards to termPrecs(termPrecList(t,termPrecListNull(location=t.location), location=t.location), location=top.location); +} action { + insert semantic token IdType_t at t.baseNameLoc; +} + +concrete production termPrecsList +top::TermPrecs ::= '{' terms::TermPrecList '}' +{ + forwards to termPrecs(terms,location=top.location); +} + +abstract production termPrecs +top::TermPrecs ::= terms::TermPrecList +{ + top.unparse = s"{${terms.unparse}}"; +} + +nonterminal TermPrecList with config, grammarName, unparse, location, precTermList, errors, env; +propagate errors, precTermList on TermPrecList; + +abstract production termPrecList +top::TermPrecList ::= h::QName t::TermPrecList +{ + top.unparse = if t.unparse == "" + then h.unparse + else h.unparse ++ ", " ++ t.unparse; + + production fName::String = if null(h.lookupType.dcls) then h.lookupLexerClass.dcl.fullName else h.lookupType.dcl.fullName; + + top.precTermList <- [fName]; + + -- Since we're looking it up in two ways, do the errors ourselves + top.errors <- if null(h.lookupType.dcls) && null(h.lookupLexerClass.dcls) + then [err(h.location, "Undeclared terminal or lexer class '" ++ h.name ++ "'.")] + else if length(h.lookupType.dcls) + length(h.lookupLexerClass.dcls) > 1 + then [err(h.location, "Ambiguous reference to terminal or lexer class '" ++ h.name ++ "'. Possibilities are:\n" ++ + printPossibilities(h.lookupType.dcls) ++ if !null(h.lookupLexerClass.dcls) then ", " ++ printPossibilities(h.lookupLexerClass.dcls) else "")] + else []; +} + +abstract production termPrecListNull +top::TermPrecList ::= +{ + top.unparse = ""; +} + +concrete production termPrecListOne +top::TermPrecList ::= t::QName +{ + forwards to termPrecList(t, termPrecListNull(location=top.location), location=top.location); +} action { + insert semantic token IdType_t at t.baseNameLoc; +} + +concrete production termPrecListCons +top::TermPrecList ::= t::QName ',' terms_tail::TermPrecList +{ + forwards to termPrecList(t, terms_tail,location=top.location); +} action { + insert semantic token IdType_t at t.baseNameLoc; +} + +nonterminal LexerClasses with location, config, unparse, lexerClasses, errors, env; +propagate errors, lexerClasses on LexerClasses; + +concrete production lexerClassesOne +top::LexerClasses ::= n::QName +{ + forwards to lexerClasses(lexerClassListMain(n, lexerClassListNull(location=top.location), location=top.location), location=top.location); +} action { + insert semantic token IdLexerClassDcl_t at n.baseNameLoc; +} + +concrete production lexerClassesList +top::LexerClasses ::= '{' cls::LexerClassList '}' +{ + forwards to lexerClasses(cls,location=top.location); +} + +abstract production lexerClasses +top::LexerClasses ::= cls::LexerClassList +{ + top.unparse = s"{${cls.unparse}}"; +} + +nonterminal LexerClassList with location, config, unparse, lexerClasses, errors, env; +propagate errors, lexerClasses on LexerClassList; + +concrete production lexerClassListOne +top::LexerClassList ::= n::QName +{ + forwards to lexerClassListMain(n,lexerClassListNull(location=n.location), location=n.location); +} action { + insert semantic token IdLexerClassDcl_t at n.baseNameLoc; +} + +concrete production lexerClassListCons +top::LexerClassList ::= n::QName ',' cl_tail::LexerClassList +{ + forwards to lexerClassListMain(n,cl_tail,location=top.location); +} action { + insert semantic token IdLexerClassDcl_t at n.baseNameLoc; +} + + +abstract production lexerClassListMain +top::LexerClassList ::= n::QName t::LexerClassList +{ + top.unparse = if t.unparse == "" + then n.unparse + else n.unparse ++ ", " ++ t.unparse; + + top.errors <- n.lookupLexerClass.errors; + + top.lexerClasses <- [n.lookupLexerClass.dcl.fullName]; +} + +abstract production lexerClassListNull +cl::LexerClassList ::= +{ + cl.unparse = ""; +} + diff --git a/grammars/silver/compiler/modification/copper_mda/Analysis.sv b/grammars/silver/compiler/modification/copper_mda/Analysis.sv new file mode 100644 index 000000000..d65601b57 --- /dev/null +++ b/grammars/silver/compiler/modification/copper_mda/Analysis.sv @@ -0,0 +1,71 @@ +grammar silver:compiler:modification:copper_mda; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:definition:concrete_syntax:ast; +imports silver:compiler:modification:copper; + +import silver:compiler:driver:util only computeDependencies, RootSpec; + + +terminal CopperMDA 'copper_mda' lexer classes {KEYWORD}; + +concrete production copperMdaDcl +top::AGDcl ::= 'copper_mda' testname::Name '(' orig::QName ')' '{' m::ParserComponents '}' +{ + top.unparse = ""; + + propagate errors, moduleNames; + + top.errors <- orig.lookupValue.errors; + + local spec :: [ParserSpec] = + if !orig.lookupValue.found then [] + else findSpec(orig.lookupValue.fullName, + head(searchEnvTree(orig.lookupValue.dcl.sourceGrammar, top.compiledGrammars)).parserSpecs); + + top.errors <- if !orig.lookupValue.found || !null(spec) then [] + else [err(orig.location, orig.name ++ " is not a parser.")]; + + -- Ignoring prefixes and any SyntaxDcls generated by the ParserComponents for now... + top.mdaSpecs = + case spec of + | parserSpec(fn,snt,hg,csl,_,_,_) :: _ -> [mdaSpec(top.grammarName ++":"++ testname.name, snt, + hg, m.moduleNames, csl, location=top.location, sourceGrammar=top.grammarName)] + | _ -> [] + end; +} + +function findSpec +[ParserSpec] ::= n::String s::[ParserSpec] +{ + return if null(s) then [] + else if n == head(s).fullName then [head(s)] + else findSpec(n, tail(s)); +} + +nonterminal MdaSpec with location, sourceGrammar, fullName, compiledGrammars,cstAst; + +abstract production mdaSpec +top::MdaSpec ::= fn::String snt::String hostgrams::[String] extgrams::[String] customStartLayout::Maybe<[String]> +{ + top.fullName = fn; + + -- TODO: see TODO s in ParserSpec + production hostmed :: ModuleExportedDefs = + moduleExportedDefs(error("no sl"), top.compiledGrammars, + computeDependencies(hostgrams ++ extgrams, top.compiledGrammars), hostgrams, []); + + production extmed :: ModuleExportedDefs = + moduleExportedDefs(error("no sl"), top.compiledGrammars, + computeDependencies(hostgrams ++ extgrams, top.compiledGrammars), extgrams, []); + + top.cstAst = + cstCopperMdaRoot(fn, snt, + foldr(consSyntax, nilSyntax(), hostmed.syntaxAst), + foldr(consSyntax, nilSyntax(), extmed.syntaxAst), + customStartLayout, location=top.location, + sourceGrammar=top.sourceGrammar); +} + diff --git a/grammars/silver/compiler/modification/copper_mda/BuildProcess.sv b/grammars/silver/compiler/modification/copper_mda/BuildProcess.sv new file mode 100644 index 000000000..8d2d961c2 --- /dev/null +++ b/grammars/silver/compiler/modification/copper_mda/BuildProcess.sv @@ -0,0 +1,65 @@ +grammar silver:compiler:modification:copper_mda; + +import silver:compiler:definition:concrete_syntax:copper as copper; +import silver:compiler:driver; +import silver:compiler:translation:java:driver; +import silver:compiler:translation:java:core only makeParserName, makeName; +import silver:reflect:nativeserialize; +import silver:util:cmdargs; + +aspect production compilation +top::Compilation ::= g::Grammars _ _ benv::BuildEnv +{ + -- TODO: consider examining all grammars, not just grammarsToTranslate? + -- I believe this choice was originally because we weren't serializing MdaSpecs to + -- interface files, but I think we could easily start doing that new with the new serialization code? + top.postOps <- map(runMdaAction(_, g.compiledGrammars, benv.silverGen ++ "src/"), + flatMap((.mdaSpecs), grammarsToTranslate)); +} + +abstract production runMdaAction +top::DriverAction ::= spec::MdaSpec compiledGrammars::EnvTree silverGen::String +{ + spec.compiledGrammars = compiledGrammars; + + local specCstAst :: SyntaxRoot = spec.cstAst; + local outDir :: String = silverGen ++ "src/" ++ grammarToPath(spec.sourceGrammar); + local parserName :: String = makeParserName(spec.fullName); + local dumpFile :: String = outDir ++ parserName ++ ".copperdump"; + + local buildGrammar::IO = + if null(specCstAst.cstErrors) then do { + mkdir(outDir); + eprintln("Running MDA for " ++ spec.fullName ++ "."); + ret::Integer <- copper:compileParserBean(specCstAst.copperParser, + makeName(spec.sourceGrammar), parserName, true, "", false, "", false); + case nativeSerialize(new(specCstAst)) of + | left(e) -> error("BUG: specCstAst was not serializable; hopefully this was caused by the most recent change to the copper_mda modification: " ++ e) + | right(dump) -> writeBinaryFile(dumpFile, dump) + end; + return ret; + } else do { + -- Should this be stderr? + eprintln("CST errors while preparing for MDA " ++ spec.fullName ++ ":\n" ++ + implode("\n", specCstAst.cstErrors)); + return 1; + }; + + top.run = do { + dumpFileExists :: Boolean <- isFile(dumpFile); + if dumpFileExists then do { + dumpFileContents::ByteArray <- readBinaryFile(dumpFile); + let dumpMatched::Either = map(eq(specCstAst, _), nativeDeserialize(dumpFileContents)); + if dumpMatched == right(true) then do { + eprintln("MDA test " ++ spec.fullName ++ " is up to date."); + return 0; + } else do { + buildGrammar; + }; + } else do { + buildGrammar; + }; + }; + + top.order = 5; +} diff --git a/grammars/silver/compiler/modification/copper_mda/Root.sv b/grammars/silver/compiler/modification/copper_mda/Root.sv new file mode 100644 index 000000000..f0ed51957 --- /dev/null +++ b/grammars/silver/compiler/modification/copper_mda/Root.sv @@ -0,0 +1,65 @@ +grammar silver:compiler:modification:copper_mda; + +import silver:compiler:driver:util; + +synthesized attribute mdaSpecs :: [MdaSpec] occurs on Root, AGDcls, AGDcl, RootSpec, Grammar; + +flowtype mdaSpecs {decorate} on Root, AGDcls, AGDcl, RootSpec, Grammar; + +aspect production root +top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls +{ + top.mdaSpecs = ags.mdaSpecs; +} + +aspect production nilAGDcls +top::AGDcls ::= +{ + top.mdaSpecs = []; +} +aspect production consAGDcls +top::AGDcls ::= h::AGDcl t::AGDcls +{ + top.mdaSpecs = h.mdaSpecs ++ t.mdaSpecs; +} + +aspect default production +top::AGDcl ::= +{ + top.mdaSpecs = []; +} +aspect production appendAGDcl +top::AGDcl ::= ag1::AGDcl ag2::AGDcl +{ + top.mdaSpecs = ag1.mdaSpecs ++ ag2.mdaSpecs; +} + +aspect production grammarRootSpec +top::RootSpec ::= g::Grammar _ _ _ _ _ +{ + top.mdaSpecs = g.mdaSpecs; +} +aspect production interfaceRootSpec +top::RootSpec ::= _ _ +{ + top.mdaSpecs = []; -- TODO +} +aspect production errorRootSpec +top::RootSpec ::= _ _ _ _ _ +{ + top.mdaSpecs = []; +} + + +aspect production nilGrammar +top::Grammar ::= +{ + top.mdaSpecs = []; +} + +aspect production consGrammar +top::Grammar ::= h::Root t::Grammar +{ + top.mdaSpecs = h.mdaSpecs ++ t.mdaSpecs; +} + diff --git a/grammars/silver/compiler/modification/copper_mda/Syntax.sv b/grammars/silver/compiler/modification/copper_mda/Syntax.sv new file mode 100644 index 000000000..be64aad9b --- /dev/null +++ b/grammars/silver/compiler/modification/copper_mda/Syntax.sv @@ -0,0 +1,42 @@ +grammar silver:compiler:modification:copper_mda; + +synthesized attribute markingTokens :: [Decorated SyntaxDcl]; +synthesized attribute bridgeProductions :: [Decorated SyntaxDcl]; + +attribute markingTokens, bridgeProductions occurs on Syntax, SyntaxDcl; + +aspect production nilSyntax +top::Syntax ::= +{ + top.markingTokens = []; + top.bridgeProductions = []; +} +aspect production consSyntax +top::Syntax ::= s1::SyntaxDcl s2::Syntax +{ + top.markingTokens = s1.markingTokens ++ s2.markingTokens; + top.bridgeProductions = s1.bridgeProductions ++ s2.bridgeProductions; +} + +aspect default production +top::SyntaxDcl ::= +{ + top.markingTokens = []; + top.bridgeProductions = []; +} + +aspect production syntaxTerminal +top::SyntaxDcl ::= n::String _ modifiers::SyntaxTerminalModifiers +{ + top.markingTokens = if modifiers.marking then [top] else []; +} + +aspect production syntaxProduction +top::SyntaxDcl ::= ns::NamedSignature modifiers::SyntaxProductionModifiers +{ + top.bridgeProductions = + if !null(lhsRef) && top.containingGrammar != head(lhsRef).containingGrammar + then [top] + else []; +} + diff --git a/grammars/silver/compiler/modification/copper_mda/SyntaxMdaRoot.sv b/grammars/silver/compiler/modification/copper_mda/SyntaxMdaRoot.sv new file mode 100644 index 000000000..252cf3b58 --- /dev/null +++ b/grammars/silver/compiler/modification/copper_mda/SyntaxMdaRoot.sv @@ -0,0 +1,100 @@ +grammar silver:compiler:modification:copper_mda; + +import silver:compiler:definition:concrete_syntax:copper as copper; +import silver:util:graph as g; +import silver:util:treemap as tm; + +abstract production cstCopperMdaRoot +top::SyntaxRoot ::= parsername::String startnt::String host::Syntax ext::Syntax customStartLayout::Maybe<[String]> +{ + propagate compareTo, isEqual, allNonterminals, allTerminals; + + -- Because there may be references between the grammars, we cannot do the + -- usual normalization. + + -- TODO: we could consider making host host-only, and ext have both... + host.cstEnv = directBuildTree(host.cstDcls ++ ext.cstDcls); + host.containingGrammar = "host"; + host.cstNTProds = error("TODO: this should only be used by normalize"); -- TODO + host.classTerminals = directBuildTree(host.classTerminalContribs ++ ext.classTerminalContribs); + host.superClasses = + directBuildTree( + g:toList( + g:transitiveClosure( + g:add( + host.superClassContribs ++ ext.superClassContribs, + g:empty())))); + host.subClasses = + directBuildTree( + g:toList( + g:transitiveClosure( + g:add( + map( + \ p::Pair -> pair(p.snd, p.fst), + host.superClassContribs ++ ext.superClassContribs), + g:empty())))); + host.parserAttributeAspects = + directBuildTree(host.parserAttributeAspectContribs ++ ext.parserAttributeAspectContribs); + host.layoutTerms = + -- ext shouldn't affect host layout, but include both so we only have to build this once + buildLayoutEnv( + map((.fullName), host.allTerminals ++ ext.allTerminals), + map((.fullName), host.allProductions ++ ext.allProductions ++ host.allNonterminals ++ ext.allNonterminals), + host.layoutContribs ++ ext.layoutContribs); + host.prefixesForTerminals = directBuildTree([]); + host.componentGrammarMarkingTerminals = directBuildTree([]); + + ext.cstEnv = host.cstEnv; + ext.containingGrammar = "ext"; + ext.cstNTProds = error("TODO: this should only be used by normalize"); -- TODO + ext.classTerminals = host.classTerminals; + ext.superClasses = host.superClasses; + ext.subClasses = host.subClasses; + ext.parserAttributeAspects = host.parserAttributeAspects; + ext.layoutTerms = host.layoutTerms; + ext.prefixesForTerminals = host.prefixesForTerminals; + ext.componentGrammarMarkingTerminals = host.componentGrammarMarkingTerminals; + + local prettyNames::tm:Map = + tm:add(host.prettyNamesAccum, tm:add(ext.prettyNamesAccum, tm:empty())); + host.prettyNames = prettyNames; + ext.prettyNames = prettyNames; + + local startFound :: [Decorated SyntaxDcl] = searchEnvTree(startnt, host.cstEnv); + + top.cstErrors := host.cstErrors ++ ext.cstErrors; + top.cstErrors <- if !null(startFound) then [] + else ["Nonterminal " ++ startnt ++ " was referenced but " ++ + "this grammar was not included in this parser. (Referenced as parser's starting nonterminal)"]; + + top.dominatingTerminals = error("Shouldn't be demanded from cstCopperMdaRoot"); + + -- The layout before and after the root nonterminal. By default, the layout of the root nonterminal. + local startLayout::[copper:ElementReference] = + map((.copperElementReference), + map(head, + lookupStrings( + fromMaybe(searchEnvTree(startnt, host.layoutTerms), customStartLayout), + host.cstEnv))); + + local hostGrammar::copper:Grammar = + copper:grammar_(top.sourceGrammar, top.location, host.containingGrammar, + host.copperGrammarElements); + + -- All disambiguation classes go in the extension grammar for now, since they + -- reference extension terminals. + local extGrammarElements::[copper:GrammarElement] = ext.copperGrammarElements + ++ flatMap((.copperGrammarElements), host.disambiguationClasses) + ++ flatMap((.copperGrammarElements), ext.disambiguationClasses); + local extGrammar::copper:Grammar = + copper:extensionGrammar(top.sourceGrammar, top.location, + ext.containingGrammar, extGrammarElements, + map((.copperElementReference), ext.markingTokens), + map((.copperElementReference), ext.bridgeProductions), + map((.copperElementReference), host.disambiguationClasses)); + + top.copperParser = copper:extendedParserBean(top.sourceGrammar, top.location, + makeCopperName(parsername), parsername, + head(startFound).copperElementReference, startLayout, [], "", "", "", + hostGrammar, extGrammar); +} diff --git a/grammars/silver/compiler/modification/defaultattr/DefaultAttr.sv b/grammars/silver/compiler/modification/defaultattr/DefaultAttr.sv new file mode 100644 index 000000000..da2fe8f62 --- /dev/null +++ b/grammars/silver/compiler/modification/defaultattr/DefaultAttr.sv @@ -0,0 +1,128 @@ +grammar silver:compiler:modification:defaultattr; + +import silver:compiler:definition:core; +import silver:compiler:definition:origins; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; +--import silver:compiler:analysis:typechecking:core; +import silver:compiler:translation:java; + +import silver:compiler:definition:flow:driver only ProductionGraph, FlowType, constructDefaultProductionGraph; -- for the "oh no again!" hack below +import silver:compiler:driver:util only RootSpec; -- ditto + +terminal Default_kwd 'default' lexer classes {KEYWORD, RESERVED}; + +concrete production aspectDefaultProduction +top::AGDcl ::= 'aspect' 'default' 'production' ns::AspectDefaultProductionSignature body::ProductionBody +{ + top.unparse = "aspect default production\n" ++ ns.unparse ++ "\n" ++ body.unparse; + + top.defs := []; + + propagate errors, flowDefs; + + local sigDefs :: [Def] = addNewLexicalTyVars(top.grammarName, top.location, ns.lexicalTyVarKinds, ns.lexicalTypeVariables); + + -- oh no again! + local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; + local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; + + local myFlowGraph :: ProductionGraph = + constructDefaultProductionGraph(ns.namedSignature, body.flowDefs, top.env, myProds, myFlow); + + ns.env = newScopeEnv(sigDefs, top.env); + + body.env = newScopeEnv(ns.defs, ns.env); + body.frame = defaultAspectContext(ns.namedSignature, myFlowGraph, sourceGrammar=top.grammarName); + + body.downSubst = emptySubst(); + + top.setupInh := body.setupInh; -- Probably should be empty? + top.initProd := "\t\t//ASPECT DEFAULT PRODUCTION for " ++ ns.namedSignature.outputElement.typerep.typeName ++ "\n" ++ body.translation; + top.valueWeaving := body.valueWeaving; -- Probably should be empty? +} action { + sigNames = []; +} + +nonterminal AspectDefaultProductionSignature with config, grammarName, env, flowEnv, location, unparse, errors, defs, namedSignature, lexicalTypeVariables, lexicalTyVarKinds; + +concrete production aspectDefaultProductionSignature +top::AspectDefaultProductionSignature ::= lhs::Name '::' te::TypeExpr '::=' +{ + top.unparse = lhs.unparse ++ "::" ++ te.unparse ++ " ::="; + top.defs := [defaultLhsDef(top.grammarName, lhs.location, lhs.name, te.typerep)]; + top.namedSignature = + namedSignature(top.grammarName ++ ":default" ++ te.typerep.typeName, + nilContext(), nilNamedSignatureElement(), + namedSignatureElement(lhs.name, te.typerep), + foldNamedSignatureElements(annotationsForNonterminal(te.typerep, top.env))); + + propagate errors, lexicalTypeVariables, lexicalTyVarKinds; + + local checkNT::TypeCheck = checkNonterminal(top.env, false, te.typerep); + checkNT.downSubst = emptySubst(); + checkNT.finalSubst = emptySubst(); + + top.errors <- + if checkNT.typeerror + then [err(top.location, "Default production LHS type must be a nonterminal. Instead it is of type " ++ checkNT.leftpp)] + else []; + + top.errors <- te.errorsKindStar; + top.errors <- + case te of + -- LHS must be either NT or NT where a b ... are all ty vars + | appTypeExpr(_, tl) -> tl.errorsTyVars + | _ -> [] + end; +} action { + insert semantic token IdSigNameDcl_t at lhs.location; + sigNames = [lhs.name]; +} + +function defaultLhsDef +Def ::= sg::String sl::Location fn::String ty::Type +{ + return valueDef(defaultEnvItem(defaultLhsDcl(fn,ty,sourceGrammar=sg,sourceLocation=sl))); +} +abstract production defaultLhsDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + top.refDispatcher = lhsReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: be smarter about the error message + top.defLHSDispatcher = defaultLhsDefLHS(_, location=_); +} + +abstract production defaultLhsDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.name = q.name; + top.unparse = q.unparse; + top.found = !existingProblems && top.defLHSattr.attrDcl.isSynthesized; + + local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; + + top.errors := + if existingProblems || top.found then [] + else [err(q.location, "Cannot define inherited attribute '" ++ top.defLHSattr.name ++ "' on the lhs '" ++ q.name ++ "'")]; + + top.typerep = q.lookupValue.typeScheme.monoType; + + top.translation = makeNTName(top.frame.lhsNtName) ++ ".defaultSynthesizedAttributes"; +} + +abstract production defaultAspectContext +top::BlockContext ::= sig::NamedSignature g::ProductionGraph +{ + top.fullName = sig.fullName; + top.lhsNtName = sig.outputElement.typerep.typeName; + top.signature = sig; + top.flowGraph = g; + top.originsContextSource = useContextLhsAndRules(); +} diff --git a/grammars/silver/compiler/modification/ffi/FunctionDcl.sv b/grammars/silver/compiler/modification/ffi/FunctionDcl.sv new file mode 100644 index 000000000..631c42b11 --- /dev/null +++ b/grammars/silver/compiler/modification/ffi/FunctionDcl.sv @@ -0,0 +1,60 @@ +grammar silver:compiler:modification:ffi; + +terminal FFI_kwd 'foreign' lexer classes {KEYWORD,RESERVED}; + +nonterminal FFIDefs with config, location, grammarName, errors, env, unparse; +nonterminal FFIDef with config, location, grammarName, errors, env, unparse; + +concrete production functionDclFFI +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody 'foreign' '{' ffidefs::FFIDefs '}' +{ + top.unparse = "function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse ++ " foreign {\n" ++ ffidefs.unparse ++ "}"; + + production fName :: String = top.grammarName ++ ":" ++ id.name; + production namedSig :: NamedSignature = ns.namedSignature; + + top.errors <- ffidefs.errors; + + -- Quick copy & paste to make signatures look right. Otherwise they contain errorTypes for + -- type parameters + production attribute sigDefs :: [Def] with ++; + sigDefs := ns.defs; + ns.signatureName = fName; + ns.env = newScopeEnv(sigDefs, top.env); + production attribute allLexicalTyVars :: [String]; + allLexicalTyVars = nub(ns.lexicalTypeVariables); + sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, ns.lexicalTyVarKinds, allLexicalTyVars); + + -- TODO this is a BS use of forwarding and should be eliminated. body.env and .frame are all wrong locally... + + forwards to functionDcl($1, id, ns, body, location=top.location); +} action { + insert semantic token IdFnProdDcl_t at id.location; + sigNames = []; +} + +concrete production ffidefsOne +top::FFIDefs ::= one::FFIDef +{ + top.unparse = one.unparse; + + top.errors := one.errors; +} + +concrete production ffidefsMany +top::FFIDefs ::= one::FFIDef more::FFIDefs +{ + top.unparse = one.unparse ++ more.unparse; + + top.errors := one.errors ++ more.errors; +} + +concrete production ffidef +top::FFIDef ::= name::String_t ':' 'return' code::String_t ';' +{ + top.unparse = name.lexeme ++ ": return " ++ code.lexeme ++ ";\n"; + + top.errors := []; + -- Up to each translation to do something with this. +} + diff --git a/grammars/silver/compiler/modification/ffi/Project.sv b/grammars/silver/compiler/modification/ffi/Project.sv new file mode 100644 index 000000000..984e19b3d --- /dev/null +++ b/grammars/silver/compiler/modification/ffi/Project.sv @@ -0,0 +1,6 @@ +grammar silver:compiler:modification:ffi; + +exports silver:compiler:modification:ffi:java with silver:compiler:translation:java:core; + +exports silver:compiler:modification:ffi:java with silver:compiler:translation:java:type; + diff --git a/grammars/silver/compiler/modification/ffi/Type.sv b/grammars/silver/compiler/modification/ffi/Type.sv new file mode 100644 index 000000000..91bfb5a15 --- /dev/null +++ b/grammars/silver/compiler/modification/ffi/Type.sv @@ -0,0 +1,30 @@ +grammar silver:compiler:modification:ffi; + +abstract production foreignType +top::Type ::= fn::String transType::String params::[Type] +{ + top.typeName = fn; + top.freeVariables = setUnionTyVarsAll(map((.freeVariables), params)); + top.freeSkolemVars := setUnionTyVarsAll(map((.freeSkolemVars), params)); + top.freeFlexibleVars := setUnionTyVarsAll(map((.freeFlexibleVars), params)); + top.substituted = foreignType(fn, transType, mapSubst(params, top.substitution)); + top.flatRenamed = foreignType(fn, transType, mapRenameSubst(params, top.substitution)); + top.typepp = fn ++ if !null(params) then "<" ++ implode(" ", map(prettyTypeWith(_, top.boundVariables), params)) ++ ">" else ""; + top.kindrep = starKind(); + top.isTypeable = false; -- TODO: May want to add extra syntax to indicate that a foreign type's translation is an instance of Typed + + -- Unification.sv + top.unify = + case top.unifyWith of + | foreignType(ofn, ott, op) -> + if fn == ofn && transType == ott + then unifyAll( params, op ) + else errorSubst("Tried to unify conflicting foreign types " ++ fn ++ " and " ++ ofn) + | _ -> errorSubst("Tried to unify foreign type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) + end; +} + +-- What we get from the standard library's declaration, so we don't need to repeat it +global ioForeignType :: Type = + foreignType("silver:core:IOToken", "common.IOToken", []); + diff --git a/grammars/silver/modification/ffi/TypeDcl.sv b/grammars/silver/compiler/modification/ffi/TypeDcl.sv similarity index 83% rename from grammars/silver/modification/ffi/TypeDcl.sv rename to grammars/silver/compiler/modification/ffi/TypeDcl.sv index 72a1761ce..909c0f080 100644 --- a/grammars/silver/modification/ffi/TypeDcl.sv +++ b/grammars/silver/compiler/modification/ffi/TypeDcl.sv @@ -1,12 +1,9 @@ -grammar silver:modification:ffi; +grammar silver:compiler:modification:ffi; -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:util; - -imports silver:modification:typedecl; +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; -- Yikes, this was a weird choice of syntax. concrete production ffiTypeDclLegacy @@ -15,6 +12,8 @@ top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs 'foreign' ';' local obj :: String_t = terminal(String_t, "\"Object\""); --forwards to ffiTypeDcl('foreign', 'type', id, tl, '=', obj, ';', location=top.location); forwards to ffiTypeDclUgly('type', id, tl, 'foreign', '=', obj, ';', location=top.location); +} action { + insert semantic token IdTypeDcl_t at id.location; } @@ -41,7 +40,7 @@ top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs 'foreign' '=' trans::St -- Strip quotes local transType :: String = substring(1, length(trans.lexeme) - 1, trans.lexeme); - top.defs := [typeAliasDef(top.grammarName, id.location, fName, tl.freeVariables, foreignType(fName, transType, tl.types))]; + top.defs := [typeAliasDef(top.grammarName, id.location, fName, [], tl.freeVariables, foreignType(fName, transType, tl.types))]; propagate errors, flowDefs; top.errors <- tl.errorsTyVars; @@ -60,5 +59,7 @@ top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs 'foreign' '=' trans::St if isLower(substring(0, 1, id.name)) then [err(id.location, "Types must be capitalized. Invalid foreign type name " ++ id.name)] else []; +} action { + insert semantic token IdTypeDcl_t at id.location; } diff --git a/grammars/silver/compiler/modification/ffi/java/FunctionDcl.sv b/grammars/silver/compiler/modification/ffi/java/FunctionDcl.sv new file mode 100644 index 000000000..bda7381c7 --- /dev/null +++ b/grammars/silver/compiler/modification/ffi/java/FunctionDcl.sv @@ -0,0 +1,74 @@ +grammar silver:compiler:modification:ffi:java; + +import silver:compiler:modification:ffi; +import silver:compiler:modification:ffi:util; +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; + +synthesized attribute ffiTranslationString :: [String] occurs on FFIDef, FFIDefs; + +aspect production ffidefsOne +top::FFIDefs ::= one::FFIDef +{ + top.ffiTranslationString = one.ffiTranslationString; +} + +aspect production ffidefsMany +top::FFIDefs ::= one::FFIDef more::FFIDefs +{ + top.ffiTranslationString = one.ffiTranslationString ++ more.ffiTranslationString; +} + +aspect production ffidef +top::FFIDef ::= name::String_t ':' 'return' code::String_t ';' +{ + top.ffiTranslationString = if name.lexeme == "\"java\"" then [cleanStringLexeme(code.lexeme)] else []; +} + +function strictChildAccessor +String ::= ns::NamedSignatureElement +{ + -- TODO: Slight bug here, if we refer to an argument more than once in an FFI string, + -- then we may evaluate argument thunks more than once! + -- E.g. "java": "Foo.bar(%x%, %x%)" + return "common.Util.<" ++ ns.typerep.transType ++ ">demand(" ++ ns.childRefElem ++ ")"; +} + +function computeSigTranslation +String ::= str::String sig::NamedSignature +{ + return substituteAll(str, + map(wrapStrictNotation, sig.inputNames) ++ map(wrapLazyNotation, sig.inputNames) ++ map(wrapContextNotation, range(0, length(sig.contexts))), + map(strictChildAccessor, sig.inputElements) ++ map((.childRefElem), sig.inputElements) ++ map(\ c::Context -> decorate c with {boundVariables = sig.freeVariables;}.contextRefElem, sig.contexts)); +} + + +-- TODO: this needs clean up. +-- We should forward either to normal function declaration or special one, rather than IFing EVERYTHING in here! +aspect production functionDclFFI +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody 'foreign' '{' ffidefs::FFIDefs '}' +{ + top.setupInh := if null(ffidefs.ffiTranslationString) + then forward.setupInh + else ""; -- hacky hacky! -- TODO should these be there, or empty? + top.initProd := if null(ffidefs.ffiTranslationString) + then forward.initProd + else ""; -- hacky hacky! -- TODO should these be there, or empty? + top.initValues := ""; + top.postInit := ""; + + top.genFiles := if null(ffidefs.ffiTranslationString) + then forward.genFiles + else + [pair("P" ++ id.name ++ ".java", + generateFunctionClassString(top.grammarName, id.name, namedSig, "return (" ++ namedSig.outputElement.typerep.transClassType ++ ")" ++ computeSigTranslation(head(ffidefs.ffiTranslationString), namedSig) ++ ";\n") + )]; + + top.errors <- if length(ffidefs.ffiTranslationString) > 1 + then [err(ffidefs.location, "There is more than one Java translation in this FFI function")] + else []; +} + diff --git a/grammars/silver/compiler/modification/ffi/java/Type.sv b/grammars/silver/compiler/modification/ffi/java/Type.sv new file mode 100644 index 000000000..91acaecb3 --- /dev/null +++ b/grammars/silver/compiler/modification/ffi/java/Type.sv @@ -0,0 +1,20 @@ +grammar silver:compiler:modification:ffi:java; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:translation:java:type; +import silver:compiler:modification:ffi; + +aspect production foreignType +top::Type ::= fn::String transType::String params::[Type] +{ + top.transType = transType; + top.transClassType = transType; + top.transTypeRep = + foldl( + \ c::String a::Type -> s"new common.AppTypeRep(${c}, ${transTypeRepWith(a, top.skolemTypeReps)})", + s"new common.BaseTypeRep(\"${fn}\")", params); + top.transTypeName = implode("_", substitute(":", "_", fn) :: map(transTypeNameWith(_, top.boundVariables), params)); +} + diff --git a/grammars/silver/modification/ffi/util/FFIUtil.sv b/grammars/silver/compiler/modification/ffi/util/FFIUtil.sv similarity index 76% rename from grammars/silver/modification/ffi/util/FFIUtil.sv rename to grammars/silver/compiler/modification/ffi/util/FFIUtil.sv index d031e0e76..18cf113c8 100644 --- a/grammars/silver/modification/ffi/util/FFIUtil.sv +++ b/grammars/silver/compiler/modification/ffi/util/FFIUtil.sv @@ -1,10 +1,9 @@ -grammar silver:modification:ffi:util; +grammar silver:compiler:modification:ffi:util; -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:modification:ffi; -import silver:util; +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:modification:ffi; function substituteAll String ::= s::String names::[String] results::[String] @@ -23,6 +22,11 @@ String ::= s::String { return "%?" ++ s ++ "?%"; } +function wrapContextNotation +String ::= i::Integer +{ + return "%@" ++ toString(i) ++ "@%"; +} function cleanStringLexeme String ::= s::String diff --git a/grammars/silver/compiler/modification/lambda_fn/DclInfo.sv b/grammars/silver/compiler/modification/lambda_fn/DclInfo.sv new file mode 100644 index 000000000..96b2c1fdb --- /dev/null +++ b/grammars/silver/compiler/modification/lambda_fn/DclInfo.sv @@ -0,0 +1,34 @@ +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; + +synthesized attribute lambdaId::Integer occurs on ValueDclInfo; +synthesized attribute lambdaParamIndex::Integer occurs on ValueDclInfo; + +aspect default production +top::ValueDclInfo ::= +{ + top.lambdaId = error("Should only be demanded on lambda params"); + top.lambdaParamIndex = error("Should only be demanded on lambda params"); +} + + +abstract production lambdaParamDcl +top::ValueDclInfo ::= fn::String ty::Type id::Integer paramIndex::Integer +{ + top.fullName = fn; + propagate isEqual; + + top.typeScheme = monoType(ty); + + top.lambdaParamIndex = paramIndex; + top.lambdaId = id; + + top.refDispatcher = lambdaParamReference(_, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- should be impossible (never in scope at production level?) + top.defLHSDispatcher = errorDefLHS(_, location=_); -- ditto +} + +function lambdaParamDef +Def ::= sg::String sl::Location fn::String ty::Type id::Integer paramIndex::Integer +{ + return valueDef(defaultEnvItem(lambdaParamDcl(fn,ty,id,paramIndex,sourceGrammar=sg,sourceLocation=sl))); +} diff --git a/grammars/silver/compiler/modification/lambda_fn/Lambda.sv b/grammars/silver/compiler/modification/lambda_fn/Lambda.sv new file mode 100644 index 000000000..4105e4f6d --- /dev/null +++ b/grammars/silver/compiler/modification/lambda_fn/Lambda.sv @@ -0,0 +1,91 @@ +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; +import silver:compiler:definition:env; +import silver:util:treeset as ts; + +--- Concrete Syntax for lambdas +-------------------------------------------------------------------------------- + +terminal Lambda_kwd '\' lexer classes {KEYWORD,RESERVED}; +terminal Arrow_t '->' precedence = 0, lexer classes {SPECOP}; + +-- Using ProductionRHS here, it is basicly just a list of names with type expressions +-- It is also used for the parameter definitions in functions, so using it here for consistancy +concrete production lambda_c +top::Expr ::= '\' params::ProductionRHS '->' e::Expr +{ + top.unparse = "\\ " ++ params.unparse ++ " -> " ++ e.unparse; + + forwards to lambdap(params, e, location=top.location); +} + +abstract production lambdap +top::Expr ::= params::ProductionRHS e::Expr +{ + top.unparse = "\\ " ++ params.unparse ++ " -> " ++ e.unparse; + top.freeVars := ts:removeAll(params.lambdaBoundVars, e.freeVars); + + propagate errors; + + top.typerep = appTypes(functionType(length(params.inputElements), []), map((.typerep), params.inputElements) ++ [e.typerep]); + + production attribute sigDefs::[Def] with ++; + sigDefs := params.lambdaDefs; + sigDefs <- + addNewLexicalTyVars_ActuallyVariables( + top.grammarName, top.location, params.lexicalTyVarKinds, + filter(\ tv::String -> null(getTypeDcl(tv, top.env)), nub(params.lexicalTypeVariables))); + + propagate downSubst, upSubst; + propagate flowDeps, flowDefs; + + params.env = newScopeEnv(sigDefs, top.env); + params.givenLambdaParamIndex = 0; + params.givenLambdaId = genInt(); + e.env = params.env; + e.frame = inLambdaContext(top.frame, sourceGrammar=top.frame.sourceGrammar); --TODO: Is this sourceGrammar correct? +} + +monoid attribute lambdaDefs::[Def]; +monoid attribute lambdaBoundVars::[String]; +attribute lambdaDefs, lambdaBoundVars occurs on ProductionRHS, ProductionRHSElem; + +flowtype lambdaDefs {decorate, givenLambdaId, givenLambdaParamIndex} on ProductionRHS, ProductionRHSElem; +flowtype lambdaBoundVars {} on ProductionRHS; +flowtype lambdaBoundVars {deterministicCount} on ProductionRHSElem; + +propagate lambdaDefs, lambdaBoundVars on ProductionRHS; + +inherited attribute givenLambdaId::Integer occurs on ProductionRHS, ProductionRHSElem; +inherited attribute givenLambdaParamIndex::Integer occurs on ProductionRHS, ProductionRHSElem; +propagate givenLambdaId on ProductionRHS, ProductionRHSElem; + +aspect production productionRHSCons +top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS +{ + t.givenLambdaParamIndex = top.givenLambdaParamIndex + 1; + h.givenLambdaParamIndex = top.givenLambdaParamIndex; +} + +aspect production productionRHSElem +top::ProductionRHSElem ::= id::Name '::' t::TypeExpr +{ + production fName :: String = toString(genInt()) ++ ":" ++ id.name; +-- production transName :: String = "lambda_param" ++ id.name ++ toString(genInt()); + top.lambdaDefs := [lambdaParamDef(top.grammarName, t.location, fName, t.typerep, top.givenLambdaId, top.givenLambdaParamIndex)]; + top.lambdaBoundVars := [id.name]; +} + +abstract production lambdaParamReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.unparse = q.unparse; + propagate errors; + top.freeVars := ts:fromList([q.name]); + + top.typerep = q.lookupValue.typeScheme.monoType; + + propagate downSubst, upSubst; + + -- TODO? + propagate flowDeps, flowDefs; +} diff --git a/grammars/silver/compiler/modification/lambda_fn/Project.sv b/grammars/silver/compiler/modification/lambda_fn/Project.sv new file mode 100644 index 000000000..480e0b36c --- /dev/null +++ b/grammars/silver/compiler/modification/lambda_fn/Project.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:modification:lambda_fn; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +--imports silver:compiler:analysis:typechecking:core; + +exports silver:compiler:modification:lambda_fn:java with silver:compiler:translation:java:core; +exports silver:compiler:modification:lambda_fn:java with silver:compiler:translation:java:type; + diff --git a/grammars/silver/compiler/modification/lambda_fn/java/Lambda.sv b/grammars/silver/compiler/modification/lambda_fn/java/Lambda.sv new file mode 100644 index 000000000..3469f492a --- /dev/null +++ b/grammars/silver/compiler/modification/lambda_fn/java/Lambda.sv @@ -0,0 +1,61 @@ +grammar silver:compiler:modification:lambda_fn:java; + +import silver:compiler:modification:lambda_fn; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; + +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; + +aspect production lambdap +top::Expr ::= params::ProductionRHS e::Expr +{ + local finTy :: Type = finalType(top); + + -- Attempt to solve a context `runtimeTypeable ${finType}`, from which the runtime TypeRep translation is computed. + -- If the type somehow contains a skolem (e.g. through scoped type variables), + -- then we will attempt to use the more specific runtime TypeRep from the context, + -- but will otherwise fall back to rigid skolem constant TypeReps. + local context :: Context = typeableContext(finTy); + context.env = top.env; + + local lambdaTrans :: (String ::= String) = \ runtimeTypeTrans::String -> +s"""(new common.NodeFactory<${finTy.outputType.transType}>() { + @Override + public final ${finTy.outputType.transType} invoke(final common.OriginContext originCtx, final Object[] lambda_${toString(params.givenLambdaId)}_args, final Object[] namedArgs) { + return ${e.translation}; + } + + @Override + public final common.TypeRep getType() { +${makeTyVarDecls(5, finTy.freeVariables)} + return ${runtimeTypeTrans}; + } + + @Override + public final String toString() { + return "lambda at ${top.grammarName}:${top.location.unparse}"; + } + })"""; + + top.translation = lambdaTrans(context.transTypeableContext); + top.lazyTranslation = top.translation; + + -- Same as above, except that we know that this is a top-level generalized binding + -- (e.g. global id :: (a ::= a) = \ x::a -> x;) + -- so freshen all skolem type vars in the run-time type representation. + top.generalizedTranslation = lambdaTrans(transFreshTypeRep(finTy)); +} + +aspect production lambdaParamReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.translation = s"common.Util.<${finalType(top).transType}>demandIndex(lambda_${toString(q.lookupValue.dcl.lambdaId)}_args, ${toString(q.lookupValue.dcl.lambdaParamIndex)})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + diff --git a/grammars/silver/compiler/modification/let_fix/DclInfo.sv b/grammars/silver/compiler/modification/let_fix/DclInfo.sv new file mode 100644 index 000000000..2330afffd --- /dev/null +++ b/grammars/silver/compiler/modification/let_fix/DclInfo.sv @@ -0,0 +1,28 @@ +grammar silver:compiler:modification:let_fix; + +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; + +abstract production lexicalLocalDcl +top::ValueDclInfo ::= fn::String ty::Type fi::ExprVertexInfo fd::[FlowVertex] +{ + top.fullName = fn; + top.isEqual = + -- Should never show up in an interface file anyway... + case top.compareTo of + | lexicalLocalDcl(fn2, ty2, _, _) -> fn == fn2 && ty == ty2 + | _ -> false + end; + + top.typeScheme = monoType(ty); + + top.refDispatcher = lexicalLocalReference(_, fi, fd, location=_); + top.defDispatcher = errorValueDef(_, _, location=_); -- should be impossible (never in scope at production level?) + top.defLHSDispatcher = errorDefLHS(_, location=_); -- ditto +} + +function lexicalLocalDef +Def ::= sg::String sl::Location fn::String ty::Type fi::ExprVertexInfo fd::[FlowVertex] +{ + return valueDef(defaultEnvItem(lexicalLocalDcl(fn,ty,fi,fd,sourceGrammar=sg,sourceLocation=sl))); +} + diff --git a/grammars/silver/compiler/modification/let_fix/Let.sv b/grammars/silver/compiler/modification/let_fix/Let.sv new file mode 100644 index 000000000..725bfdf49 --- /dev/null +++ b/grammars/silver/compiler/modification/let_fix/Let.sv @@ -0,0 +1,150 @@ +grammar silver:compiler:modification:let_fix; + +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; +import silver:util:treeset as ts; + +--- Concrete Syntax for lets +-------------------------------------------------------------------------------- + +terminal Let_kwd 'let' lexer classes {KEYWORD,RESERVED}; +terminal In_kwd 'in' lexer classes {KEYWORD,RESERVED}; + +concrete production letp_c +top::Expr ::= 'let' la::LetAssigns 'in' e::Expr 'end' +{ + top.unparse = "let " ++ la.unparse ++ " in " ++ e.unparse ++ " end"; + + forwards to letp(la.letAssignExprs, e, location=top.location); +} + +nonterminal LetAssigns with unparse, location, letAssignExprs; + +synthesized attribute letAssignExprs :: AssignExpr; + +concrete production assignsListCons +top::LetAssigns ::= ae::AssignExpr ',' list::LetAssigns +{ + top.unparse = ae.unparse ++ ", " ++ list.unparse; + top.letAssignExprs = appendAssignExpr(ae, list.letAssignExprs, location=top.location); +} +concrete production assignListSingle +top::LetAssigns ::= ae::AssignExpr +{ + top.unparse = ae.unparse; + top.letAssignExprs = ae; +} + +-------------------------------------------------------------------------------- +--- Abstract Syntax for lets +-------------------------------------------------------------------------------- + +abstract production letp +top::Expr ::= la::AssignExpr e::Expr +{ + top.unparse = "let " ++ la.unparse ++ " in " ++ e.unparse ++ " end"; + top.freeVars := ts:removeAll(la.boundNames, e.freeVars); + + propagate errors; + + top.typerep = e.typerep; + + propagate downSubst, upSubst; + + -- Semantics for the moment is these are not mutually recursive, + -- so la does NOT get new environment, only e. Thus, la.defs can depend on downSubst... + e.env = newScopeEnv(la.defs, top.env); +} + +monoid attribute boundNames::[String]; + +nonterminal AssignExpr with location, config, grammarName, env, compiledGrammars, + unparse, defs, errors, boundNames, freeVars, upSubst, + downSubst, finalSubst, frame, isRoot, originRules; + +propagate errors, defs on AssignExpr; + +abstract production appendAssignExpr +top::AssignExpr ::= a1::AssignExpr a2::AssignExpr +{ + top.unparse = a1.unparse ++ ", " ++ a2.unparse; + top.freeVars := a1.freeVars ++ ts:removeAll(a1.boundNames, a2.freeVars); + + propagate boundNames, downSubst, upSubst; +} + +-- TODO: Well, okay, so this isn't really abstract syntax... +concrete production assignExpr +top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr +{ + top.unparse = id.unparse ++ " :: " ++ t.unparse ++ " = " ++ e.unparse; + propagate freeVars; + top.boundNames := [id.name]; + + -- Right now some things (pattern matching) abuse us by giving type variables + -- for `t`. So we want to do a little inference before we stuff this into + -- our DclInfo in `defs` because we expect variables in the env to have + -- explicit types. We can't use `finalSubst` here because that requires + -- having completed type inference which requires `defs` which we're defining. + local semiTy :: Type = performSubstitution(t.typerep, top.upSubst); + production fName :: String = toString(genInt()) ++ ":" ++ id.name; + + -- Using finalTy here, so our defs requires we have downSubst... + -- references to this def want to know if its decorated, to enable the + -- auto-undecorate feature, so that's why we bother substituting. + -- (er, except that we're starting with t, which is a Type... must be because we fake these + -- in e.g. the pattern matching code, so type variables might appear there?) + top.defs <- [lexicalLocalDef(top.grammarName, id.location, fName, semiTy, e.flowVertexInfo, e.flowDeps)]; + + -- TODO: At present, this isn't working properly, because the local scope is + -- whatever scope encloses the real local scope... hrmm! + top.errors <- + if length(getValueDclInScope(id.name, top.env)) > 1 + then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] + else []; + + top.errors <- t.errorsKindStar; + + thread downSubst, upSubst on top, e, errCheck1, top; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + + errCheck1 = check(e.typerep, t.typerep); + top.errors <- + if errCheck1.typeerror + then [err(id.location, "Value " ++ id.name ++ " declared with type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] + else []; +} + +abstract production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName fi::ExprVertexInfo fd::[FlowVertex] +{ + top.unparse = q.unparse; + top.errors := []; + top.freeVars := ts:fromList([q.name]); + + -- We're adding the "unusual" behavior that types like "Decorated Foo" in LETs + -- will auto-undecorate if you want a Foo. + + -- (The usual behavior is a declared Foo, but value is Decorated Foo, can + -- be used either way.) + + -- A note about possible unexpected behavior here: if q.lookupValue.typerep + -- is itself a ntOrDecType, which is only possible if for generated 'let' + -- expressions that use a type variable as their type, then this ntOrDecType + -- we're generating here means we're NOT propagating the information about the + -- "actual usage" backwards to expression. + -- i.e. "let x :: a = someLocal in wantsUndecorated(x) end" + -- will mean "let x = decorated version of someLocal in wantsUndecorated(x.undecorate())" + -- and not "let x = undecorated someLocal in wantsUndecorated(x)" + + top.typerep = + case q.lookupValue.typeScheme.monoType of + | ntOrDecType(t, i, _) -> ntOrDecType(t, i, freshType()) + | decoratedType(t, i) -> ntOrDecType(t, i, freshType()) + | partiallyDecoratedType(t, i) -> ntOrDecType(t, i, freshType()) + | t -> t + end; + + propagate downSubst, upSubst; +} + diff --git a/grammars/silver/compiler/modification/let_fix/Project.sv b/grammars/silver/compiler/modification/let_fix/Project.sv new file mode 100644 index 000000000..6303ae671 --- /dev/null +++ b/grammars/silver/compiler/modification/let_fix/Project.sv @@ -0,0 +1,11 @@ +grammar silver:compiler:modification:let_fix; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; +imports silver:compiler:definition:type:syntax; +--imports silver:compiler:analysis:typechecking:core; + +exports silver:compiler:modification:let_fix:java with silver:compiler:translation:java:core; +exports silver:compiler:modification:let_fix:java with silver:compiler:translation:java:type; + diff --git a/grammars/silver/compiler/modification/let_fix/java/Let.sv b/grammars/silver/compiler/modification/let_fix/java/Let.sv new file mode 100644 index 000000000..20a4c275d --- /dev/null +++ b/grammars/silver/compiler/modification/let_fix/java/Let.sv @@ -0,0 +1,87 @@ +grammar silver:compiler:modification:let_fix:java; + +import silver:compiler:modification:let_fix; + +import silver:compiler:definition:core; +import silver:compiler:definition:env; +import silver:compiler:definition:type; +import silver:compiler:definition:type:syntax; + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; + +import silver:compiler:definition:flow:ast only ExprVertexInfo, FlowVertex; + +aspect production letp +top::Expr ::= la::AssignExpr e::Expr +{ + local finTy :: Type = finalType(top); + + -- We need to create these nested locals, so we have no choice but to create a thunk object so we can declare these things. + local closureExpr :: String = + s"new common.Thunk<${finTy.transType}>(new common.Thunk.Evaluable<${finTy.transType}>() { public final ${finTy.transType} eval() { ${la.let_translation} return ${e.translation}; } })"; + --TODO: java lambdas are bugged + --s"new common.Thunk<${finTy.transType}>(() -> { ${la.let_translation} return ${e.translation};\n})"; + + top.translation = s"${closureExpr}.eval()"; + + top.lazyTranslation = + if top.frame.lazyApplication + then closureExpr + else top.translation; +} + +synthesized attribute let_translation :: String occurs on AssignExpr; + +function makeLocalValueName +String ::= s::String +{ + return "__SV_LOCAL_" ++ makeIdName(s); +} + +aspect production appendAssignExpr +top::AssignExpr ::= a1::AssignExpr a2::AssignExpr +{ + top.let_translation = a1.let_translation ++ a2.let_translation; +} + +aspect production assignExpr +top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr +{ + -- We must use `finalSubst` in translation. + -- "let abuse" means type variables can appear in `t`, and if we don't + -- use `finalSubst` we get confusion with `ntOrDecType` not knowing + -- it's being used undecorated later. + local finalTy :: Type = performSubstitution(t.typerep, top.finalSubst); + top.let_translation = makeSpecialLocalBinding(fName, e.translation, finalTy.transType); +} + +function makeSpecialLocalBinding +String ::= fn::String et::String ty::String +{ + return s"final common.Thunk<${ty}> ${makeLocalValueName(fn)} = ${wrapThunkText(et, ty)};\n"; +} + +aspect production lexicalLocalReference +top::Expr ::= q::PartiallyDecorated QName fi::ExprVertexInfo fd::[FlowVertex] +{ + -- To account for a magic case where we generate a let expression with a type + -- that is, for example, a ntOrDecType or something, + -- we do final subst on q.lookupValue ALSO here... + -- it could be isDecorated (ntOrDecType) that later gets specialized to undecorated + -- and therefore we must be careful not to try to undecorate it again! + local needsUndecorating :: Boolean = + performSubstitution(q.lookupValue.typeScheme.monoType, top.finalSubst).isDecorated && !finalType(top).isDecorated; + + top.translation = + if needsUndecorating + then "((" ++ finalType(top).transType ++ ")((common.DecoratedNode)" ++ makeLocalValueName(q.lookupValue.fullName) ++ ".eval()).undecorate())" + else "((" ++ finalType(top).transType ++ ")(" ++ makeLocalValueName(q.lookupValue.fullName) ++ ".eval()))"; + + top.lazyTranslation = + if !top.frame.lazyApplication then top.translation + else if needsUndecorating + then "common.Thunk.transformUndecorate(" ++ makeLocalValueName(q.lookupValue.fullName) ++ ")" + else makeLocalValueName(q.lookupValue.fullName); +} + diff --git a/grammars/silver/compiler/modification/list/List.sv b/grammars/silver/compiler/modification/list/List.sv new file mode 100644 index 000000000..2988fa430 --- /dev/null +++ b/grammars/silver/compiler/modification/list/List.sv @@ -0,0 +1,98 @@ +grammar silver:compiler:modification:list; + +import silver:compiler:definition:type:syntax; + +terminal LSqr_t '[' ; +terminal RSqr_t ']' ; + +-- The TYPE -------------------------------------------------------------------- +concrete production listTypeExpr +top::TypeExpr ::= '[' te::TypeExpr ']' +{ + top.unparse = "[" ++ te.unparse ++ "]"; + + top.typerep = listType(te.typerep); + + top.errorsKindStar := + if top.typerep.kindrep != starKind() + then [err(top.location, s"${top.unparse} has kind ${prettyKind(top.typerep.kindrep)}, but kind * is expected here")] + else []; + + forwards to + appTypeExpr( + listCtrTypeExpr('[', ']', location=top.location), + bTypeList('<', typeListSingle(te, location=te.location), '>', location=top.location), + location=top.location); +} + +concrete production listCtrTypeExpr +top::TypeExpr ::= '[' ']' +{ + top.unparse = "[]"; + + top.typerep = listCtrType(); + + top.errorsKindStar := + if top.typerep.kindrep != starKind() + then [err(top.location, s"${top.unparse} has kind ${prettyKind(top.typerep.kindrep)}, but kind * is expected here")] + else []; + + forwards to typerepTypeExpr(listCtrType(),location=top.location); +} + +-- The expressions ------------------------------------------------------------- + +concrete production emptyList +top::Expr ::= '[' ']' +{ + top.unparse = "[]"; + + forwards to mkStrFunctionInvocation(top.location, "silver:core:nil", []); +} + +-- TODO: BUG: '::' is HasType_t. We probably want to have a different +-- terminal here, with different precedence! + +concrete production consListOp +top::Expr ::= h::Expr '::' t::Expr +{ + top.unparse = "(" ++ h.unparse ++ " :: " ++ t.unparse ++ ")" ; + + forwards to mkStrFunctionInvocation(top.location, "silver:core:cons", [h, t]); +} + +concrete production fullList +top::Expr ::= '[' es::Exprs ']' +{ + top.unparse = "[ " ++ es.unparse ++ " ]"; + + forwards to es.listtrans; +} + +synthesized attribute listtrans :: Expr occurs on Exprs; + +aspect production exprsEmpty +top::Exprs ::= +{ + top.listtrans = emptyList('[',']', location=top.location); +} + +aspect production exprsSingle +top::Exprs ::= e::Expr +{ + top.listtrans = mkStrFunctionInvocation(e.location, "silver:core:cons", [e, emptyList('[',']', location=top.location)]); +} + +aspect production exprsCons +top::Exprs ::= e1::Expr ',' e2::Exprs +{ + top.listtrans = mkStrFunctionInvocation(e1.location, "silver:core:cons", [e1, e2.listtrans]); +} + +-- Overloaded operators -------------------------------------------------------- + +abstract production listPlusPlus +top::Expr ::= e1::PartiallyDecorated Expr e2::PartiallyDecorated Expr +{ + forwards to mkStrFunctionInvocationDecorated(e1.location, "silver:core:append", [e1,e2]); +} diff --git a/grammars/silver/compiler/modification/list/Project.sv b/grammars/silver/compiler/modification/list/Project.sv new file mode 100644 index 000000000..49d5cb826 --- /dev/null +++ b/grammars/silver/compiler/modification/list/Project.sv @@ -0,0 +1,8 @@ +grammar silver:compiler:modification:list; + +imports silver:compiler:definition:type; +imports silver:compiler:definition:env; +imports silver:compiler:definition:core; + +exports silver:compiler:modification:list:java with silver:compiler:translation:java:core; -- special case for [] +exports silver:compiler:modification:list:java:type with silver:compiler:translation:java:type; -- listCtrType translation diff --git a/grammars/silver/compiler/modification/list/Type.sv b/grammars/silver/compiler/modification/list/Type.sv new file mode 100644 index 000000000..980f7124c --- /dev/null +++ b/grammars/silver/compiler/modification/list/Type.sv @@ -0,0 +1,37 @@ +grammar silver:compiler:modification:list; + +{- + listType exists here purely to provide a better pretty print. + -} +abstract production listType +top::Type ::= el::Type +{ + propagate substituted, flatRenamed; + top.typepp = "[" ++ el.typepp ++ "]"; + + forwards to appType(listCtrType(), el); +} + +abstract production listCtrType +top::Type ::= +{ + propagate substituted, flatRenamed; + + top.freeVariables = []; + top.freeSkolemVars := []; + top.freeFlexibleVars := []; + top.typepp = "[]"; + + -- Suppress its "nonterminal"ness + top.isNonterminal = false; + top.isDecorated = false; + + top.tracked = false; + top.kindrep = arrowKind(starKind(),starKind()); + + top.unify = + case top.unifyWith of + | listCtrType() -> emptySubst() + | _ -> errorSubst("Tried to unify List with " ++ prettyType(top.unifyWith)) + end; +} diff --git a/grammars/silver/compiler/modification/list/java/List.sv b/grammars/silver/compiler/modification/list/java/List.sv new file mode 100644 index 000000000..226ff7d69 --- /dev/null +++ b/grammars/silver/compiler/modification/list/java/List.sv @@ -0,0 +1,21 @@ +grammar silver:compiler:modification:list:java; + +import silver:compiler:modification:list; +import silver:compiler:definition:core; +import silver:compiler:translation:java:core; + +-- A wonderous hack +aspect production emptyList +top::Expr ::= '[' ']' +{ + -- Technically, this is interfering, however it's hardly the worst violation + -- in the Silver compiler, and it's not too bad: we do the same thing, just + -- slightly more efficiently. + -- For the time being, this is easier than (and as effective as) amending + -- function calls with special optimization cases. + + -- This makes `[]` act like a constant literal (e.g. a number) instead of + -- a function call. + top.translation = "(common.ConsCell)common.ConsCell.nil"; + top.lazyTranslation = top.translation; +} diff --git a/grammars/silver/compiler/modification/list/java/type/Type.sv b/grammars/silver/compiler/modification/list/java/type/Type.sv new file mode 100644 index 000000000..278a1acff --- /dev/null +++ b/grammars/silver/compiler/modification/list/java/type/Type.sv @@ -0,0 +1,15 @@ +grammar silver:compiler:modification:list:java:type; + +import silver:compiler:modification:list; +import silver:compiler:definition:type; +import silver:compiler:translation:java:type; + +aspect production listCtrType +top::Type ::= +{ + top.transType = "common.ConsCell"; + top.transCovariantType = "common.ConsCell"; + top.transClassType = "common.ConsCell"; + top.transTypeRep = "new common.BaseTypeRep(\"[]\")"; + top.transTypeName = "List"; +} diff --git a/grammars/silver/compiler/modification/primitivepattern/PrimitiveMatch.sv b/grammars/silver/compiler/modification/primitivepattern/PrimitiveMatch.sv new file mode 100644 index 000000000..375914550 --- /dev/null +++ b/grammars/silver/compiler/modification/primitivepattern/PrimitiveMatch.sv @@ -0,0 +1,506 @@ +grammar silver:compiler:modification:primitivepattern; + +imports silver:util:treeset as ts; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +import silver:compiler:definition:type:syntax only typerepType, TypeExpr, errorsKindStar; +import silver:compiler:extension:patternmatching only Arrow_kwd, Vbar_kwd, ensureDecoratedExpr; -- TODO remove + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; + +-- Actually only used for lists, in this file... TODO +import silver:compiler:modification:let_fix only makeSpecialLocalBinding, lexicalLocalDef; +import silver:compiler:definition:flow:ast only noVertex; + +import silver:compiler:modification:list; -- Oh no, this is a hack! TODO + +terminal Match_kwd 'match' lexer classes {KEYWORD,RESERVED}; -- temporary!!! + +nonterminal PrimPatterns with + config, grammarName, env, compiledGrammars, frame, + location, unparse, errors, freeVars, + downSubst, upSubst, finalSubst, + scrutineeType, returnType, translation, isRoot, originRules; +nonterminal PrimPattern with + config, grammarName, env, compiledGrammars, frame, + location, unparse, errors, freeVars, + downSubst, upSubst, finalSubst, + scrutineeType, returnType, translation, isRoot, originRules; + +autocopy attribute scrutineeType :: Type; +autocopy attribute returnType :: Type; + +propagate errors on PrimPatterns, PrimPattern; +propagate freeVars on PrimPatterns, PrimPattern excluding prodPatternNormal, prodPatternGadt, conslstPattern; + +concrete production matchPrimitiveConcrete +top::Expr ::= 'match' e::Expr 'return' t::TypeExpr 'with' pr::PrimPatterns 'else' '->' f::Expr 'end' +{ + top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; + + forwards to matchPrimitive(e, t, pr, f, location=top.location); +} +abstract production matchPrimitive +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; + + propagate freeVars; + + e.downSubst = top.downSubst; + forward.downSubst = e.upSubst; + + forwards to matchPrimitiveReal(ensureDecoratedExpr(e), t, pr, f, location=top.location); +} +{-- + - @param e The value to match against (should be DECORATED if it's nonterminal type at all) + - @param t The RETURN TYPE, explicitly. + - @param pr The cases of this match expression + - @param f The failure expression. (if the patterns don't match, evaluate to this.) + -} +abstract production matchPrimitiveReal +top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr +{ + top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; + + propagate errors, freeVars; + top.typerep = t.typerep; + + top.errors <- t.errorsKindStar; + + {-- + - Invariant: if we were given an undecorated expression, it should have been + - decorated by matchPrimitive before we got here, so we should either + - have a decorated expr, or some other type. + -} + production scrutineeType :: Type = performSubstitution(e.typerep, e.upSubst); + + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + errCheck2 = check(f.typerep, t.typerep); + top.errors <- + if errCheck2.typeerror + then [err(top.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + -- ordinary threading: e, pr, f, errCheck2 + e.downSubst = top.downSubst; + pr.downSubst = e.upSubst; + f.downSubst = pr.upSubst; + errCheck2.downSubst = f.upSubst; + top.upSubst = errCheck2.upSubst; + + pr.scrutineeType = scrutineeType; + pr.returnType = t.typerep; + + local resultTransType :: String = performSubstitution(t.typerep, top.finalSubst).transType; + -- It is necessary to subst on scrutineeType here for the horrible reason that the type we're matching on + -- may not be determined until we get to the constructor list. e.g. 'case error("lol") of pair(x,_) -> x end' + -- which is legal, but if we don't do this will result in java translation errors (as the scrutinee will be + -- type 'a' which is Object, which doesn't have .childAsIs for 'x'.) + local scrutineeFinalType :: Type = performSubstitution(scrutineeType, top.finalSubst); + local scrutineeTransType :: String = scrutineeFinalType.transType; + + top.translation = + "new common.PatternLazy<" ++ scrutineeTransType ++ ", " ++ resultTransType ++ ">() { " ++ + "public final " ++ resultTransType ++ " eval(final common.DecoratedNode context, " ++ scrutineeTransType ++ " scrutineeIter) {" ++ + (if scrutineeFinalType.isDecorated + then + "while(true) {" ++ + "final " ++ scrutineeTransType ++ " scrutinee = scrutineeIter; " ++ -- our Lazy needs a final variable + "final common.Node scrutineeNode = scrutinee.undecorate(); " ++ + pr.translation ++ + "if(!scrutineeIter.undecorate().hasForward()) break;" ++ + "scrutineeIter = scrutineeIter.forward();" ++ + "}" + else + "final " ++ scrutineeTransType ++ " scrutinee = scrutineeIter; " ++ -- ditto + pr.translation) ++ + "return " ++ f.translation ++ ";" ++ + "}}.eval(context, (" ++ scrutineeTransType ++")" ++ e.translation ++ ")"; + + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); + -- TODO there seems to be an opportunity here to avoid an anon class somehow... +} + +concrete production onePattern +top::PrimPatterns ::= p::PrimPattern +{ + top.unparse = p.unparse; + + top.translation = p.translation; + + p.downSubst = top.downSubst; + top.upSubst = p.upSubst; +} +concrete production consPattern +top::PrimPatterns ::= p::PrimPattern '|' ps::PrimPatterns +{ + top.unparse = p.unparse ++ " | " ++ ps.unparse; + + top.translation = p.translation ++ "\nelse " ++ ps.translation; + + p.downSubst = top.downSubst; + ps.downSubst = p.upSubst; + top.upSubst = ps.upSubst; +} + +-- TODO: Long term, I'd like to switch to having a PrimRule and rename PrimPatterns PrimRules. +-- However, we cannot do this yet, because the GADT case does CRAZY things with typing. +-- (Ideally, we'd be able to do those crazy things with constraints added to the +-- context there, instead...) + +concrete production prodPattern +top::PrimPattern ::= qn::QName '(' ns::VarBinders ')' '->' e::Expr +{ + top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; + + top.freeVars := ts:removeAll(ns.boundNames, e.freeVars); + + local t::Type = qn.lookupValue.typeScheme.typerep.outputType; + local isGadt :: Boolean = + case t.baseType of + -- If the lookup is successful, and it's a production type, and it + -- constructs a nonterminal that either: + -- 1. has a non-type-variable parameter (e.g. Expr) + -- 2. has fewer free variables than parameters (e.g. Eq) + -- THEN it's a gadt. + | nonterminalType(_, _, _) -> !isOnlyTyVars(t.argTypes) || length(t.argTypes) != length(setUnionTyVarsAll(map((.freeVariables), t.argTypes))) + | _ -> false + end; + + -- The reason we do it this way is because the threading of type information + -- around is very different, and I don't want to confuse myself while I'm writing + -- the code. After it works, perhaps these can be merged into one non-forwarding + -- production, once the code is understood fully. + forwards to if isGadt + then prodPatternGadt(qn, ns, e, location=top.location) + else prodPatternNormal(qn, ns, e, location=top.location); +} +abstract production prodPatternNormal +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; + + top.freeVars := ts:removeAll(ns.boundNames, e.freeVars); + + local chk :: [Message] = + if null(qn.lookupValue.dcls) || ns.varBinderCount == prod_type.arity then [] + else [err(qn.location, qn.name ++ " has " ++ toString(prod_type.arity) ++ " parameters but " ++ toString(ns.varBinderCount) ++ " patterns were provided")]; + + top.errors <- qn.lookupValue.errors; + top.errors <- + case qn.lookupValue.dcls of + | prodDcl (_, _) :: _ -> [] + | [] -> [] + | _ -> [err(qn.location, qn.name ++ " is not a production.")] + end; + + -- Turns the existential variables existential + local prod_contexts_type :: Pair<[Context] Type> = skolemizeProductionType(qn.lookupValue.typeScheme); + production prod_contexts :: [Context] = prod_contexts_type.fst; + production prod_type :: Type = prod_contexts_type.snd; + -- Note that we're going to check prod_type against top.scrutineeType shortly. + -- This is where the type variables become unified. + + ns.bindingTypes = prod_type.inputTypes; + ns.bindingIndex = 0; + ns.bindingNames = if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.inputNames; + ns.matchingAgainst = if null(qn.lookupValue.dcls) then nothing() else just(qn.lookupValue.dcl); + + -- VarBinders need occurs-on contexts in their env to determine whether var types are decorable + local contextOccursDefs::[OccursDclInfo] = concat( + zipWith( + \ c::Context oc::Context -> + c.contextPatternOccursDefs( + oc, + if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.freeVariables, + scrutineeName, top.location, top.grammarName), + prod_contexts, if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.contexts)); + ns.env = occursEnv(contextOccursDefs, top.env); + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(decoratedType(prod_type.outputType, freshInhSet()), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, qn.name ++ " has type " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + -- Thread NORMALLY! YAY! + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + -- If there are contexts on the production, then we need to make the scrutinee available + -- in the RHS to access their implementations. + local scrutineeName::String = "__scrutineeNode_" ++ toString(genInt()); + local contextDefs::[Def] = concat( + zipWith( + \ c::Context oc::Context -> + performContextSubstitution(c, e.downSubst).contextPatternDefs( + oc, + if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.freeVariables, + scrutineeName, top.location, top.grammarName), + prod_contexts, if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.contexts)); + e.env = newScopeEnv(contextDefs ++ ns.defs, ns.env); + + top.translation = "if(scrutineeNode instanceof " ++ makeProdName(qn.lookupValue.fullName) ++ ") { " ++ + (if null(prod_contexts) then "" else s"final ${makeProdName(qn.lookupValue.fullName)} ${scrutineeName} = (${makeProdName(qn.lookupValue.fullName)})scrutineeNode; ") ++ + ns.translation ++ " return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ e.translation ++ "; }"; +} + +abstract production prodPatternGadt +top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr +{ + top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; + + top.freeVars := ts:removeAll(ns.boundNames, e.freeVars); + + local chk :: [Message] = + if null(qn.lookupValue.dcls) || ns.varBinderCount == prod_type.arity then [] + else [err(qn.location, qn.name ++ " has " ++ toString(prod_type.arity) ++ " parameters but " ++ toString(ns.varBinderCount) ++ " patterns were provided")]; + + top.errors <- qn.lookupValue.errors; + top.errors <- + case qn.lookupValue.dcls of + | prodDcl (_, _) :: _ -> [] + | [] -> [] + | _ -> [err(qn.location, qn.name ++ " is not a production.")] + end; + + local prod_contexts_type :: Pair<[Context] Type> = fullySkolemizeProductionType(qn.lookupValue.typeScheme); -- that says FULLY. See the comments on that function. + production prod_contexts :: [Context] = prod_contexts_type.fst; + production prod_type :: Type = prod_contexts_type.snd; + + ns.bindingTypes = prod_type.inputTypes; + ns.bindingIndex = 0; + ns.bindingNames = if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.inputNames; + ns.matchingAgainst = if null(qn.lookupValue.dcls) then nothing() else just(qn.lookupValue.dcl); + + -- VarBinders need occurs-on contexts in their env to determine whether var types are decorable + local contextOccursDefs::[OccursDclInfo] = concat( + zipWith( + \ c::Context oc::Context -> + c.contextPatternOccursDefs( + oc, + if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.freeVariables, + scrutineeName, top.location, top.grammarName), + prod_contexts, if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.contexts)); + ns.env = occursEnv(contextOccursDefs, top.env); + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = composeSubst(errCheck2.upSubst, top.finalSubst); -- part of the + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = composeSubst(errCheck2.upSubst, top.finalSubst); -- threading hack + + errCheck1 = check(decoratedType(prod_type.outputType, freshInhSet()), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, qn.name ++ " has type " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + -- For GADTs, threading gets a bit weird. + -- TODO: we SHOULD check that the "base type" is accurate for the pattern / scrutineeType first. + -- but for now for simplicity, we avoid that. + -- So for now, we're just skipping over this case entirely: + top.upSubst = top.downSubst; + + -- AFTER everything is done elsewhere, we come back with finalSubst, and we produce the refinement, and thread THAT through everything. + errCheck1.downSubst = composeSubst(top.finalSubst, produceRefinement(top.scrutineeType, decoratedType(prod_type.outputType, freshInhSet()))); + e.downSubst = errCheck1.upSubst; + errCheck2.downSubst = e.upSubst; + -- Okay, now update the finalSubst.... + e.finalSubst = errCheck2.upSubst; + -- Here ends the hack + + -- If there are contexts on the production, then we need to make the scrutinee available + -- in the RHS to access their implementations. + local scrutineeName::String = "__scrutinee_" ++ toString(genInt()); + local contextDefs::[Def] = concat( + zipWith( + \ c::Context oc::Context -> + performContextSubstitution(c, e.finalSubst).contextPatternDefs( + oc, + if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.freeVariables, + scrutineeName, top.location, top.grammarName), + prod_contexts, if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.contexts)); + e.env = newScopeEnv(contextDefs ++ ns.defs, ns.env); + + top.translation = "if(scrutineeNode instanceof " ++ makeProdName(qn.lookupValue.fullName) ++ ") { " ++ + (if null(prod_contexts) then "" else s"final ${makeProdName(qn.lookupValue.fullName)} ${scrutineeName} = (${makeProdName(qn.lookupValue.fullName)})scrutineeNode; ") ++ + ns.translation ++ " return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ e.translation ++ "; }"; +} + +-- TODO: We currently provide the below for ease of translation from complex case exprs, but +-- we should really translate those to appropriate expressions, and not handle primitive types here + +abstract production integerPattern +top::PrimPattern ::= i::Int_t '->' e::Expr +{ + top.unparse = i.lexeme ++ " -> " ++ e.unparse; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(intType(), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, i.lexeme ++ " is an " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + top.translation = "if(scrutinee == " ++ i.lexeme ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ + e.translation ++ "; }"; +} +abstract production floatPattern +top::PrimPattern ::= f::Float_t '->' e::Expr +{ + top.unparse = f.lexeme ++ " -> " ++ e.unparse; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(floatType(), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, f.lexeme ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + top.translation = "if(scrutinee == " ++ f.lexeme ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ + e.translation ++ "; }"; +} +abstract production stringPattern +top::PrimPattern ::= i::String_t '->' e::Expr +{ + top.unparse = i.lexeme ++ " -> " ++ e.unparse; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(stringType(), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, i.lexeme ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + top.translation = "if(scrutinee.equals(" ++ i.lexeme ++ ")) { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ + e.translation ++ "; }"; +} +abstract production booleanPattern +top::PrimPattern ::= i::String '->' e::Expr +{ + top.unparse = i ++ " -> " ++ e.unparse; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(boolType(), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, i ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + top.translation = "if(scrutinee == " ++ i ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ + e.translation ++ "; }"; +} +abstract production nilPattern +top::PrimPattern ::= e::Expr +{ + top.unparse = "nil() -> " ++ e.unparse; + + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + + errCheck1 = check(listType(freshType()), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, "nil matches lists but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + top.translation = "if(scrutinee.nil()) { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ + e.translation ++ "; }"; +} +abstract production conslstPattern +top::PrimPattern ::= h::Name t::Name e::Expr +{ + top.unparse = "cons(" ++ h.unparse ++ ", " ++ t.unparse ++ ") -> " ++ e.unparse; + + top.freeVars := ts:removeAll([h.name, t.name], e.freeVars); + + local h_fName :: String = toString(genInt()) ++ ":" ++ h.name; + local t_fName :: String = toString(genInt()) ++ ":" ++ t.name; + local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; + local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; + local elemType :: Type = freshType(); + + errCheck1 = check(listType(elemType), top.scrutineeType); + top.errors <- if errCheck1.typeerror + then [err(top.location, "cons matches lists but we're trying to match against " ++ errCheck1.rightpp)] + else []; + + errCheck2 = check(e.typerep, top.returnType); + top.errors <- if errCheck2.typeerror + then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] + else []; + + thread downSubst, upSubst on top, errCheck1, e, errCheck2, top; + + local consdefs :: [Def] = + [lexicalLocalDef(top.grammarName, top.location, h_fName, elemType, noVertex(), []), + lexicalLocalDef(top.grammarName, top.location, t_fName, top.scrutineeType, noVertex(), [])]; + + e.env = newScopeEnv(consdefs, top.env); + + top.translation = + let + elemTrans :: String = performSubstitution(elemType, top.finalSubst).transType, + listTrans :: String = performSubstitution(top.scrutineeType, top.finalSubst).transType + in + "if(!scrutineeIter.nil()) {" ++ + "@SuppressWarnings(\"unchecked\") " ++ + makeSpecialLocalBinding(h_fName, s"(${elemTrans})scrutinee.head()", elemTrans) ++ + "@SuppressWarnings(\"unchecked\") " ++ + makeSpecialLocalBinding(t_fName, s"(${listTrans})scrutinee.tail()", listTrans) ++ + "return " ++ e.translation ++ "; }" + end; +} + + diff --git a/grammars/silver/compiler/modification/primitivepattern/Types.sv b/grammars/silver/compiler/modification/primitivepattern/Types.sv new file mode 100644 index 000000000..bda06396e --- /dev/null +++ b/grammars/silver/compiler/modification/primitivepattern/Types.sv @@ -0,0 +1,449 @@ +grammar silver:compiler:modification:primitivepattern; + + -- so we cover these types with the 'refine' hack below. +import silver:compiler:modification:ffi only foreignType; +import silver:compiler:modification:list only listCtrType; + +import silver:compiler:translation:java; + +{-- + - Turns the existential variables of a production type into skolem constants, + - and freshen the rest. + - e.g. (?a -> ?b -> F ?a) becomes (?c -> !d -> F ?c) + - This is done so we can just unify the scrutinee type an go, no hairy details! + - (This is used for *non-gadt* productions.) + -} +function skolemizeProductionType +Pair<[Context] Type> ::= te::PolyType +{ + local existentialVars :: [TyVar] = removeAll(te.typerep.outputType.freeVariables, te.boundVars); + + local skolemize :: Substitution = composeSubst( + zipVarsIntoSkolemizedSubstitution(existentialVars, freshTyVars(existentialVars)), + zipVarsIntoSubstitution(te.typerep.outputType.freeVariables, freshTyVars(te.typerep.outputType.freeVariables))); + + return pair(map(performContextRenaming(_, skolemize), te.contexts), performRenaming(te.typerep, skolemize)); +} + +{-- + - (This is used for *gadt* productions.) + - wat? why? well, one skolem constant is as good as another, and we're here INTRODUCING + - new variables, and we need to make them skolem constants. + - + - Here's the example, suppose we have 'arrow :: T -> T -> T>' + - and we do 'case (::TypeExpr) of arrow(...)' we're going to refine + - the c to A, but there's a HUGE HUGE PROBLEM THERE because we can't + - allow a and b to be unified together later on, because we have no idea what + - types they are! So a and b MUST wind up as different skolem constants, + - not as type variables, despite appearing in the 'output type'. + - + - So my solution right now is to skolemize the entire type, and I *think* this + - works just fine... for now. The reason is that we're going OutsideIn, so + - type checking should be 'completed'. That is, there should be + - *** NO TYPE VARIABLES AT ALL *** in the scrutineeType anymore. + - Either they got unified with some skolem constant, got unified with some type + - or an error should have been raised somewhere. (Even once we add real inference + - this should be the case, since all free type variables should end up unified with + - some skolem constant upon generalization of an expression...) + - + - TODO: what about nontermination / truely useless ones? + - case error("lol") of eq() -> "umm" | unit() -> "lol" end + - is a-okay with the type checker, but that's because of the TODO in prodPatternGadt. + - Could there be any other issues? + - + - And since we're just doing a 'refine' afterwards, well... one skolem constant + - is as good as another, as far as correctness goes, anyway... + -} +function fullySkolemizeProductionType +Pair<[Context] Type> ::= te::PolyType +{ + local skolemize :: Substitution = zipVarsIntoSkolemizedSubstitution(te.boundVars, freshTyVars(te.boundVars)); + + return pair(map(performContextRenaming(_, skolemize), te.contexts), performRenaming(te.typerep, skolemize)); +} + + + +--- This is unification, EXCEPT that skolem constants behave like type variables! + +inherited attribute refineWith :: Type occurs on Type; +synthesized attribute refine :: Substitution occurs on Type; + +flowtype Type = refine {refineWith}; + +aspect production varType +top::Type ::= tv::TyVar +{ + top.refine = + case top.refineWith of + | varType(j) when j.kindrep == tv.kindrep -> + if tv == j + then emptySubst() + else subst(tv, top.refineWith) + | t when t.kindrep == tv.kindrep -> + if contains(tv, t.freeVariables) + then errorSubst("Infinite type! Tried to refine with " ++ prettyType(t)) + else subst(tv, t) + | t -> errorSubst("Kind mismatch! Tried to refine with " ++ prettyType(top.refineWith)) + end; +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.refine = + case top.refineWith of + | skolemType(j) when j.kindrep == tv.kindrep -> + if tv == j + then emptySubst() + else subst(tv, top.refineWith) + | t when t.kindrep == tv.kindrep -> + if contains(tv, t.freeVariables) + then errorSubst("Infinite type! Tried to refine with " ++ prettyType(t)) + else subst(tv, t) + | t -> errorSubst("Kind mismatch! Tried to refine with " ++ prettyType(top.refineWith)) + end; +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.refine = + case top.refineWith of + | appType(c1, a1) -> + let refineC :: Substitution = refine(c, c1) + in composeSubst(refineC, refine(performSubstitution(a, refineC), performSubstitution(a1, refineC))) + end + | _ -> errorSubst("Tried to refine " ++ prettyType(top) ++ " with " ++ prettyType(top.refineWith)) + end; +} + +aspect production errorType +top::Type ::= +{ + top.refine = emptySubst(); +} + +aspect production intType +top::Type ::= +{ + top.refine = + case top.refineWith of + | intType() -> emptySubst() + | _ -> errorSubst("Tried to refine Integer with " ++ prettyType(top.refineWith)) + end; +} + +aspect production boolType +top::Type ::= +{ + top.refine = + case top.refineWith of + | boolType() -> emptySubst() + | _ -> errorSubst("Tried to refine Boolean with " ++ prettyType(top.refineWith)) + end; +} + +aspect production floatType +top::Type ::= +{ + top.refine = + case top.refineWith of + | floatType() -> emptySubst() + | _ -> errorSubst("Tried to refine Float with " ++ prettyType(top.refineWith)) + end; +} + +aspect production stringType +top::Type ::= +{ + top.refine = + case top.refineWith of + | stringType() -> emptySubst() + | _ -> errorSubst("Tried to refine Boolean with " ++ prettyType(top.refineWith)) + end; +} + +aspect production terminalIdType +top::Type ::= +{ + top.refine = + case top.refineWith of + | terminalIdType() -> emptySubst() + | _ -> errorSubst("Tried to refine TerminalId with " ++ prettyType(top.refineWith)) + end; +} + +aspect production inhSetType +top::Type ::= inhs::[String] +{ + top.refine = + case top.refineWith of + | inhSetType(oinhs) when inhs == oinhs -> emptySubst() + | _ -> errorSubst("Tried to refine inh set type " ++ prettyType(top) ++ " with " ++ prettyType(top.refineWith)) + end; +} + +aspect production nonterminalType +top::Type ::= fn::String ks::[Kind] _ +{ + top.refine = + case top.refineWith of + | nonterminalType(ofn, oks, _) -> + if fn == ofn && ks == oks + then emptySubst() + else errorSubst("Tried to refine conflicting nonterminal types " ++ fn ++ " and " ++ ofn) + | _ -> errorSubst("Tried to refine nonterminal type " ++ fn ++ " with " ++ prettyType(top.refineWith)) + end; +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.refine = + case top.refineWith of + | terminalType(ofn) -> + if fn == ofn + then emptySubst() + else errorSubst("Tried to refine conflicting terminal types " ++ fn ++ " and " ++ ofn) + | _ -> errorSubst("Tried to refine terminal type " ++ fn ++ " with " ++ prettyType(top.refineWith)) + end; +} + +aspect production decoratedType +top::Type ::= te::Type i::Type +{ + top.refine = + case top.refineWith of + | decoratedType(oi, ote) -> composeSubst(refine(te, ote), refine(i, oi)) + | _ -> errorSubst("Tried to refine decorated type with " ++ prettyType(top.refineWith)) + end; +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type i::Type +{ + top.refine = + case top.refineWith of + | partiallyDecoratedType(oi, ote) -> composeSubst(refine(te, ote), refine(i, oi)) + | _ -> errorSubst("Tried to refine partially decorated type with " ++ prettyType(top.refineWith)) + end; +} + +aspect production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.refine = + case top.refineWith of + | functionType(op, onp) when params == op && namedParams == onp -> emptySubst() + | _ -> errorSubst("Tried to refine function type with " ++ prettyType(top.refineWith)) + end; +} + +aspect production foreignType +top::Type ::= fn::String transType::String params::[Type] +{ + top.refine = + case top.refineWith of + | foreignType(ofn, _, op) -> + if fn == ofn + then refineAll( params, op ) + else errorSubst("Tried to refine conflicting foreign types " ++ fn ++ " and " ++ ofn) + | _ -> errorSubst("Tried to refine foreign type " ++ fn ++ " with " ++ prettyType(top.refineWith)) + end; +} + +aspect production listCtrType +top::Type ::= +{ + top.refine = + case top.refineWith of + | listCtrType() -> emptySubst() + | _ -> errorSubst("Tried to refine [] with " ++ prettyType(top.refineWith)) + end; +} + +{-- + - Produces substitutions that may involve skolem constants, as well as free variables + - for constructors. + - + - @param scrutineeType The decorated type of the value being examined. Should not be a type variable! + - @param constructorType The decorated type of the production's product (i.e. the type it constructs) + -} +function produceRefinement +Substitution ::= scrutineeType::Type constructorType::Type +{ + -- only do refinement if they're the same type constructor. + -- If you look at the type rules, you'll notice they're requiring "T" be the same, + -- and this refinement only happens on the parameters (i.e. fmgu(T p = T a)) + return case scrutineeType, constructorType of + | decoratedType(t1, i1), decoratedType(t2, i2) -> + case t1.baseType, t2.baseType of + | nonterminalType(n1, _, _), nonterminalType(n2, _, _) when n1 == n2 -> + refineAll(i1 :: t1.argTypes, i2 :: t2.argTypes) + | _, _ -> emptySubst() + end + | _, _ -> emptySubst() + end; +} + +function refine +Substitution ::= te1::Type te2::Type +{ + local attribute leftward :: Substitution; + leftward = te1.refine; + te1.refineWith = te2; + + local attribute rightward :: Substitution; + rightward = te2.refine; + te2.refineWith = te1; + + return if null(leftward.substErrors) + then leftward -- arbitrary choice if both work, but if they are confluent, it's okay + else rightward; -- arbitrary choice of errors. Non-confluent!! +} +function refineAll +Substitution ::= te1::[Type] te2::[Type] +{ + local attribute first :: Substitution; + first = refine(head(te1), head(te2)); + + return if null(te1) && null(te2) + then emptySubst() + else if null(te1) || null(te2) + then errorSubst("Internal error: refineing mismatching numbers") + else composeSubst(first, refineAll( mapSubst(tail(te1), first), + mapSubst(tail(te2), first) )); +} + + +-------- +function isOnlyTyVars +Boolean ::= ls::[Type] +{ + return case ls of + | [] -> true + | varType(_) :: t -> isOnlyTyVars(t) + | skolemType(_) :: t -> isOnlyTyVars(t) + | _ -> false + end; +} + + +-------- +synthesized attribute contextPatternDefs::([Def] ::= Context [TyVar] String Location String) occurs on Context; +synthesized attribute contextPatternOccursDefs::([OccursDclInfo] ::= Context [TyVar] String Location String) occurs on Context; + +aspect default production +top::Context ::= +{ + top.contextPatternDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> []; + top.contextPatternOccursDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> []; +} + +aspect production instContext +top::Context ::= cls::String t::Type +{ + top.contextPatternDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [tcInstDef(instPatternConstraintDcl(cls, t, oc, tvs, st, sourceLocation=l, sourceGrammar=g))]; +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextPatternOccursDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [occursPatternConstraintDcl(attr, ntty, atty, oc, tvs, st, sourceLocation=l, sourceGrammar=g)]; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.contextPatternOccursDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [occursPatternConstraintDcl(attr, ntty, atty, oc, tvs, st, sourceLocation=l, sourceGrammar=g)]; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextPatternOccursDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [annoPatternConstraintDcl(attr, ntty, atty, oc, tvs, st, sourceLocation=l, sourceGrammar=g)]; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + top.contextPatternDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [tcInstDef(typeablePatternConstraintDcl(t, oc, tvs, st, sourceLocation=l, sourceGrammar=g))]; +} + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.contextPatternDefs = \ oc::Context tvs::[TyVar] st::String l::Location g::String -> + [tcInstDef(inhSubsetPatternConstraintDcl(i1, i2, oc, tvs, st, sourceLocation=l, sourceGrammar=g))]; +} + +abstract production instPatternConstraintDcl +top::InstDclInfo ::= fntc::String ty::Type oc::Context tvs::[TyVar] scrutineeTrans::String +{ + top.fullName = fntc; + propagate compareTo, isEqual; + top.typeScheme = monoType(ty); + + oc.boundVariables = tvs; + top.transContext = s"${scrutineeTrans}.${oc.transContextMemberName}"; +} + +abstract production occursPatternConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type oc::Context tvs::[TyVar] scrutineeTrans::String +{ + top.fullName = ntty.typeName; + propagate compareTo, isEqual; + top.attrOccurring = fnat; + top.typeScheme = monoType(atty); + + oc.boundVariables = tvs; + top.attrOccursIndex = s"${scrutineeTrans}.${oc.transContextMemberName}"; + + -- Never appears for anything on which we can define an equation + top.attrOccursIndexName = error("Not needed"); + top.attrOccursInitIndex = error("Not needed"); +} + +abstract production annoPatternConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type oc::Context tvs::[TyVar] scrutineeTrans::String +{ + top.fullName = ntty.typeName; + propagate compareTo, isEqual; + top.attrOccurring = fnat; + top.typeScheme = monoType(atty); + top.isAnnotation = true; + + oc.boundVariables = tvs; + top.attrOccursIndex = error("Not actually an attribute"); + top.attrOccursIndexName = error("Not actually an attribute"); + top.attrOccursInitIndex = error("Not actually an attribute"); +} + +abstract production typeablePatternConstraintDcl +top::InstDclInfo ::= ty::Type oc::Context tvs::[TyVar] scrutineeTrans::String +{ + top.fullName = "typeable"; + propagate compareTo, isEqual; + top.typeScheme = monoType(ty); + + oc.boundVariables = tvs; + top.transContext = s"${scrutineeTrans}.${oc.transContextMemberName}"; +} + +abstract production inhSubsetPatternConstraintDcl +top::InstDclInfo ::= i1::Type i2::Type oc::Context tvs::[TyVar] scrutineeTrans::String +{ + top.fullName = "subset"; + propagate compareTo, isEqual; + top.typeScheme = monoType(i1); + top.typerep2 = i2; + + oc.boundVariables = tvs; + top.transContext = s"${scrutineeTrans}.${oc.transContextMemberName}"; +} diff --git a/grammars/silver/compiler/modification/primitivepattern/VarBinders.sv b/grammars/silver/compiler/modification/primitivepattern/VarBinders.sv new file mode 100644 index 000000000..606c64dd5 --- /dev/null +++ b/grammars/silver/compiler/modification/primitivepattern/VarBinders.sv @@ -0,0 +1,195 @@ +grammar silver:compiler:modification:primitivepattern; + +option silver:compiler:analysis:warnings:flow; -- needed due to receivedDeps attribute + +import silver:compiler:translation:java:core; +import silver:compiler:translation:java:type; + +import silver:compiler:modification:let_fix only makeSpecialLocalBinding, lexicalLocalDef; + +import silver:compiler:definition:flow:ast only hasVertex, noVertex, PatternVarProjection, patternVarProjection, anonVertexType, ExprVertexInfo, FlowVertex, inhVertex; +-- also unfortunately placed references to flowEnv + +import silver:compiler:analysis:warnings:flow only receivedDeps; -- Used in computing flow errors + +nonterminal VarBinders with + config, grammarName, env, compiledGrammars, frame, + location, unparse, errors, defs, boundNames, + bindingTypes, bindingIndex, translation, varBinderCount, + finalSubst, flowProjections, bindingNames, flowEnv, matchingAgainst; +nonterminal VarBinder with + config, grammarName, env, compiledGrammars, frame, + location, unparse, errors, defs, boundNames, + bindingType, bindingIndex, translation, + finalSubst, flowProjections, bindingName, flowEnv, matchingAgainst; + +flowtype decorate {grammarName, env, flowEnv, finalSubst, frame, compiledGrammars, config, bindingTypes, bindingIndex, bindingNames, matchingAgainst} on VarBinders; +flowtype decorate {grammarName, env, flowEnv, finalSubst, frame, compiledGrammars, config, bindingType, bindingIndex, bindingName, matchingAgainst} on VarBinder; + +flowtype forward {decorate} on VarBinders, VarBinder; +flowtype errors {decorate, receivedDeps} on VarBinders, VarBinder; +flowtype defs {decorate} on VarBinders, VarBinder; +flowtype boundNames {} on VarBinders, VarBinder; + +propagate errors, defs, boundNames on VarBinders, VarBinder; + +--- Types of each child +inherited attribute bindingTypes :: [Type]; +inherited attribute bindingType :: Type; +--- Index of each child +inherited attribute bindingIndex :: Integer; +--- Names of each child (for flow analysis) +inherited attribute bindingNames :: [String]; +inherited attribute bindingName :: String; +--- Extractions of decoration sites from children +synthesized attribute flowProjections :: [PatternVarProjection]; + +-- The DclInfo of the production we're matching against +autocopy attribute matchingAgainst :: Maybe; + +synthesized attribute varBinderCount :: Integer; + +monoid attribute boundNames::[String]; + +concrete production oneVarBinder +top::VarBinders ::= v::VarBinder +{ + top.unparse = v.unparse; + + top.translation = v.translation; + top.varBinderCount = 1; + top.flowProjections = v.flowProjections; + + v.bindingIndex = top.bindingIndex; + v.bindingType = + if null(top.bindingTypes) + then errorType() + else head(top.bindingTypes); + v.bindingName = + if null(top.bindingNames) + then "__NONAME" + else head(top.bindingNames); +} +concrete production consVarBinder +top::VarBinders ::= v::VarBinder ',' vs::VarBinders +{ + top.unparse = v.unparse ++ ", " ++ vs.unparse; + + top.translation = v.translation ++ vs.translation; + top.varBinderCount = 1 + vs.varBinderCount; + top.flowProjections = v.flowProjections ++ vs.flowProjections; + + v.bindingIndex = top.bindingIndex; + vs.bindingIndex = top.bindingIndex + 1; + + v.bindingType = + if null(top.bindingTypes) + then errorType() + else head(top.bindingTypes); + vs.bindingTypes = + if null(top.bindingTypes) + then [] + else tail(top.bindingTypes); + v.bindingName = + if null(top.bindingNames) + then "__NONAME" + else head(top.bindingNames); + vs.bindingNames = + if null(top.bindingNames) + then [] + else tail(top.bindingNames); +} +concrete production nilVarBinder +top::VarBinders ::= +{ + top.unparse = ""; + + top.translation = ""; + top.varBinderCount = 0; + top.flowProjections = []; +} + +concrete production varVarBinder +top::VarBinder ::= n::Name +{ + top.unparse = n.unparse; + + -- top.bindingType comes straight from the type in the production signature. + -- Consequently, the child is only auto-decorated if + -- isDecorable(top.bindingType, top.env), and never otherwise. + -- (We *DO NOT* want to substitute first... because that will turn the type + -- variables into concrete types! and type variables in a production are + -- NOT automatically decorated!) + -- Also, don't attempt to decorate already-partially-decorated types. + local ty :: Type = + if isDecorable(top.bindingType, top.env) && !top.bindingType.isDecorated + then decoratedType(top.bindingType, freshInhSet()) + else top.bindingType; + production finalTy::Type = performSubstitution(ty, top.finalSubst); + production refSet::Maybe<[String]> = getMaxRefSet(finalTy, top.env); + + production fName :: String = "__pv" ++ toString(genInt()) ++ ":" ++ n.name; + + -- If it's decorable, then we do projections through the production + -- if it's not, then we treat it like a generic reference. + top.flowProjections = + if isDecorable(top.bindingType, top.env) + then [patternVarProjection(top.bindingName, top.bindingType.typeName, fName)] + else []; + -- because we don't have an 'anonEq' (the nonterminal stitch point gets generated for us by the above contribution) we won't be reported as missing in this production. Checks for presence in remote productions have to be done explicitly + + -- Recall that we emit (vertex, [reference set]) for expressions with a vertex. + -- and the correct value is computed based on how this gets used. + -- (e.g. if 'new' + local vt :: ExprVertexInfo = + if isDecorable(top.bindingType, top.env) + then hasVertex(anonVertexType(fName)) + else noVertex(); + local deps :: [FlowVertex] = + if isDecorable(top.bindingType, top.env) + then map(anonVertexType(fName).inhVertex, fromMaybe([], refSet)) + else []; + + top.defs <- [lexicalLocalDef(top.grammarName, n.location, fName, ty, vt, deps)]; + top.boundNames <- [n.name]; + + -- finalSubst is not necessary, downSubst would work fine, but is not threaded through here. + -- the point is that 'ty' for Pair would currently show Pair + -- since top.bindingType comes straight from the production's type in the environment. + -- we need to do some substitution to connect it with the real types. + -- (in the env above its okay, since that must always be consulted with the current substitution, + -- but here we're rendering the translation. it's the end of the line.) + local actualTy :: Type = performSubstitution(ty, top.finalSubst); + + top.translation = + makeSpecialLocalBinding(fName, + "scrutinee." ++ + (if isDecorable(top.bindingType, top.env) + then "childDecorated(" + else s"<${actualTy.transType}>childAsIs(") ++ + toString(top.bindingIndex) ++ ")", + actualTy.transType); + + -- We prevent this to prevent newbies from thinking patterns are "typecase" + -- (Types have to be upper case) + top.errors <- + if !isUpper(substring(0,1,n.name)) then [] + else [err(top.location, "Pattern variables must start with a lower case letter")]; + + -- We prevent this to avoid people possibly forgetting the parens, e.g. writing 'nothing' + -- One thing we could do is specifically raise this error, only if it's the production would be the right type. + -- this would allow us to match 'left' and 'right' on a Pair, for example, but error on Either + top.errors <- + case getValueDcl(n.name, top.env) of + | prodDcl(_,_) :: _ -> [err(top.location, "Pattern variables cannot have the same name as productions (to avoid confusion)")] + | _ -> [] + end; +} +concrete production ignoreVarBinder +top::VarBinder ::= '_' +{ + top.unparse = "_"; + top.flowProjections = []; + top.translation = ""; +} + diff --git a/grammars/silver/compiler/translation/java/Project.sv b/grammars/silver/compiler/translation/java/Project.sv new file mode 100644 index 000000000..61080f3ee --- /dev/null +++ b/grammars/silver/compiler/translation/java/Project.sv @@ -0,0 +1,7 @@ +grammar silver:compiler:translation:java; + +exports silver:compiler:translation:java:core; +exports silver:compiler:translation:java:type; + +exports silver:compiler:translation:java:driver; + diff --git a/grammars/silver/compiler/translation/java/core/Annotation.sv b/grammars/silver/compiler/translation/java/core/Annotation.sv new file mode 100644 index 000000000..231bde2fd --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/Annotation.sv @@ -0,0 +1,23 @@ +grammar silver:compiler:translation:java:core; + +aspect production annotationDcl +top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' +{ + local className :: String = "A" ++ a.name; + +-- We report the trans type, despite the fact that the attribute may be parameterized! +-- It should be fine, though. If we're a tv, then it's 'Object' and anything +-- else will be a subtype... + + top.genFiles := [pair(className ++ ".java", s""" + +package ${makeName(top.grammarName)}; + +public interface ${className} { + + public ${te.typerep.transType} getAnno_${makeIdName(fName)}(); + +} +""")]; +} + diff --git a/grammars/silver/compiler/translation/java/core/AspectDcl.sv b/grammars/silver/compiler/translation/java/core/AspectDcl.sv new file mode 100644 index 000000000..33ba50508 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/AspectDcl.sv @@ -0,0 +1,17 @@ +grammar silver:compiler:translation:java:core; + +aspect production aspectProductionDcl +top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody +{ + top.setupInh := body.setupInh; + top.initProd := s"\t\t//ASPECT PRODUCTION ${id.name} ${ns.unparse}\n${body.translation}"; + top.valueWeaving := body.valueWeaving; +} + +aspect production aspectFunctionDcl +top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody +{ + top.setupInh := body.setupInh; + top.initProd := s"\t\t//ASPECT FUNCTION ${id.name} ${ns.unparse}\n${body.translation}"; + top.valueWeaving := body.valueWeaving; +} diff --git a/grammars/silver/compiler/translation/java/core/BlockContext.sv b/grammars/silver/compiler/translation/java/core/BlockContext.sv new file mode 100644 index 000000000..39525fcec --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/BlockContext.sv @@ -0,0 +1,49 @@ +grammar silver:compiler:translation:java:core; + +attribute className, prodLocalCountName occurs on BlockContext; + +{-- + - The name of the class for the current context. + - e.g. "silver.definition.core.PbaseExpr" + - + - Valid to access only within productions/functions. + - (used by equations. exprs, but only in flowDefs, which shouldn't be accessed outside productions) + - (Also used by production action blocks to access children) + -} +synthesized attribute className :: String; +{-- + - The name to access a production's local count. + - e.g. "silver.definition.core.Init.count_local__ON__silver_definition_core_PbaseExpr" + - + - Also valid only in prod/func. Used only by local declarations. + -} +synthesized attribute prodLocalCountName :: String; + +aspect default production +top::BlockContext ::= +{ + top.className = error("context does not have a className"); + top.prodLocalCountName = error("prodLocalCountName in context without locals"); +} + +aspect production functionContext +top::BlockContext ::= sig::NamedSignature _ +{ + top.className = makeProdName(top.fullName); + top.prodLocalCountName = + makeName(top.sourceGrammar) ++ ".Init.count_local__ON__" ++ makeIdName(top.fullName); +} + +aspect production productionContext +top::BlockContext ::= sig::NamedSignature _ +{ + top.className = makeProdName(top.fullName); + top.prodLocalCountName = + makeName(top.sourceGrammar) ++ ".Init.count_local__ON__" ++ makeIdName(top.fullName); +} + +aspect production globalExprContext +top::BlockContext ::= _ _ _ _ +{ +} + diff --git a/grammars/silver/compiler/translation/java/core/ClassDcl.sv b/grammars/silver/compiler/translation/java/core/ClassDcl.sv new file mode 100644 index 000000000..043f5316d --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/ClassDcl.sv @@ -0,0 +1,68 @@ +grammar silver:compiler:translation:java:core; + +aspect production typeClassDcl +top::AGDcl ::= 'class' cl::ConstraintList '=>' id::QNameType var::TypeExpr '{' body::ClassBody '}' +{ + local className :: String = "C" ++ last(explode(":", fName)); + + top.genFiles := [pair(className ++ ".java", s""" + +package ${makeName(top.grammarName)}; + +public interface ${className} { + + // Needed since 'this' may refer to something else inside member translations + public default ${className} currentInstance() { + return this; + } + +${flatMap(\ c::Context -> s"\tpublic ${c.transType} ${c.transContextSuperAccessorName}();\n", cl.contexts)} + +${body.translation} + +} +""")]; +} + +attribute translation occurs on ClassBody, ClassBodyItem; + +aspect production consClassBody +top::ClassBody ::= h::ClassBodyItem t::ClassBody +{ + top.translation = h.translation ++ t.translation; +} +aspect production nilClassBody +top::ClassBody ::= +{ + top.translation = ""; +} + +aspect production constraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr ';' +{ + local contexts::Contexts = foldContexts(cl.contexts); + contexts.boundVariables = boundVars; + + top.translation = s"\t${ty.typerep.transType} ${makeInstanceMemberAccessorName(id.name)}(${contexts.contextParamTrans});"; +} + +aspect production defaultConstraintClassBodyItem +top::ClassBodyItem ::= id::Name '::' cl::ConstraintList '=>' ty::TypeExpr '=' e::Expr ';' +{ + local contexts::Contexts = foldContexts(cl.contexts); + contexts.boundVariables = boundVars; + + top.translation = s""" + default ${ty.typerep.transType} ${makeInstanceMemberAccessorName(id.name)}(${contexts.contextParamTrans}) { + final common.DecoratedNode context = common.TopNode.singleton; + //${e.unparse} + return ${e.generalizedTranslation}; + } +"""; +} + +function makeInstanceMemberAccessorName +String ::= s::String +{ + return "getMember_" ++ last(explode(":", s)); +} diff --git a/grammars/silver/compiler/translation/java/core/DclInfo.sv b/grammars/silver/compiler/translation/java/core/DclInfo.sv new file mode 100644 index 000000000..9cca5a52f --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/DclInfo.sv @@ -0,0 +1,113 @@ +grammar silver:compiler:translation:java:core; + + +attribute attrOccursIndexName, attrOccursInitIndex, attrGlobalOccursInitIndex, attrOccursIndex occurs on OccursDclInfo; + +{-- + - The name of the occurs variable. e.g. silver_def_core_pp__ON__silver_def_core_Expr + -} +synthesized attribute attrOccursIndexName :: String; +{-- + - Index of the attribute used for initializating attribute equations. + - e.g. silver.def.core.silver_def_core_pp__ON__silver_def_core_Expr + - or foo.bar.PExpr.foo_bar_inh__ON__a for an inherited occurs-on constraing + -} +synthesized attribute attrOccursInitIndex :: String; +{-- + - Index of the attribute used for initializating attribute equations. + - Only defined for global occurs-on declarations, doesn't depend on transContextDeps. + - e.g. silver.def.core.silver_def_core_pp__ON__silver_def_core_Expr + -} +synthesized attribute attrGlobalOccursInitIndex :: String; +{-- + - Index of the attribute used for accessing the attribute on a DecoratedNode. + - e.g. silver.def.core.silver_def_core_pp__ON__silver_def_core_Expr + - or foo.bar.PExpr.d_foo_bar_inh__a for an inherited occurs-on constraint + -} +synthesized attribute attrOccursIndex :: String; + +aspect default production +top::OccursDclInfo ::= +{ + top.attrGlobalOccursInitIndex = error("Should only be accessed on global occursDcl"); +} + +aspect production occursDcl +top::OccursDclInfo ::= fnnt::String fnat::String ntty::Type atty::Type +{ + top.attrOccursIndexName = makeIdName(fnat ++ "__ON__" ++ fnnt); + top.attrOccursInitIndex = top.attrGlobalOccursInitIndex; + top.attrGlobalOccursInitIndex = makeName(top.sourceGrammar) ++ ".Init." ++ top.attrOccursIndexName; + top.attrOccursIndex = top.attrOccursInitIndex; +} +aspect production occursInstConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type tvs::[TyVar] +{ + top.attrOccursIndexName = makeIdName(fnat ++ "__ON__" ++ ntty.transTypeName); + top.attrOccursInitIndex = top.attrOccursIndex; + top.attrOccursIndex = makeConstraintDictName(fnat, ntty, tvs); +} +aspect production occursSigConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type ns::NamedSignature +{ + top.attrOccursIndexName = makeIdName(fnat ++ "__ON__" ++ ntty.transTypeName); + top.attrOccursInitIndex = makeProdName(ns.fullName) ++ "." ++ top.attrOccursIndexName; + top.attrOccursIndex = s"((${makeProdName(ns.fullName)})(context.undecorate())).${makeConstraintDictName(fnat, ntty, ns.freeVariables)}"; +} +aspect production occursSuperDcl +top::OccursDclInfo ::= fnat::String atty::Type baseDcl::InstDclInfo +{ + top.attrOccursIndexName = makeIdName(fnat ++ "__ON__" ++ transTypeNameWith(baseDcl.typeScheme.typerep, baseDcl.typeScheme.boundVars)); + top.attrOccursInitIndex = top.attrOccursIndex; + top.attrOccursIndex = baseDcl.transContext ++ s".${makeInstanceSuperAccessorName(fnat)}()"; +} +aspect production annoInstanceDcl +top::OccursDclInfo ::= fnnt::String fnat::String ntty::Type atty::Type +{ + top.attrOccursIndexName = error("Not actually an attribute"); + top.attrOccursInitIndex = error("Not actually an attribute"); + top.attrGlobalOccursInitIndex = error("Not actually an attribute"); + top.attrOccursIndex = error("Not actually an attribute"); +} +aspect production annoInstConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type tvs::[TyVar] +{ + top.attrOccursIndexName = error("Not actually an attribute"); + top.attrOccursInitIndex = error("Not actually an attribute"); + top.attrOccursIndex = error("Not actually an attribute"); +} +aspect production annoSigConstraintDcl +top::OccursDclInfo ::= fnat::String ntty::Type atty::Type ns::NamedSignature +{ + top.attrOccursIndexName = error("Not actually an attribute"); + top.attrOccursInitIndex = error("Not actually an attribute"); + top.attrOccursIndex = error("Not actually an attribute"); +} +aspect production annoSuperDcl +top::OccursDclInfo ::= fnat::String atty::Type baseDcl::InstDclInfo +{ + top.attrOccursIndexName = error("Not actually an attribute"); + top.attrOccursInitIndex = error("Not actually an attribute"); + top.attrOccursIndex = error("Not actually an attribute"); +} + +attribute attrOccursIndexName, attrOccursInitIndex, attrOccursIndex occurs on ValueDclInfo; + +aspect default production +top::ValueDclInfo ::= +{ + top.attrOccursIndexName = error("Internal compiler error: not a local/production attribute"); + top.attrOccursInitIndex = error("Internal compiler error: not a local/production attribute"); + top.attrOccursIndex = error("Internal compiler error: not a local/production attribute"); +} + +aspect production localDcl +top::ValueDclInfo ::= fn::String ty::Type +{ + local attribute li :: Integer; + li = lastIndexOf(":local:", fn); + top.attrOccursIndexName = makeIdName(substring(li+7, length(fn), fn) ++ "__ON__" ++ substring(0,li,fn)); + top.attrOccursInitIndex = top.attrOccursIndex; + top.attrOccursIndex = makeName(top.sourceGrammar) ++ ".Init." ++ top.attrOccursIndexName; +} + diff --git a/grammars/silver/compiler/translation/java/core/Expr.sv b/grammars/silver/compiler/translation/java/core/Expr.sv new file mode 100644 index 000000000..95feeebd7 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/Expr.sv @@ -0,0 +1,587 @@ +grammar silver:compiler:translation:java:core; + + +import silver:compiler:analysis:typechecking:core only finalSubst; + +import silver:compiler:driver only noOrigins; + +function finalType +Type ::= e::Decorated Expr +{ + return performSubstitution(e.typerep, e.finalSubst); +} + +{-- + - A translation string that will be a thunk instead of the raw value. + - BUT, is permitted to be a raw value IF it's totally safe to do so. + -} +synthesized attribute lazyTranslation :: String; + +attribute lazyTranslation, translation occurs on Expr; +attribute lazyTranslation occurs on Exprs; + +-- `translation` should yield an expression of the appropriate Java type. +-- e.g. `NodeFactory` for a (String ::= ...) +-- At the moment, this requires a lot of casts. Oh well. + +-- `lazyTranslation` can yield any type, since it's only ever immediately used +-- to put values in a `new Object[]{...}` + +synthesized attribute invokeTranslation :: String occurs on Expr; +inherited attribute invokeArgs :: Decorated AppExprs occurs on Expr; +inherited attribute invokeNamedArgs :: Decorated AnnoAppExprs occurs on Expr; +inherited attribute sameProdAsProductionDefinedOn :: Boolean occurs on Expr; + +{-- + - A translation string where skolems in run-time type info should be generalized. + - E.g. global id :: (a ::= a) = \ x::a -> x; it is safe and more general for the lambda + - to have runtime type (var ::= var) rather than (skolem ::= skolem). + -} +synthesized attribute generalizedTranslation :: String occurs on Expr; + +aspect default production +top::Expr ::= +{ + top.invokeTranslation = + -- dynamic method invocation + s"${top.translation}.invoke(${makeOriginContextRef(top)}, new Object[]{${argsTranslation(top.invokeArgs)}}, ${namedargsTranslation(top.invokeNamedArgs)})"; + top.generalizedTranslation = top.translation; +} + +aspect production errorExpr +top::Expr ::= msg::[Message] +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); + top.lazyTranslation = top.translation; +} + +aspect production errorReference +top::Expr ::= msg::[Message] q::PartiallyDecorated QName +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); + top.lazyTranslation = top.translation; +} + +aspect production childReference +top::Expr ::= q::PartiallyDecorated QName +{ + local childIDref :: String = + top.frame.className ++ ".i_" ++ q.lookupValue.fullName; + + top.translation = + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if finalType(top).isDecorated + then s"((${finalType(top).transType})context.childDecorated(${childIDref}))" + else s"((${finalType(top).transType})context.childDecorated(${childIDref}).undecorate())" + else s"context.<${finalType(top).transType}>childAsIs(${childIDref})"; + -- the reason we do .childDecorated().undecorate() is that it's not safe to mix as-is/decorated accesses to the same child. + -- this is a potential source of minor inefficiency for functions that do not decorate. + + top.lazyTranslation = + if !top.frame.lazyApplication then top.translation else + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if finalType(top).isDecorated + then s"context.childDecoratedLazy(${childIDref})" + else s"common.Thunk.transformUndecorate(context.childDecoratedLazy(${childIDref}))" + else s"context.childAsIsLazy(${childIDref})"; +} + +aspect production localReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.translation = + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if finalType(top).isDecorated + then s"((${finalType(top).transType})context.localDecorated(${q.lookupValue.dcl.attrOccursIndex}))" + else s"((${finalType(top).transType})context.localDecorated(${q.lookupValue.dcl.attrOccursIndex}).undecorate())" + else s"context.<${finalType(top).transType}>localAsIs(${q.lookupValue.dcl.attrOccursIndex})"; + -- reminder: look at comments for childReference + + top.lazyTranslation = + if !top.frame.lazyApplication then top.translation else + if isDecorable(q.lookupValue.typeScheme.typerep, top.env) + then if finalType(top).isDecorated + then s"context.localDecoratedLazy(${q.lookupValue.dcl.attrOccursIndex})" + else s"common.Thunk.transformUndecorate(context.localDecoratedLazy(${q.lookupValue.dcl.attrOccursIndex}))" + else s"context.localAsIsLazy(${q.lookupValue.dcl.attrOccursIndex})"; +} + +aspect production lhsReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.translation = + if finalType(top).isDecorated + then "context" + else s"((${finalType(top).transType})context.undecorate())"; + + top.lazyTranslation = top.translation; +} + +aspect production forwardReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.translation = + if finalType(top).isDecorated + then "context.forward()" + else s"((${finalType(top).transType})context.forward().undecorate())"; + + -- this might evaluate the forward equation, so suspend it as a thunk + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production productionReference +top::Expr ::= q::PartiallyDecorated QName +{ + top.translation = + if null(typeScheme.contexts) + then makeProdName(q.lookupValue.fullName) ++ ".factory" + else s"new ${makeProdName(q.lookupValue.fullName)}.Factory(${implode(", ", contexts.transContexts)})"; + top.lazyTranslation = top.translation; + top.invokeTranslation = + -- static constructor invocation + s"new ${makeProdName(q.lookupValue.fullName)}(${implode(", ", makeNewConstructionOrigin(top, !top.sameProdAsProductionDefinedOn) ++ contexts.transContexts ++ map((.lazyTranslation), top.invokeArgs.exprs ++ reorderedAnnoAppExprs(top.invokeNamedArgs)))})"; +} + +aspect production functionReference +top::Expr ::= q::PartiallyDecorated QName +{ + -- functions, unlike productions, can return a type variable. + -- as such, we have to cast it to the real inferred final type. + top.translation = + if top.typerep.transType != finalType(top).transType + then s"common.Util.<${finalType(top).transType}>uncheckedCast(${top.lazyTranslation})" + else top.lazyTranslation; + top.lazyTranslation = + if null(typeScheme.contexts) + then makeProdName(q.lookupValue.fullName) ++ ".factory" + else s"${makeProdName(q.lookupValue.fullName)}.getFactory(${implode(", ", contexts.transContexts)})"; + + local invokeTrans::String = + -- static method invocation + s"${makeProdName(q.lookupValue.fullName)}.invoke(${implode(", ", [makeOriginContextRef(top)] ++ contexts.transContexts ++ map((.lazyTranslation), top.invokeArgs.exprs))})"; + top.invokeTranslation = + if top.typerep.outputType.transType != finalType(top).outputType.transType + then s"common.Util.<${finalType(top).outputType.transType}>uncheckedCast(${invokeTrans})" + else invokeTrans; +} + +aspect production classMemberReference +top::Expr ::= q::PartiallyDecorated QName +{ + local transContextMember::String = + s"${instHead.transContext}.${makeInstanceMemberAccessorName(q.lookupValue.fullName)}(${implode(", ", contexts.transContexts)})"; + local resolvedDcl::InstDclInfo = head(instHead.resolved); + top.translation = + if !null(resolvedDcl.typeScheme.boundVars) || !contains(q.lookupValue.fullName, resolvedDcl.definedMembers) + -- The resolved instance has a polymorphic implementation for the member, + -- or relies on a default implementation, which may have a more general type. + -- This means that we must insert a cast to the more specific inferred result type. + then s"common.Util.<${finalType(top).transType}>uncheckedCast(${transContextMember})" + else transContextMember; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production globalValueReference +top::Expr ::= q::PartiallyDecorated QName +{ + local directThunk :: String = + s"${makeName(q.lookupValue.dcl.sourceGrammar)}.Init.global_${fullNameToShort(q.lookupValue.fullName)}" ++ + if null(typeScheme.contexts) then "" + else s"(${implode(", ", contexts.transContexts)})"; + + top.translation = s"common.Util.<${finalType(top).transType}>uncheckedCast(${directThunk}.eval())"; + top.lazyTranslation = + if top.frame.lazyApplication + then directThunk + else s"${directThunk}.eval()"; +} +aspect production errorApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs annos::PartiallyDecorated AnnoAppExprs +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); + top.lazyTranslation = top.translation; +} + +aspect production functionInvocation +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs annos::PartiallyDecorated AnnoAppExprs +{ + top.translation = e.invokeTranslation; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); + + e.invokeArgs = es; + e.invokeNamedArgs = annos; + e.sameProdAsProductionDefinedOn = + case e of + | baseExpr(qn) -> qn.name == last(explode(":", top.frame.fullName)) + | _ -> false + end; +} + +function argsTranslation +String ::= e::Decorated AppExprs +{ + -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. + return implode(", ", map((.lazyTranslation), e.exprs)); +} +function namedargsTranslation +String ::= e::Decorated AnnoAppExprs +{ + -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. + return if null(e.exprs) then "null" + else s"new Object[]{${implode(", ", map((.lazyTranslation), reorderedAnnoAppExprs(e)))}}"; +} +function namedargsTranslationNOReorder +String ::= e::Decorated AnnoAppExprs +{ + -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. + return if null(e.exprs) then "null" + else s"new Object[]{${implode(", ", map((.lazyTranslation), e.exprs))}}"; +} + +function int2str String ::= i::Integer { return toString(i); } + +aspect production partialApplication +top::Expr ::= e::PartiallyDecorated Expr es::PartiallyDecorated AppExprs annos::PartiallyDecorated AnnoAppExprs +{ + local step1 :: String = e.translation; + -- Note: we check for nullity of the index lists instead of use + -- isPartial here... Because we may supply ALL values (thus, NOT isPartial!) + -- of one of the param lists, but that means we still need to apply it!! + local step2 :: String = + if !null(es.appExprIndicies) then + step1 ++ ".invokePartial(" ++ + s"new int[]{${implode(", ", map(int2str, es.appExprIndicies))}}, " ++ + s"new Object[]{${argsTranslation(es)}})" + else step1; + local step3 :: String = + if !null(annos.annoIndexConverted) || !null(annos.annoIndexSupplied) then + step2 ++ ".invokeNamedPartial(" ++ + (if null(annos.annoIndexConverted) then "null" + else s"new int[]{${implode(", ", map(int2str, annos.annoIndexConverted))}}") ++ ", " ++ + (if null(annos.annoIndexSupplied) then "null" + else s"new int[]{${implode(", ", map(int2str, annos.annoIndexSupplied))}}") ++ ", " ++ + namedargsTranslationNOReorder(annos) ++ ")" + else step2; + + -- The theory is the `e.translation` we started with has the right type, so we don't need a cast here. In theory. + top.translation = step3; + + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production errorAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); + top.lazyTranslation = top.translation; +} + +aspect production errorDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); + top.lazyTranslation = top.translation; +} + +aspect production forwardAccess +top::Expr ::= e::Expr '.' 'forward' +{ + top.translation = s"((${finalType(top).transType})${e.translation}.forwardOrThis())"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production synDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.translation = wrapAccessWithOT(top, s"${e.translation}.<${finalType(top).transType}>synthesized(${q.attrOccursIndex})"); + + top.lazyTranslation = + case e, top.frame.lazyApplication of + | childReference(cqn), true -> + if isDecorable(cqn.lookupValue.typeScheme.typerep, top.env) + then + s"context.childDecoratedSynthesizedLazy(${top.frame.className}.i_${cqn.lookupValue.fullName}, ${q.attrOccursIndex})" + else + s"context.childAsIsSynthesizedLazy(${top.frame.className}.i_${cqn.lookupValue.fullName}, ${q.attrOccursIndex})" + | lhsReference(_), true -> + s"context.contextSynthesizedLazy(${q.attrOccursIndex})" + | _, _ -> wrapThunk(top.translation, top.frame.lazyApplication) + end; +} + +aspect production inhDecoratedAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + top.translation = wrapAccessWithOT(top, s"${e.translation}.<${finalType(top).transType}>inherited(${q.attrOccursIndex})"); + + top.lazyTranslation = + case e, top.frame.lazyApplication of + | lhsReference(_), true -> s"context.contextInheritedLazy(${q.attrOccursIndex})" + | _, _ -> wrapThunk(top.translation, top.frame.lazyApplication) + end; +} + +aspect production terminalAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + local accessor :: String = + if q.name == "lexeme" || q.name == "location" + then q.name + else if q.name == "line" + then "getLine()" + else if q.name == "column" + then "getColumn()" + else if q.name == "filename" + then "getFilename()" + else error("Not possible -- an error should have been raised about " ++ q.unparse); + + top.translation = s"((${finalType(top).transType})${e.translation}.${accessor})"; + + top.lazyTranslation = top.translation; +} + +aspect production annoAccessHandler +top::Expr ::= e::PartiallyDecorated Expr q::PartiallyDecorated QNameAttrOccur +{ + -- Note that the transType is specific to the nonterminal we're accessing from. + top.translation = s"((${finalType(top).transType})((${makeAnnoName(q.attrDcl.fullName)})${e.translation}).getAnno_${makeIdName(q.attrDcl.fullName)}())"; + + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + + +aspect production decorateExprWith +top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' +{ + top.translation = s"((common.Decorable)${e.translation})" ++ + case inh of + | exprInhsEmpty() -> ".decorate(context, (common.Lazy[])null)" + -- Note: we don't NEED to pass context here, but it's good for error messages! + -- When the user forgets to provide inherited attributes + -- (especially important because we're implicitly inserted when accessing attributes + -- from undecorated nodes, and this is a common error for new silverers.) + | _ -> ".decorate(context, common.Util.populateInh(" ++ + case finalType(e) of + -- Don't know the actual number of attributes for skolems with occurs-on contexts, + -- fall back to using the max index. + | skolemType(_) -> foldr1(\ i1::String i2::String -> s"Math.max(${i1}, ${i2})", inh.nameTrans) ++ " + 1" + | t -> s"${makeNTName(t.typeName)}.num_inh_attrs" + end ++ ", " ++ + s"new int[]{${implode(", ", inh.nameTrans)}}, " ++ + s"new common.Lazy[]{${implode(", ", inh.valueTrans)}}))" + end; + + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +synthesized attribute nameTrans :: [String]; +synthesized attribute valueTrans :: [String]; + +attribute nameTrans occurs on ExprInhs, ExprInh, ExprLHSExpr; +attribute valueTrans occurs on ExprInhs, ExprInh; + +aspect production exprInh +top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' +{ + top.nameTrans = lhs.nameTrans; + top.valueTrans = [wrapLazy(e)]; -- TODO: this is another appearance of the nested lazy problem... +} + +aspect production exprInhsEmpty +top::ExprInhs ::= +{ + top.nameTrans = []; + top.valueTrans = []; +} + +aspect production exprInhsOne +top::ExprInhs ::= lhs::ExprInh +{ + top.nameTrans = lhs.nameTrans; + top.valueTrans = lhs.valueTrans; +} + +aspect production exprInhsCons +top::ExprInhs ::= lhs::ExprInh inh::ExprInhs +{ + top.nameTrans = lhs.nameTrans ++ inh.nameTrans; + top.valueTrans = lhs.valueTrans ++ inh.valueTrans; +} + + +aspect production exprLhsExpr +top::ExprLHSExpr ::= q::QNameAttrOccur +{ + top.nameTrans = [q.attrOccursIndex]; +} + + +aspect production trueConst +top::Expr ::='true' +{ + top.translation = "true"; + top.lazyTranslation = top.translation; +} + +aspect production falseConst +top::Expr ::= 'false' +{ + top.translation = "false"; + top.lazyTranslation = top.translation; +} + +aspect production and +top::Expr ::= e1::Expr '&&' e2::Expr +{ + top.translation = s"(${e1.translation} && ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production or +top::Expr ::= e1::Expr '||' e2::Expr +{ + top.translation = s"(${e1.translation} || ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production notOp +top::Expr ::= '!' e::Expr +{ + top.translation = s"(!${e.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production ifThenElse +top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr +{ + {- + We need to cast the else branch to the correct type, as otherwise + Java tries to cast it to the type of the then branch, which + doesn't always work. + -} + top.translation = s"(${e1.translation} ? ${e2.translation} : (${finalType(top).transType})${e3.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production intConst +top::Expr ::= i::Int_t +{ + top.translation = s"((int)${i.lexeme})"; + top.lazyTranslation = top.translation; +} + +aspect production floatConst +top::Expr ::= f::Float_t +{ + top.translation = s"((float)${f.lexeme})"; + top.lazyTranslation = top.translation; +} + +aspect production noteAttachment +top::Expr ::= 'attachNote' note::Expr 'on' e::Expr 'end' +{ + top.translation = e.translation; + top.lazyTranslation = e.lazyTranslation; +} + +aspect production plus +top::Expr ::= e1::Expr '+' e2::Expr +{ + top.translation = s"(${e1.translation} + ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} +aspect production minus +top::Expr ::= e1::Expr '-' e2::Expr +{ + top.translation = s"(${e1.translation} - ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} +aspect production multiply +top::Expr ::= e1::Expr '*' e2::Expr +{ + top.translation = s"(${e1.translation} * ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} +aspect production divide +top::Expr ::= e1::Expr _ e2::Expr +{ + top.translation = s"(${e1.translation} / ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} +aspect production modulus +top::Expr ::= e1::Expr '%' e2::Expr +{ + top.translation = s"(${e1.translation} % ${e2.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} +aspect production neg +top::Expr ::= '-' e::Expr +{ + top.translation = s"(-${e.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production terminalConstructor +top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' +{ + top.translation = s"new ${makeTerminalName(t.typerep.typeName)}(${es.translation}, (silver.core.NLocation)${el.translation})"; + top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); +} + +aspect production stringConst +top::Expr ::= s::String_t +{ + top.translation = s"(new common.StringCatter(${s.lexeme}))"; + top.lazyTranslation = top.translation; +} + +aspect production exprsEmpty +top::Exprs ::= +{ + top.lazyTranslation = ""; +} + +aspect production exprsSingle +top::Exprs ::= e::Expr +{ + top.lazyTranslation = e.lazyTranslation; +} + +aspect production exprsCons +top::Exprs ::= e1::Expr ',' e2::Exprs +{ + top.lazyTranslation = e1.lazyTranslation ++ ", " ++ e2.lazyTranslation; +} + +aspect production exprRef +top::Expr ::= e::PartiallyDecorated Expr +{ + top.translation = e.translation; + top.lazyTranslation = e.lazyTranslation; +} + + +function wrapThunk +String ::= exp::String beLazy::Boolean +{ + return if beLazy then wrapThunkText(exp, "Object") else exp; +} +function wrapThunkText +String ::= exp::String ty::String +{ + return s"new common.Thunk<${ty}>(new common.Thunk.Evaluable<${ty}>() { public final ${ty} eval() { return ${exp}; } })"; + --TODO: java lambdas are bugged + --return s"new common.Thunk<${ty}>(() -> ${exp})"; +} +function wrapLazy +String ::= e::Decorated Expr +{ + -- It *may* be wise to leave `Lazy`s as anon classes, rather than lambdas. + -- This splits all the Thunk methods across each `Lazy` instead of concentrating + -- them all on the top-level class, like `Init` + -- We're *unlikely* to be close to hitting the 64K method limit, but + -- we have hit the 64K bytecode limit in the past, which is why `Init` farms + -- initialization code out across each production. So who knows. + local swizzleOrigins::String = if e.config.noOrigins then "" else "final common.OriginContext originCtx = context.originCtx;"; + return s"new common.Lazy() { public final Object eval(final common.DecoratedNode context) { ${swizzleOrigins} return ${e.translation}; } }"; +} diff --git a/grammars/silver/compiler/translation/java/core/FunctionDcl.sv b/grammars/silver/compiler/translation/java/core/FunctionDcl.sv new file mode 100644 index 000000000..be617479e --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/FunctionDcl.sv @@ -0,0 +1,247 @@ +grammar silver:compiler:translation:java:core; + +import silver:compiler:modification:ffi only ioForeignType; -- for main type check only +import silver:compiler:modification:list only listCtrType; + +aspect production functionDcl +top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody +{ + top.setupInh := body.setupInh; + top.initProd := s"\t\t//FUNCTION ${id.name} ${ns.unparse}\n" ++ body.translation; + + local localVar :: String = "count_local__ON__" ++ makeIdName(fName); + + top.initWeaving := s"\tpublic static int ${localVar} = 0;\n"; + top.valueWeaving := body.valueWeaving; + + local argsAccess :: String = + implode(", ", map(\ c::Context -> decorate c with {boundVariables = namedSig.freeVariables;}.contextRefElem, namedSig.contexts) ++ map((.childRefElem), namedSig.inputElements)); + + local funBody :: String = +s""" final common.DecoratedNode context = new P${id.name}(${argsAccess}).decorate(originCtx); + //${head(body.uniqueSignificantExpression).unparse} + return (${namedSig.outputElement.typerep.transType})(${head(body.uniqueSignificantExpression).translation}); +"""; + + top.genFiles := + [pair(s"P${id.name}.java", generateFunctionClassString(top.grammarName, id.name, namedSig, funBody))] ++ + if id.name == "main" + then [pair("Main.java", generateMainClassString(top.grammarName, !typeIOValFailed))] -- !typeIOValFailed true if main type used was IOVal + else []; + + -- For main functions which return IOVal + local attribute typeIOValFailed::Boolean = unify(namedSig.typerep, + appTypes( + functionType(2, []), + [appType(listCtrType(), stringType()), + ioForeignType, + appType(nonterminalType("silver:core:IOVal", [starKind()], false), intType())])).failure; + + -- For main functions which return IO + local attribute typeIOMonadFailed::Boolean = unify(namedSig.typerep, + appTypes( + functionType(1, []), + [appType(listCtrType(), stringType()), + appType(nonterminalType("silver:core:IO", [starKind()], false), intType())])).failure; + + -- main function signature check TODO: this should probably be elsewhere! + top.errors <- + if id.name == "main" && typeIOValFailed && typeIOMonadFailed -- Neither legal main function type used + then [err(top.location, "main function must have type signature (IOVal ::= [String] IOToken) " ++ + "or (IO ::= [String]). Instead it has type " ++ prettyType(namedSig.typerep))] + else []; +} + +function generateFunctionClassString +String ::= whatGrammar::String whatName::String whatSig::NamedSignature whatResult::String +{ + local className :: String = "P" ++ whatName; + + local localVar :: String = + s"count_local__ON__${makeIdName(whatGrammar)}_${whatName}"; + + local commaIfArgs :: String = if length(whatSig.contexts) + length(whatSig.inputElements) != 0 then "," else ""; + + local contexts::Contexts = foldContexts(whatSig.contexts); + contexts.boundVariables = whatSig.freeVariables; + + return s""" +package ${makeName(whatGrammar)}; + +import silver.core.NOriginInfo; + +public final class ${className} extends common.FunctionNode { + +${makeIndexDcls(0, whatSig.inputElements)} + + public static final int num_local_attrs = Init.${localVar}; + public static final String[] occurs_local = new String[num_local_attrs]; + + public static final common.Lazy[][] childInheritedAttributes = new common.Lazy[${toString(length(whatSig.inputElements))}][]; + + public static final common.Lazy[] localAttributes = new common.Lazy[num_local_attrs]; + public static final common.Lazy[][] localInheritedAttributes = new common.Lazy[num_local_attrs][]; + +${whatSig.inhOccursIndexDecls} + + public static final int[] childInhContextTypeVars = {${implode(",", whatSig.childTypeVarElems)}}; + public static final int[] localInhContextTypeVars = new int[num_local_attrs]; + + static { +${whatSig.childStatic} + } + + public ${className}(${whatSig.javaSignature}) { +${implode("", map(makeChildAssign, whatSig.inputElements))} +${contexts.contextInitTrans} + } + +${whatSig.childDecls} + +${contexts.contextMemberDeclTrans} + + @Override + public Object getChild(final int index) { + switch(index) { +${implode("", map(makeChildAccessCase, whatSig.inputElements))} + default: return null; + } + } + + @Override + public Object getChildLazy(final int index) { + switch(index) { +${implode("", map(makeChildAccessCaseLazy, whatSig.inputElements))} + default: return null; + } + } + + @Override + public final int getNumberOfChildren() { + return ${toString(length(whatSig.inputElements))}; + } + + @Override + public common.Lazy[] getLocalInheritedAttributes(final int key) { +${flatMap(makeInhOccursContextAccess(whatSig.freeVariables, whatSig.contextInhOccurs, "localInhContextTypeVars", "localInheritedAttributes", _), whatSig.inhOccursContextTypes)} + return localInheritedAttributes[key]; + } + + @Override + public common.Lazy[] getChildInheritedAttributes(final int key) { +${flatMap(makeInhOccursContextAccess(whatSig.freeVariables, whatSig.contextInhOccurs, "childInhContextTypeVars", "childInheritedAttributes", _), whatSig.inhOccursContextTypes)} + return childInheritedAttributes[key]; + } + + @Override + public common.Lazy getLocal(final int key) { + return localAttributes[key]; + } + + @Override + public final int getNumberOfLocalAttrs() { + return num_local_attrs; + } + + @Override + public final String getNameOfLocalAttr(final int index) { + return occurs_local[index]; + } + + @Override + public String getName() { + return "${whatSig.fullName}"; + } + + public static ${whatSig.outputElement.typerep.transType} invoke(final common.OriginContext originCtx ${commaIfArgs} ${whatSig.javaSignature}) { + try { +${whatResult} + } catch(Throwable t) { + throw new common.exceptions.TraceException("Error while evaluating function ${whatSig.fullName}", t); + } + } + +${if null(whatSig.contexts) -- Can only use a singleton when there aren't contexts. + then s""" + public static final common.NodeFactory<${whatSig.outputElement.typerep.transCovariantType}> factory = new Factory(); +""" else s""" + public static final common.NodeFactory<${whatSig.outputElement.typerep.transCovariantType}> getFactory(${contexts.contextParamTrans}) { + return new Factory(${implode(", ", map(\ c::Context -> decorate c with {boundVariables = whatSig.freeVariables;}.contextRefElem, whatSig.contexts))}); + } +"""} + + public static final class Factory extends common.NodeFactory<${whatSig.outputElement.typerep.transType}> { +${contexts.contextMemberDeclTrans} + + public Factory(${contexts.contextParamTrans}) { +${contexts.contextInitTrans} + } + + @Override + public final ${whatSig.outputElement.typerep.transType} invoke(final common.OriginContext originCtx, final Object[] children, final Object[] namedNotApplicable) { + return ${className}.invoke(${implode(", ", ["originCtx"] ++ map(\ c::Context -> decorate c with {boundVariables = whatSig.freeVariables;}.contextRefElem, whatSig.contexts) ++ unpackChildren(0, whatSig.inputElements))}); + } + + @Override + public final common.AppTypeRep getType() { +${makeTyVarDecls(3, whatSig.typerep.freeVariables)} + return ${transFreshTypeRep(whatSig.typerep)}; + } + + @Override + public final String toString() { + return "${whatGrammar}:${whatName}"; + } + }; +}"""; +} + +function generateMainClassString +String ::= whatGrammar::String isIOValReturn::Boolean +{ + local attribute package :: String; + package = makeName(whatGrammar); + + -- Code used if main function return type is IOVal + local attribute invocationIOVal::String = package ++ + ".Pmain.invoke(common.OriginContext.ENTRY_CONTEXT, cvargs(args), common.IOToken.singleton)"; + + -- Code used if main function return type is IO + local attribute invokationEvalIO::String = + "silver.core.PevalIO.invoke(common.OriginContext.ENTRY_CONTEXT, " ++ package ++ + ".Pmain.invoke(common.OriginContext.ENTRY_CONTEXT, cvargs(args)), common.IOToken.singleton)"; + + return s""" +package ${package}; + +import silver.core.*; + +public class Main { + public static void main(String[] args) { + common.Util.init(); + ${package}.Init.initAllStatics(); + ${package}.Init.init(); + ${package}.Init.postInit(); + + try { + common.Node rv = (common.Node) ${if isIOValReturn then invocationIOVal else invokationEvalIO}; + common.DecoratedNode drv = rv.decorate(common.TopNode.singleton, (common.Lazy[])null); + drv.synthesized(silver.core.Init.silver_core_io__ON__silver_core_IOVal); // demand the io token + System.exit( (Integer)drv.synthesized(silver.core.Init.silver_core_iovalue__ON__silver_core_IOVal) ); + } catch(Throwable t) { + Throwable rt = common.exceptions.SilverException.getRootCause(t); + if(rt instanceof common.exceptions.SilverExit) + System.exit(((common.exceptions.SilverExit)rt).getExitCode()); + common.Util.printStackCauses(t); + } + } + public static common.ConsCell cvargs(String[] args) { + common.ConsCell result = common.ConsCell.nil; + for(int i = args.length - 1; i >= 0; i--) { + result = new common.ConsCell(new common.StringCatter(args[i]), result); + } + return result; + } +}"""; +} + diff --git a/grammars/silver/compiler/translation/java/core/GlobalDcl.sv b/grammars/silver/compiler/translation/java/core/GlobalDcl.sv new file mode 100644 index 000000000..638f637c9 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/GlobalDcl.sv @@ -0,0 +1,14 @@ +grammar silver:compiler:translation:java:core; + +aspect production globalValueDclConcrete +top::AGDcl ::= 'global' id::Name '::' cl::ConstraintList '=>' t::TypeExpr '=' e::Expr ';' +{ + top.initValues := + if null(cl.contexts) then + s"\tpublic static final common.Thunk<${t.typerep.transCovariantType}> global_${id.name} = ${wrapThunkText(e.generalizedTranslation, t.typerep.transType)};\n" + else s""" + public static final common.Thunk<${t.typerep.transCovariantType}> global_${id.name}(${implode(", ", map(\ c::Context -> decorate c with {boundVariables = boundVars;}.contextSigElem, cl.contexts))}){ + return ${wrapThunkText(e.generalizedTranslation, t.typerep.transType)}; + } +"""; +} diff --git a/grammars/silver/compiler/translation/java/core/InstanceDcl.sv b/grammars/silver/compiler/translation/java/core/InstanceDcl.sv new file mode 100644 index 000000000..72fd8517c --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/InstanceDcl.sv @@ -0,0 +1,83 @@ +grammar silver:compiler:translation:java:core; + +aspect production instanceDcl +top::AGDcl ::= 'instance' cl::ConstraintList '=>' id::QNameType ty::TypeExpr '{' body::InstanceBody '}' +{ + local className :: String = "I" ++ substitute(":", "_", fName) ++ "_" ++ transTypeName(ty.typerep); + + local contexts::Contexts = foldContexts(cl.contexts); + contexts.boundVariables = boundVars; + + top.genFiles := if contexts.isTypeError then [] else [pair(className ++ ".java", s""" + +package ${makeName(top.grammarName)}; + +public class ${className} implements ${makeClassName(fName)} { + final static common.DecoratedNode context = common.TopNode.singleton; // For decoration in member bodies + + public ${className}(${contexts.contextParamTrans}) { +${contexts.contextInitTrans} + } + +${contexts.contextMemberDeclTrans} +${superContexts.transContextSuperAccessors} + +${body.translation} + +} +""")]; +} + +synthesized attribute contextMemberDeclTrans::String occurs on Contexts, Context; +synthesized attribute contextParamTrans::String occurs on Contexts, Context; +synthesized attribute contextInitTrans::String occurs on Contexts, Context; + +aspect production consContext +top::Contexts ::= h::Context t::Contexts +{ + top.contextMemberDeclTrans = h.contextMemberDeclTrans ++ t.contextMemberDeclTrans; + top.contextParamTrans = h.contextParamTrans ++ case t of nilContext() -> "" | _ -> ", " ++ t.contextParamTrans end; + top.contextInitTrans = h.contextInitTrans ++ t.contextInitTrans; +} +aspect production nilContext +top::Contexts ::= +{ + top.contextMemberDeclTrans = ""; + top.contextParamTrans = ""; + top.contextInitTrans = ""; +} + +aspect default production +top::Context ::= +{ + top.contextMemberDeclTrans = s"\tpublic final ${top.transType} ${top.transContextMemberName};\n"; + top.contextParamTrans = s"${top.transType} ${top.transContextMemberName}"; + top.contextInitTrans = s"\t\tthis.${top.transContextMemberName} = ${top.transContextMemberName};\n"; +} + +attribute translation occurs on InstanceBody, InstanceBodyItem; + +aspect production consInstanceBody +top::InstanceBody ::= h::InstanceBodyItem t::InstanceBody +{ + top.translation = h.translation ++ t.translation; +} +aspect production nilInstanceBody +top::InstanceBody ::= +{ + top.translation = ""; +} + +aspect production instanceBodyItem +top::InstanceBodyItem ::= id::QName '=' e::Expr ';' +{ + local contexts::Contexts = foldContexts(memberContexts); + contexts.boundVariables = boundVars; + + top.translation = s""" + public ${finalType(e).transType} ${makeInstanceMemberAccessorName(top.fullName)}(${contexts.contextParamTrans}) { + //${e.unparse} + return ${e.generalizedTranslation}; + } +"""; +} diff --git a/grammars/silver/compiler/translation/java/core/NamedSignature.sv b/grammars/silver/compiler/translation/java/core/NamedSignature.sv new file mode 100644 index 000000000..70371d285 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/NamedSignature.sv @@ -0,0 +1,421 @@ +grammar silver:compiler:translation:java:core; + +{-- + - The java translation of the *input parameters* signature. + -} +synthesized attribute javaSignature :: String occurs on NamedSignature; +synthesized attribute refInvokeTrans :: String occurs on NamedSignature; +-- "final ContextName d_contextname" +synthesized attribute contextSigElems :: [String] occurs on Contexts; +synthesized attribute contextSigElem :: String occurs on Context; +-- Track what children with inh occurs-on contexts need to have indices generated +monoid attribute contextInhOccurs :: [(Type, String)] occurs on NamedSignature, Contexts, Context; +autocopy attribute sigInhOccurs :: [(Type, String)] occurs on NamedSignatureElements, NamedSignatureElement; +synthesized attribute inhOccursContextTypes :: [Type] occurs on NamedSignature; +monoid attribute inhOccursIndexDecls :: String occurs on NamedSignature, Contexts, Context; +-- Track what children can be used to resolve contexts at runtime +monoid attribute typeChildren :: [(Type, String)] occurs on NamedSignatureElements, NamedSignatureElement; +inherited attribute typeChildrenIn :: [(Type, String)] occurs on Contexts, Context; +monoid attribute contextRuntimeResolve :: String occurs on NamedSignature, Contexts, Context; +synthesized attribute contextRuntimeResolveFailure :: String occurs on Context; +-- "final Object c_signame" +synthesized attribute childSigElems :: [String] occurs on NamedSignatureElements; +synthesized attribute annoSigElems :: [String] occurs on NamedSignatureElements; +synthesized attribute childSigElem :: String occurs on NamedSignatureElement; +synthesized attribute annoSigElem :: String occurs on NamedSignatureElement; +-- "d_contextname" +synthesized attribute contextRefElems :: [String] occurs on Contexts; +synthesized attribute contextRefElem :: String occurs on Context; +-- "c_signame" +synthesized attribute childRefElems :: [String] occurs on NamedSignatureElements; +synthesized attribute childRefElem :: String occurs on NamedSignatureElement; +synthesized attribute annoRefElems :: [String] occurs on NamedSignatureElements; +synthesized attribute annoRefElem :: String occurs on NamedSignatureElement; +-- "type_a" or "-1" +synthesized attribute childTypeVarElems :: [String] occurs on NamedSignature, NamedSignatureElements; +synthesized attribute childTypeVarElem :: String occurs on NamedSignatureElement; +-- "inhs[c_signame] = new lazy[]" +synthesized attribute childStaticElem :: String occurs on NamedSignatureElement; +synthesized attribute childStatic :: String occurs on NamedSignature, NamedSignatureElements; +-- "private Object child_signame..." +synthesized attribute childDeclElem :: String occurs on NamedSignatureElement; +synthesized attribute annoDeclElem :: String occurs on NamedSignatureElement; +synthesized attribute childDecls :: String occurs on NamedSignature, NamedSignatureElements; +-- "signame" +synthesized attribute annoNameElem :: String occurs on NamedSignatureElement; +-- "if (name.equals("signame")) { return getAnno_signame(); }" +synthesized attribute annoLookupElem :: String occurs on NamedSignatureElement; + +aspect production namedSignature +top::NamedSignature ::= fn::String ctxs::Contexts ie::NamedSignatureElements oe::NamedSignatureElement np::NamedSignatureElements +{ + top.javaSignature = implode(", ", ctxs.contextSigElems ++ ie.childSigElems ++ np.annoSigElems); + top.refInvokeTrans = implode(", ", ctxs.contextRefElems ++ ie.childRefElems ++ np.annoRefElems); + top.childTypeVarElems = ie.childTypeVarElems; + top.childStatic = ie.childStatic; + top.childDecls = ie.childDecls; + + top.inhOccursContextTypes = nubBy(typeNameEq, map(fst, ctxs.contextInhOccurs)); + top.inhOccursIndexDecls := + flatMap( + \ t::Type -> s"\tpublic static final int type_${transTypeNameWith(t, top.freeVariables)} = ${toString(positionOfBy(typeNameEq, t, top.inhOccursContextTypes))};\n", + top.inhOccursContextTypes) ++ + flatMap( + \ t::Type -> s"\tpublic static int count_inh__ON__${transTypeNameWith(t, top.freeVariables)} = 0;\n", + top.inhOccursContextTypes) ++ + ctxs.inhOccursIndexDecls; + top.contextInhOccurs := ctxs.contextInhOccurs; + ie.sigInhOccurs = ctxs.contextInhOccurs; + + ctxs.typeChildrenIn = ie.typeChildren; + propagate contextRuntimeResolve; +} + +aspect production globalSignature +top::NamedSignature ::= fn::String ctxs::Contexts ty::Type +{ + top.javaSignature = error("Translation shouldn't be demanded from global signature"); + top.refInvokeTrans = error("Translation shouldn't be demanded from global signature"); + top.contextRuntimeResolve := error("Translation shouldn't be demanded from global signature"); + top.childTypeVarElems = error("Translation shouldn't be demanded from global signature"); + top.childStatic = error("Translation shouldn't be demanded from global signature"); + top.childDecls = error("Translation shouldn't be demanded from global signature"); + + top.inhOccursContextTypes = error("Translation shouldn't be demanded from global signature"); + top.inhOccursIndexDecls := error("Translation shouldn't be demanded from global signature"); + top.contextInhOccurs := error("Translation shouldn't be demanded from global signature"); +} + +propagate contextInhOccurs, inhOccursIndexDecls on Contexts, Context; +propagate typeChildrenIn, contextRuntimeResolve on Contexts; + +aspect production consContext +top::Contexts ::= h::Context t::Contexts +{ + top.contextSigElems = h.contextSigElem :: t.contextSigElems; + top.contextRefElems = h.contextRefElem :: t.contextRefElems; +} +aspect production nilContext +top::Contexts ::= +{ + top.contextSigElems = []; + top.contextRefElems = []; +} + +aspect default production +top::Context ::= +{ + top.contextRuntimeResolveFailure = s""" + if (true) throw new common.exceptions.SilverError("Can't construct production " + getName() + " because context ${prettyContext(top)} cannot be resolved at runtime"); + final ${top.transType} ${top.transContextMemberName} = ${top.transContextDummyInit}; +"""; + top.contextRuntimeResolve := top.contextRuntimeResolveFailure; +} + +aspect production instContext +top::Context ::= fn::String t::Type +{ + top.contextSigElem = s"final ${top.transType} ${makeConstraintDictName(fn, t, top.boundVariables)}"; + top.contextRefElem = makeConstraintDictName(fn, t, top.boundVariables); +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextSigElem = s"final int ${makeConstraintDictName(attr, ntty, top.boundVariables)}"; + top.contextRefElem = makeConstraintDictName(attr, ntty, top.boundVariables); + top.contextInhOccurs <- [(ntty, attr)]; + top.inhOccursIndexDecls <- + s"\tpublic static final int ${makeIdName(attr)}__ON__${ntty.transTypeName} = count_inh__ON__${ntty.transTypeName}++;\n"; + top.contextRuntimeResolve := + case lookupBy(typeNameEq, ntty, top.typeChildrenIn) of + | just(child) -> s""" + final common.RTTIManager.Nonterminalton ${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt = common.RTTIManager.getNonterminalton(common.Reflection.getType(${child}).typeName()); + if (${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt == null) { + throw new common.exceptions.SilverError(common.Reflection.getType(${child}) + " is not a nonterminal."); + } + final int ${makeConstraintDictName(attr, ntty, top.boundVariables)} = ${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt.getOccursIndex("${attr}"); +""" + | nothing() -> top.contextRuntimeResolveFailure + end; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.contextSigElem = s"final int ${makeConstraintDictName(attr, ntty, top.boundVariables)}"; + top.contextRefElem = makeConstraintDictName(attr, ntty, top.boundVariables); + top.contextRuntimeResolve := + case lookupBy(typeNameEq, ntty, top.typeChildrenIn) of + | just(child) -> s""" + final common.RTTIManager.Nonterminalton ${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt = common.RTTIManager.getNonterminalton(common.Reflection.getType(${child}).typeName()); + if (${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt == null) { + throw new common.exceptions.SilverError(common.Reflection.getType(${child}) + " is not a nonterminal."); + } + final int ${makeConstraintDictName(attr, ntty, top.boundVariables)} = ${makeConstraintDictName(attr, ntty, top.boundVariables)}_nt.getOccursIndex("${attr}"); +""" + | nothing() -> top.contextRuntimeResolveFailure + end; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.contextSigElem = s"final ${top.transType} ${makeConstraintDictName(attr, ntty, top.boundVariables)}"; + top.contextRefElem = makeConstraintDictName(attr, ntty, top.boundVariables); + top.contextRuntimeResolve := + case lookupBy(typeNameEq, ntty, top.typeChildrenIn) of + | just(child) -> s""" + if (!${child} instanceof makeAnnoName(${attr})) { + throw new common.exceptions.SilverError(common.Reflection.getType(${child}) + " does not have annotation ${attr}."); + } + final ${top.transType} ${makeConstraintDictName(attr, ntty, top.boundVariables)} = null; +""" + | nothing() -> top.contextRuntimeResolveFailure + end; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + top.contextSigElem = s"final ${top.transType} ${makeTypeableName(t, top.boundVariables)}"; + top.contextRefElem = makeTypeableName(t, top.boundVariables); + top.contextRuntimeResolve := + case lookupBy(typeNameEq, t, top.typeChildrenIn) of + | just(child) -> s"\t\t\tfinal ${top.transType} ${makeTypeableName(t, top.boundVariables)} = common.Reflection.getType(${child});\n" + | nothing() -> top.contextRuntimeResolveFailure + end; +} + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + top.contextSigElem = s"final ${top.transType} ${makeInhSubsetName(i1, i2, top.boundVariables)}"; + top.contextRefElem = makeInhSubsetName(i1, i2, top.boundVariables); + top.contextRuntimeResolve := s"final ${top.transType} ${makeInhSubsetName(i1, i2, top.boundVariables)} = null;"; +} + +aspect production typeErrorContext +top::Context ::= _ +{ + top.contextSigElem = error("Translation demanded in the presence of errors"); + top.contextRefElem = error("Translation demanded in the presence of errors"); + top.contextRuntimeResolve := error("Translation demanded in the presence of errors"); +} + +propagate typeChildren on NamedSignatureElements; + +aspect production consNamedSignatureElement +top::NamedSignatureElements ::= h::NamedSignatureElement t::NamedSignatureElements +{ + top.childSigElems = h.childSigElem :: t.childSigElems; + top.annoSigElems = h.annoSigElem :: t.annoSigElems; + top.childRefElems = h.childRefElem :: t.childRefElems; + top.annoRefElems = h.annoRefElem :: t.annoRefElems; + top.childTypeVarElems = h.childTypeVarElem :: t.childTypeVarElems; + top.childStatic = h.childStaticElem ++ t.childStatic; + top.childDecls = h.childDeclElem ++ t.childDecls; +} + +aspect production nilNamedSignatureElement +top::NamedSignatureElements ::= +{ + top.childSigElems = []; + top.annoSigElems = []; + top.childRefElems = []; + top.annoRefElems = []; + top.childTypeVarElems = []; + top.childStatic = ""; + top.childDecls = ""; +} + + +-- TODO: It'd be nice to maybe split these into the ordered parameters and the annotations +aspect production namedSignatureElement +top::NamedSignatureElement ::= n::String ty::Type +{ + top.childSigElem = "final Object c_" ++ n; + top.childRefElem = "c_" ++ n; + top.childDeclElem = +s"""private Object child_${n}; + public final ${ty.transType} getChild_${n}() { + final ${ty.transType} result = common.Util.<${ty.transType}>demand(child_${n}); + child_${n} = result; + return result; + } +"""; + + local ntType::Type = if ty.isDecorated then ty.decoratedType else ty; + ntType.boundVariables = ty.boundVariables; + + top.childTypeVarElem = + if lookupBy(typeNameEq, ty, top.sigInhOccurs).isJust + then s"type_${ntType.transTypeName}" + else "-1"; + + top.childStaticElem = + if lookupBy(typeNameEq, ty, top.sigInhOccurs).isJust + then s"\t\tchildInheritedAttributes[i_${n}] = new common.Lazy[count_inh__ON__${ntType.transTypeName}];\n" + else if ty.isNonterminal || ty.isPartiallyDecorated && ntType.isNonterminal + then s"\t\tchildInheritedAttributes[i_${n}] = new common.Lazy[${makeNTName(ntType.typeName)}.num_inh_attrs];\n" + else ""; + + top.typeChildren := [(ty, top.childRefElem)]; + + -- annos are full names: + + local fn :: String = makeIdName(n); + + top.annoSigElem = "final Object a_" ++ fn; + top.annoRefElem = "a_" ++ fn; + top.annoDeclElem = +s""" protected Object anno_${fn}; + @Override + public final ${ty.transType} getAnno_${fn}() { + final ${ty.transType} result = common.Util.<${ty.transType}>demand(anno_${fn}); + anno_${fn} = result; + return result; + } + +"""; + + top.annoNameElem = s"\"${n}\""; + top.annoLookupElem = +s"""if (name.equals("${n}")) { + return getAnno_${fn}(); + } else """; +} + +function makeIndexDcls +String ::= i::Integer s::[NamedSignatureElement] +{ + return if null(s) then "" + else s"\tpublic static final int i_${head(s).elementName} = ${toString(i)};\n" ++ makeIndexDcls(i+1, tail(s)); +} + +function unpackChildren +[String] ::= i::Integer ns::[NamedSignatureElement] +{ + return if null(ns) then [] + else (s"children[${toString(i)}]") :: unpackChildren(i + 1, tail(ns)); +} +function unpackAnnotations +[String] ::= i::Integer ns::[NamedSignatureElement] +{ + return if null(ns) then [] + else (s"annotations[${toString(i)}]") :: unpackAnnotations(i + 1, tail(ns)); +} + +function makeChildAccessCase +String ::= n::NamedSignatureElement +{ + return s"\t\t\tcase i_${n.elementName}: return getChild_${n.elementName}();\n"; +} +function makeChildAccessCaseLazy +String ::= n::NamedSignatureElement +{ + return s"\t\t\tcase i_${n.elementName}: return child_${n.elementName};\n"; +} + +function makeAnnoAssign +String ::= n::NamedSignatureElement +{ + local fn :: String = makeIdName(n.elementName); + return s"\t\tthis.anno_${fn} = a_${fn};\n"; +} +function makeChildAssign +String ::= n::NamedSignatureElement +{ + return s"\t\tthis.child_${n.elementName} = c_${n.elementName};\n"; +} + +function makeInhOccursContextAccess +String ::= bv::[TyVar] sigInhOccurs::[(Type, String)] typeVarArray::String inhArray::String t::Type +{ + t.boundVariables = bv; + local inhs::[String] = lookupAllBy(typeNameEq, t, sigInhOccurs); + return s""" if (${typeVarArray}[key] == type_${t.transTypeName}) { + common.Lazy[] res = new common.Lazy[${foldr1(\ i1::String i2::String -> s"Math.max(${i1}, ${i2})", map(makeConstraintDictName(_, t, bv), inhs))} + 1]; +${flatMap(\ inh::String -> s"\t\t\tres[${makeConstraintDictName(inh, t, bv)}] = ${inhArray}[key][${makeIdName(inh)}__ON__${t.transTypeName}];\n", inhs)} + return res; + } +"""; +} + +function makeTyVarDecls +String ::= indent::Integer vars::[TyVar] +{ + return + implode( + "\n", + map( + \ tv::TyVar -> + s"${concat(repeat("\t", indent))}common.VarTypeRep freshTypeVar_${toString(tv.extractTyVarRep)} = new common.VarTypeRep();", + vars)); + +} +function makeAnnoIndexDcls +String ::= i::Integer s::[NamedSignatureElement] +{ + return if null(s) then "" + else s"\t\tfinal int i${head(s).annoRefElem} = ${toString(i)};\n" ++ makeAnnoIndexDcls(i+1, tail(s)); +} +function makeChildUnify +String ::= fn::String n::NamedSignatureElement +{ + return +s"""try { + if (!common.TypeRep.unify(${transFreshTypeRep(n.typerep)}, common.Reflection.getType(getChild_${n.elementName}()))) { + throw new common.exceptions.SilverInternalError("Unification failed."); + } + } catch (common.exceptions.SilverException e) { + throw new common.exceptions.TraceException("While constructing type of child '${n.elementName}' of production '${fn}'", e); + } +"""; +} +function makeChildReify +String ::= fn::String numChildren::Integer n::NamedSignatureElement +{ + return +s"""Object ${n.childRefElem} = null; + try { + ${n.childRefElem} = common.Reflection.reify(rules, ${transFreshTypeRep(n.typerep)}, childASTs[i_${n.elementName}]); + } catch (common.exceptions.SilverException e) { + throw new common.exceptions.ChildReifyTraceException("${fn}", "${n.elementName}", ${toString(numChildren)}, i_${n.elementName}, e); + } +"""; +} +function makeAnnoReify +String ::= fn::String n::NamedSignatureElement +{ + return +s"""Object ${n.annoRefElem} = null; + try { + ${n.annoRefElem} = common.Reflection.reify(rules, ${transFreshTypeRep(n.typerep)}, annotationASTs[i${n.annoRefElem}]); + } catch (common.exceptions.SilverException e) { + throw new common.exceptions.AnnotationReifyTraceException("${fn}", "${n.elementName}", e); + } +"""; +} + +function makeConstructDirectChildren +String ::= x::NamedSignatureElement +{ + return +s"""Object ${x.childRefElem} = children[counter]; + counter++; +"""; +} +function makeConstructDirectAnno +String ::= x::NamedSignatureElement +{ + return +s"""Object ${x.annoRefElem} = annos[counter]; + counter++; +"""; +} + +function typeNameEq +Boolean ::= t1::Type t2::Type +{ return t1.typeName == t2.typeName; } + diff --git a/grammars/silver/compiler/translation/java/core/NonTerminalDcl.sv b/grammars/silver/compiler/translation/java/core/NonTerminalDcl.sv new file mode 100644 index 000000000..307b334cb --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/NonTerminalDcl.sv @@ -0,0 +1,107 @@ +grammar silver:compiler:translation:java:core; + +aspect production nonterminalDcl +top::AGDcl ::= quals::NTDeclQualifiers 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' +{ + local className :: String = "N" ++ id.name; + + local inhVar :: String = "count_inh__ON__" ++ id.name; + local synVar :: String = "count_syn__ON__" ++ id.name; + + local myAnnos :: [NamedSignatureElement] = + annotationsForNonterminal(nonterminalType(fName, map((.kindrep), tl.types), quals.tracked), top.env); + + local commaIfAnnos :: String = if length(myAnnos)!=0 then "," else ""; + local wantsTracking :: Boolean = typeWantsTracking(nonterminalType(fName, map((.kindrep), tl.types), quals.tracked), top.config, top.env); + + top.initProd := s"\t\tcommon.RTTIManager.registerNonterminal(${className}.nonterminalton);\n\n"; + top.initWeaving := s""" + public static int ${inhVar} = 0; + public static int ${synVar} = 0;"""; + + local interfaces::[String] = map(makeAnnoName, map((.elementName), myAnnos)) ++ if wantsTracking then ["common.Tracked"] else []; + + top.genFiles := [pair(className ++ ".java", s""" +package ${makeName(top.grammarName)}; + +import java.util.*; +import silver.core.*; + +public abstract class ${className} extends common.Node ${ + (if null(interfaces) then "" else + " implements " ++ implode(", ", interfaces) + )} { + + public static final int num_inh_attrs = Init.${inhVar}; + public static final int num_syn_attrs = Init.${synVar}; + + public static final String[] occurs_inh = new String[num_inh_attrs]; + public static final String[] occurs_syn = new String[num_syn_attrs]; + public static final LinkedList decorators = new LinkedList(); + + public static final common.Lazy[] defaultSynthesizedAttributes = new common.Lazy[num_syn_attrs]; + + protected ${className}(${if wantsTracking then "final NOriginInfo origin"++commaIfAnnos else ""} ${implode(", ", map((.annoSigElem), myAnnos))}) { + ${if wantsTracking then "this.origin = origin;" else ""} +${implode("", map(makeAnnoAssign, myAnnos))} + } + +${implode("", map((.annoDeclElem), myAnnos))} + + @Override + public final int getNumberOfInhAttrs() { + return num_inh_attrs; + } + + @Override + public final int getNumberOfSynAttrs() { + return num_syn_attrs; + } + + @Override + public final common.Lazy getDefaultSynthesized(final int index) { + return defaultSynthesizedAttributes[index]; + } + + @Override + public final String getNameOfInhAttr(final int index) { + return occurs_inh[index]; + } + + @Override + public final String getNameOfSynAttr(final int index) { + return occurs_syn[index]; + } + + @Override + public final String[] getAnnoNames() { + return new String[]{${implode(", ", map((.annoNameElem), myAnnos))}}; + } + + @Override + public final Object getAnno(final String name) { + ${concat(map((.annoLookupElem), myAnnos))}{ + throw new common.exceptions.SilverInternalError("Invalid annotation " + name); + } + } + + public static final common.RTTIManager.Nonterminalton<${className}> nonterminalton = new Nonterminalton(); + + public static final class Nonterminalton extends common.RTTIManager.Nonterminalton<${className}> { + public String getName(){ return "${top.grammarName}:${id.name}"; } + public String[] getOccursInh() { return ${className}.occurs_inh; } + } + + ${otImpl} +} +""")]; + + local otImpl::String = if wantsTracking then s""" + protected final silver.core.NOriginInfo origin; + + public final silver.core.NOriginInfo getOrigin() { + return this.origin; + } +""" else ""; + +} diff --git a/grammars/silver/compiler/translation/java/core/OccursDcl.sv b/grammars/silver/compiler/translation/java/core/OccursDcl.sv new file mode 100644 index 000000000..2fc0454d7 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/OccursDcl.sv @@ -0,0 +1,37 @@ +grammar silver:compiler:translation:java:core; + +aspect production defaultAttributionDcl +top::AGDcl ::= at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + local ntfn :: String = nt.lookupType.fullName; + local occursType :: String = if at.lookupAttribute.dcl.isSynthesized then "syn" else "inh"; + local ntgrammar :: String = substring(0, lastIndexOf(":", ntfn), ntfn); + local ntname :: String = substring(lastIndexOf(":", ntfn)+1 , length(ntfn), ntfn); + -- TODO: don't we have a 'grammarFromQName' function somewhere? or even an attribute on qname why not? + + top.setupInh := + if at.lookupAttribute.dcl.isAnnotation then + "" + else + s"\t\t${makeNTName(ntfn)}.occurs_${occursType}[${head(occursCheck).attrGlobalOccursInitIndex}] = \"${at.lookupAttribute.fullName}\";\n"; + + top.postInit := + if at.lookupAttribute.dcl.isAnnotation then + "" + else + s"\t\tcommon.RTTIManager.registerOccurs(\"${ntfn}\", \"${at.lookupAttribute.fullName}\", ${head(occursCheck).attrGlobalOccursInitIndex});\n"; + + top.valueWeaving := + if at.lookupAttribute.dcl.isAnnotation then + "" + else + s"public static final int ${head(occursCheck).attrOccursIndexName} = " ++ + s"${makeName(ntgrammar)}.Init.count_${occursType}__ON__${ntname}++;\n"; +} + +aspect production errorAttributionDcl +top::AGDcl ::= msg::[Message] at::PartiallyDecorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs +{ + top.setupInh := error("Internal compiler error: translation not defined in the presence of errors"); + top.valueWeaving := error("Internal compiler error: translation not defined in the presence of errors"); +} diff --git a/grammars/silver/compiler/translation/java/core/Origins.sv b/grammars/silver/compiler/translation/java/core/Origins.sv new file mode 100644 index 000000000..3ab986862 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/Origins.sv @@ -0,0 +1,120 @@ +import silver:compiler:definition:origins; +import silver:util:cmdargs only CmdArgs; +import silver:compiler:driver; + +-- Information on how to compute origins for stuff constructed/modified in different block contexts +-- How do we get an OriginContext representing this? +synthesized attribute contextRef :: String occurs on ContextOriginInfoSource; + +-- Given a string holding java code to produce a NOriginRule[] array, produce a string holding java code +-- to produce a OriginContext for this context adding those rules (faster than getting contextRef and +-- adding rules) +synthesized attribute contextRefAddingRules :: (String ::= String) occurs on ContextOriginInfoSource; + +-- Is this context something that's `interesting` (= er flag = isNewConstruction) all the time? +-- Only `false` for cases where we are in a production body +synthesized attribute alwaysConsideredInteresting :: Boolean occurs on ContextOriginInfoSource; + +aspect production useContextLhsAndRules +top::ContextOriginInfoSource ::= +{ + top.contextRef = "new common.OriginContext(context.undecorate(), null)"; + top.contextRefAddingRules = (\x::String -> s"new common.OriginContext(context.undecorate(), ${x})"); + top.alwaysConsideredInteresting = false; +} + +aspect production useRuntimePassedInfo +top::ContextOriginInfoSource ::= +{ + top.contextRef = "originCtx"; + top.contextRefAddingRules = (\x::String -> s"new common.OriginContext(${top.contextRef}, ${x})"); + top.alwaysConsideredInteresting = true; +} + +aspect production useBogusInfo +top::ContextOriginInfoSource ::= name::String +{ + top.contextRef = "common.OriginContext."++name; + top.contextRefAddingRules = (\x::String -> s"new common.OriginContext(${top.contextRef}, ${x})"); + top.alwaysConsideredInteresting = true; +} + +function makeOriginContextRef +String ::= top::Decorated Expr --need .frame anno +{ + local rulesTrans :: [String] = (if top.config.tracingOrigins then [locRule] else []) ++ map((.translation), top.originRules); + local locRule :: String = s"new silver.core.PtraceNote(new common.StringCatter(\"${substitute("\"", "\\\"", top.location.unparse)}\"))"; + + return if top.config.noOrigins then "null" + else if length(rulesTrans)==0 + then top.frame.originsContextSource.contextRef + else top.frame.originsContextSource.contextRefAddingRules(s"new silver.core.NOriginNote[]{${implode(", ", rulesTrans)}}"); +} + +global newConstructionOriginUsingCtxRef :: String = + "originCtx.makeNewConstructionOrigin(true)"; + +function makeNewConstructionOrigin +[String] ::= top::Decorated Expr inInteresting::Boolean --need .frame anno +{ + local ty :: Type = finalType(top).outputType; + local interesting :: Boolean = top.frame.originsContextSource.alwaysConsideredInteresting || !top.isRoot || inInteresting; + + return if typeWantsTracking(ty, top.config, top.env) + then [makeOriginContextRef(top)++s".makeNewConstructionOrigin(${if interesting then "true" else "false"})"] + else []; +} + +-- These types will not have origins (will not be TrackedNodes) even if built with --force-origins +function getSpecialCaseNoOrigins +[String] ::= +{ + production attribute names::[String] with ++; + names := [ + -- These are forced to be untracked to prevent circularity + "silver:core:OriginInfo", + "silver:core:OriginInfoType", + "silver:core:OriginNote", + -- List is special(TM) because of it's special(TM) quasi-extension translation specialization + "silver:core:List" + ]; + return names; +} + + +function typeWantsTracking +Boolean ::= ty::Type conf::Decorated CmdArgs env::Decorated Env +{ + return if conf.noOrigins || containsBy((\a::String b::String -> a==b), ty.typeName, getSpecialCaseNoOrigins()) then false + else case ty of + | nonterminalType(fn, _, tracked) -> conf.forceOrigins || tracked + | appType(c, _) -> typeWantsTracking(c, conf, env) + | _ -> false + end; +} + +function wrapAccessWithOT +String ::= top::Decorated Expr expr::String +{ + local ty :: Type = finalType(top); + + -- The complexity here is needed because of silver generics. A nonterminal like Maybe is monomorphized in such a way + -- that the parameter type is Object. As a result we can't tell, when it's possible that we are doing something on a type + -- that may be a parameter, if it will want OI or not (or for that matter, if it's even a subclass of Node or if it's a + -- boxed primitive). As a result our paths are: + -- - directCopy is the fastpath for things we know want OI + -- - noop is the "fastpath" where we do nothing, either because we know it's a Node and it dosen't want OI, or because + -- we know it's a primitive + -- - polyCopy is the slowpath that uses java instanceof to check if it's tracked, and then send OI if it is + + local polyCopy :: String = s"${makeOriginContextRef(top)}.attrAccessCopyPoly(${expr})"; + local directCopy :: String = s"${makeOriginContextRef(top)}.attrAccessCopy(${expr})"; + local noop :: String = expr; + + local impl :: String = if ty.transType == "Object" then polyCopy else + (if typeWantsTracking(ty, top.config, top.env) then directCopy else noop); + + return if (top.config.noRedex || top.config.noOrigins) then noop else impl; + -- The extra (common.Node) cast in the non-generic non-primitive case is sometimes required for reasons I don't fully understand. + +} diff --git a/grammars/silver/compiler/translation/java/core/ProductionBody.sv b/grammars/silver/compiler/translation/java/core/ProductionBody.sv new file mode 100644 index 000000000..cb657b4e1 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/ProductionBody.sv @@ -0,0 +1,216 @@ +grammar silver:compiler:translation:java:core; + + +synthesized attribute attrName :: String; + +attribute attrName occurs on ForwardLHSExpr; + +attribute setupInh, translation, valueWeaving occurs on ProductionBody, ProductionStmts, ProductionStmt; +attribute translation occurs on DefLHS, ForwardInhs, ForwardInh; + +propagate setupInh, valueWeaving on ProductionBody, ProductionStmts; + +aspect production productionBody +top::ProductionBody ::= '{' stmts::ProductionStmts '}' +{ + top.translation = stmts.translation; +} + +aspect production productionStmtsNil +top::ProductionStmts ::= +{ + top.translation = ""; +} + +aspect production productionStmtsSnoc +top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt +{ + top.translation = h.translation ++ t.translation; +} + +------- + +aspect production productionStmtAppend +top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt +{ + propagate setupInh, valueWeaving; + top.translation = h.translation ++ t.translation; +} + +aspect production errorProductionStmt +top::ProductionStmt ::= e::[Message] +{ + top.translation = ""; +} + +-------------------------------------------------------------------------------- + +aspect default production +top::ProductionStmt ::= +{ + -- Always require translation + propagate setupInh, valueWeaving; +} + +aspect production forwardsTo +top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' +{ + top.translation = ""; +} + +aspect production forwardingWith +top::ProductionStmt ::= 'forwarding' 'with' '{' inh::ForwardInhs '}' ';' +{ + top.translation = inh.translation; +} + +aspect production forwardInh +top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' +{ + top.translation = + s"\t\t//${top.unparse}\n" ++ + s"\t\t${top.frame.className}.forwardInheritedAttributes[${lhs.attrName}] = ${wrapLazy(e)};\n"; +} + +aspect production forwardInhsOne +top::ForwardInhs ::= lhs::ForwardInh +{ + top.translation = lhs.translation; +} + +aspect production forwardInhsCons +top::ForwardInhs ::= lhs::ForwardInh rhs::ForwardInhs +{ + top.translation = lhs.translation ++ rhs.translation; +} + +aspect production forwardLhsExpr +top::ForwardLHSExpr ::= q::QNameAttrOccur +{ + top.attrName = q.attrOccursInitIndex; +} + +aspect production localAttributeDcl +top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' +{ + local attribute ugh_dcl_hack :: ValueDclInfo; + ugh_dcl_hack = head(getValueDclAll(fName, top.env)); -- TODO really, we should have a DclInfo for ourselves no problem. but out current approach of constructing it via localDef makes this annoyingly difficult. this suggests a probably environment refactoring... + + top.valueWeaving := s"public static final int ${ugh_dcl_hack.attrOccursIndexName} = ${top.frame.prodLocalCountName}++;\n"; + + top.setupInh := + if isDecorable(te.typerep, top.env) + then + s"\t\t//${top.unparse}\n" ++ + s"\t\t${top.frame.className}.localInheritedAttributes[${ugh_dcl_hack.attrOccursInitIndex}] = " ++ + if te.typerep.isNonterminal || te.typerep.isPartiallyDecorated + then s"new common.Lazy[${makeNTName(te.typerep.typeName)}.num_inh_attrs];\n" + else s"new common.Lazy[${top.frame.className}.count_inh__ON__${makeIdName(transTypeNameWith(te.typerep, top.frame.signature.freeVariables))}];\n" + else ""; + + top.setupInh <- s"\t\t${top.frame.className}.occurs_local[${ugh_dcl_hack.attrOccursInitIndex}] = \"${fName}\";\n"; + + top.translation = ""; +} + +aspect production productionAttributeDcl +top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr ';' +{ + local attribute ugh_dcl_hack :: ValueDclInfo; + ugh_dcl_hack = head(getValueDclAll(fName, top.env)); -- TODO really, we should have a DclInfo for ourselves no problem. but out current approach of constructing it via localDef makes this annoyingly difficult. this suggests a probably environment refactoring... + + top.valueWeaving := s"public static final int ${ugh_dcl_hack.attrOccursIndexName} = ${top.frame.prodLocalCountName}++;\n"; + + top.setupInh := + if isDecorable(te.typerep, top.env) + then + s"\t\t//${top.unparse}\n" ++ + s"\t\t${top.frame.className}.localInheritedAttributes[${ugh_dcl_hack.attrOccursInitIndex}] = " ++ + if te.typerep.isNonterminal || te.typerep.isPartiallyDecorated + then s"new common.Lazy[${makeNTName(te.typerep.typeName)}.num_inh_attrs];\n" + else s"new common.Lazy[${top.frame.className}.count_inh__ON__${makeIdName(transTypeNameWith(te.typerep, top.frame.signature.freeVariables))}];\n" + else ""; + + top.setupInh <- s"\t\t${top.frame.className}.occurs_local[${ugh_dcl_hack.attrOccursInitIndex}] = \"${fName}\";\n"; + + top.translation = ""; +} + +aspect production childDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.translation = s"${top.frame.className}.childInheritedAttributes[${top.frame.className}.i_${q.lookupValue.fullName}]"; +} + +aspect production lhsDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.translation = s"${top.frame.className}.synthesizedAttributes"; +} + +aspect production localDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.translation = s"${top.frame.className}.localInheritedAttributes[${q.lookupValue.dcl.attrOccursIndex}]"; +} + +aspect production forwardDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.translation = s"${top.frame.className}.forwardInheritedAttributes"; +} + +aspect production errorDefLHS +top::DefLHS ::= q::PartiallyDecorated QName +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); +} + +aspect production errorAttributeDef +top::ProductionStmt ::= msg::[Message] dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); +} + +aspect production synthesizedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.translation = + s"\t\t// ${dl.unparse}.${attr.unparse} = ${e.unparse}\n" ++ + s"\t\t${dl.translation}[${attr.attrOccursInitIndex}] = ${wrapLazy(e)};\n"; +} + +aspect production inheritedAttributeDef +top::ProductionStmt ::= dl::PartiallyDecorated DefLHS attr::PartiallyDecorated QNameAttrOccur e::Expr +{ + top.translation = + s"\t\t// ${dl.unparse}.${attr.unparse} = ${e.unparse}\n" ++ + s"\t\t${dl.translation}[${attr.attrOccursInitIndex}] = ${wrapLazy(e)};\n"; +} + + +aspect production errorValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.translation = error("Internal compiler error: translation not defined in the presence of errors"); +} + +aspect production localValueDef +top::ProductionStmt ::= val::PartiallyDecorated QName e::Expr +{ + top.translation = + s"\t\t// ${val.unparse} = ${e.unparse}\n" ++ + s"\t\t${top.frame.className}.localAttributes[${val.lookupValue.dcl.attrOccursInitIndex}] = ${wrapLazy(e)};\n"; +} + +aspect production returnDef +top::ProductionStmt ::= 'return' e::Expr ';' +{ + top.translation = ""; +} + +aspect production attachNoteStmt +top::ProductionStmt ::= 'attachNote' note::Expr ';' +{ + top.translation = ""; +} \ No newline at end of file diff --git a/grammars/silver/compiler/translation/java/core/ProductionDcl.sv b/grammars/silver/compiler/translation/java/core/ProductionDcl.sv new file mode 100644 index 000000000..5a0c22ea3 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/ProductionDcl.sv @@ -0,0 +1,344 @@ +grammar silver:compiler:translation:java:core; + +import silver:compiler:driver; + +aspect production productionDcl +top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody +{ + local className :: String = "P" ++ id.name; + + top.setupInh := body.setupInh; + top.initProd := s"\t\t${className}.initProductionAttributeDefinitions();\n" + ++ s"\t\tcommon.RTTIManager.registerProduction(${className}.prodleton);\n\n"; + top.postInit := s"\t\tcommon.Decorator.applyDecorators(${fnnt}.decorators, ${className}.prodleton);\n"; + + top.initWeaving := s"\tpublic static int ${localVar} = 0;\n"; + top.valueWeaving := body.valueWeaving; + + local localVar :: String = "count_local__ON__" ++ makeIdName(fName); + local ntName :: String = namedSig.outputElement.typerep.typeName; + + local fnnt :: String = makeNTName(ntName); + local wantsTracking :: Boolean = typeWantsTracking(namedSig.outputElement.typerep, top.config, top.env); + + local ntDeclPackage :: String = implode(".", init(explode(".", fnnt))); + local typeNameSnipped :: String = last(explode(":", namedSig.outputElement.typerep.typeName)); + + local dupX :: (String ::= NamedSignatureElement String) = + (\x::NamedSignatureElement gc::String -> + (if x.typerep.transType == "Object" then s"(${gc} instanceof common.Tracked?((common.Tracked)${gc}).duplicate(null, notes):${gc})" + else if !x.typerep.tracked then gc + else gc++".duplicate(null, notes)")); + + local dupChild :: (String ::= NamedSignatureElement) = + (\x::NamedSignatureElement -> dupX(x, s"getChild_${x.elementName}()")); + + local dupAnno :: (String ::= NamedSignatureElement) = + (\x::NamedSignatureElement -> dupX(x, s"getAnno_${makeIdName(x.elementName)}()")); + + local copyChild :: (String ::= NamedSignatureElement) = + (\x::NamedSignatureElement -> s"child_${x.elementName}"); + + local copyAnno :: (String ::= NamedSignatureElement) = + (\x::NamedSignatureElement -> s"anno_${makeIdName(x.elementName)}"); + + local getChildTypes :: (String ::= NamedSignatureElement) = + (\x::NamedSignatureElement -> case x.typerep of + | nonterminalType(fn, _, _) -> s"\"${fn}\"" + | _ -> "null" + end); + + local commaIfKids :: String = if length(namedSig.inputElements)!=0 then "," else ""; + local commaIfAnnos :: String = if length(namedSig.namedInputElements)!=0 then "," else ""; + local commaIfContexts :: String = if length(namedSig.contexts)!=0 then "," else ""; + local commaIfAny :: String = if length(namedSig.inputElements)!=0 || length(namedSig.namedInputElements)!=0 || length(namedSig.contexts)!=0 then "," else ""; + + local contexts::Contexts = foldContexts(namedSig.contexts); + contexts.boundVariables = namedSig.freeVariables; + + top.genFiles := [pair(className ++ ".java", s""" +package ${makeName(top.grammarName)}; + +import silver.core.*; + +// ${ntName} +// ${ns.unparse} +public final class ${className} extends ${fnnt} { + +${makeIndexDcls(0, namedSig.inputElements)} + + public static final String childTypes[] = {${implode(",", map(getChildTypes, namedSig.inputElements))}}; + + public static final int num_local_attrs = Init.${localVar}; + public static final String[] occurs_local = new String[num_local_attrs]; + + public static final common.Lazy[] forwardInheritedAttributes = new common.Lazy[${fnnt}.num_inh_attrs]; + + public static final common.Lazy[] synthesizedAttributes = new common.Lazy[${fnnt}.num_syn_attrs]; + public static final common.Lazy[][] childInheritedAttributes = new common.Lazy[${toString(length(namedSig.inputElements))}][]; + + public static final common.Lazy[] localAttributes = new common.Lazy[num_local_attrs]; + public static final common.Lazy[][] localInheritedAttributes = new common.Lazy[num_local_attrs][]; + +${namedSig.inhOccursIndexDecls} + + public static final int[] childInhContextTypeVars = {${implode(",", namedSig.childTypeVarElems)}}; + public static final int[] localInhContextTypeVars = new int[num_local_attrs]; + + static { +${namedSig.childStatic} + } + + public ${className}(final NOriginInfo origin ${commaIfAny} ${namedSig.javaSignature}) { + super(${if wantsTracking then "origin"++commaIfAnnos else ""}${implode(", ", map((.annoRefElem), namedSig.namedInputElements))}); +${implode("", map(makeChildAssign, namedSig.inputElements))} +${contexts.contextInitTrans} + } + + + public ${className}(${namedSig.javaSignature}) { + this(null ${if length(namedSig.refInvokeTrans)!=0 then ", " ++ namedSig.refInvokeTrans else ""}); + } + +${namedSig.childDecls} + +${contexts.contextMemberDeclTrans} + + @Override + public Object getChild(final int index) { + switch(index) { +${implode("", map(makeChildAccessCase, namedSig.inputElements))} + default: return null; + } + } + + @Override + public Object getChildLazy(final int index) { + switch(index) { +${implode("", map(makeChildAccessCaseLazy, namedSig.inputElements))} + default: return null; + } + } + + @Override + public final int getNumberOfChildren() { + return ${toString(length(namedSig.inputElements))}; + } + + @Override + public common.Lazy getSynthesized(final int index) { + return synthesizedAttributes[index]; + } + + @Override + public common.Lazy[] getLocalInheritedAttributes(final int key) { +${flatMap(makeInhOccursContextAccess(namedSig.freeVariables, namedSig.contextInhOccurs, "localInhContextTypeVars", "localInheritedAttributes", _), namedSig.inhOccursContextTypes)} + return localInheritedAttributes[key]; + } + + @Override + public common.Lazy[] getChildInheritedAttributes(final int key) { +${flatMap(makeInhOccursContextAccess(namedSig.freeVariables, namedSig.contextInhOccurs, "childInhContextTypeVars", "childInheritedAttributes", _), namedSig.inhOccursContextTypes)} + return childInheritedAttributes[key]; + } + + @Override + public boolean hasForward() { + return ${(if null(body.uniqueSignificantExpression) then "false" else "true")}; + } + + @Override + public common.Node evalForward(final common.DecoratedNode context) { + ${if null(body.uniqueSignificantExpression) + then s"throw new common.exceptions.SilverInternalError(\"Production ${fName} erroneously claimed to forward\")" + else s"return ((common.Node)${head(body.uniqueSignificantExpression).translation}${if wantsTracking && !top.config.noRedex then s".duplicateForForwarding(context.undecorate(), \"${substitute("\"", "\\\"", hackUnparse(head(body.uniqueSignificantExpression).location))}\")" else ""})"}; + } + + @Override + public common.Lazy getForwardInheritedAttributes(final int index) { + return forwardInheritedAttributes[index]; + } + + @Override + public common.Lazy getLocal(final int key) { + return localAttributes[key]; + } + + @Override + public final int getNumberOfLocalAttrs() { + return num_local_attrs; + } + + @Override + public final String getNameOfLocalAttr(final int index) { + return occurs_local[index]; + } + + @Override + public String getName() { + return "${fName}"; + } + + ${otImpl} + + @Override + public final common.TypeRep getType() { +${makeTyVarDecls(2, namedSig.typerep.freeVariables)} + + ${implode("\n\t\t", map(makeChildUnify(fName, _), namedSig.inputElements))} + + return ${transFreshTypeRep(namedSig.outputElement.typerep)}; + } + + static void initProductionAttributeDefinitions() { +${body.translation} + } + + public static final common.RTTIManager.Prodleton<${className}> prodleton = new Prodleton(); + + public static final class Prodleton extends common.RTTIManager.Prodleton<${className}> { + public ${className} reify( + final silver.core.NAST origAST, + final common.ConsCell rules, + final common.TypeRep resultType, + final silver.core.NAST[] childASTs, + final String[] annotationNames, + final silver.core.NAST[] annotationASTs) + { + assert annotationNames.length == annotationASTs.length; + ${makeAnnoIndexDcls(0, namedSig.namedInputElements)} + ${makeTyVarDecls(2, namedSig.typerep.freeVariables)} + + common.TypeRep givenType = ${transFreshTypeRep(namedSig.outputElement.typerep)}; + if (!common.TypeRep.unify(resultType, givenType)) { + throw new common.exceptions.SilverError("reify is constructing " + resultType.toString() + ", but found " + givenType.toString() + " production ${fName} AST."); + } + + if (childASTs.length != ${toString(length(namedSig.inputElements))}) { + throw new common.exceptions.SilverError("Production ${fName} expected ${toString(length(namedSig.inputElements))} child(ren), but got " + childASTs.length + "."); + } + + String[] expectedAnnotationNames = new String[] {${implode(", ", map((.annoNameElem), annotationsForNonterminal(namedSig.outputElement.typerep, top.env)))}}; + if (!java.util.Arrays.equals(annotationNames, expectedAnnotationNames)) { + throw new common.exceptions.SilverError("Production ${fName} expected " + common.Util.namesToString(expectedAnnotationNames, "no") + " annotation(s), but got " + common.Util.namesToString(annotationNames, "none") + "."); + } + + ${implode("\n\t\t", map(makeChildReify(fName, length(namedSig.inputElements), _), namedSig.inputElements))} + ${implode("\n\t\t", map(makeAnnoReify(fName, _), namedSig.namedInputElements))} + ${namedSig.contextRuntimeResolve} + + return new ${className}(${if wantsTracking then "new silver.core.PoriginOriginInfo(common.OriginsUtil.SET_FROM_REIFICATION_OIT, origAST, rules, true)"++commaIfAny else ""} ${namedSig.refInvokeTrans}); + } + + public ${className} constructDirect( + final Object[] children, + final Object[] annos) + { + int counter = 0; + ${implode("\n\t\t", map(makeConstructDirectChildren, namedSig.inputElements))} + + counter = 0; + ${implode("\n\t\t", map(makeConstructDirectAnno, namedSig.namedInputElements))} + + ${namedSig.contextRuntimeResolve} + + return new ${className}(${namedSig.refInvokeTrans}); + } + + public String getName(){ return "${fName}"; } + public common.RTTIManager.Nonterminalton<${fnnt}> getNonterminalton(){ return ${fnnt}.nonterminalton; } + + public String getTypeUnparse() { return "${substitute("\"", "\\\"", substitute("\\", "\\\\", ns.unparse))}"; } + public int getChildCount() { return ${toString(length(namedSig.inputElements))}; } + public int getAnnoCount() { return ${toString(length(namedSig.namedInputElements))}; } + + public String[] getOccursInh() { return ${className}.occurs_inh; } + public String[] getChildTypes() { return ${className}.childTypes; } + public common.Lazy[][] getChildInheritedAttributes() { return ${className}.childInheritedAttributes; } + } + + public common.RTTIManager.Prodleton<${className}> getProdleton() { return prodleton; } + + + ${if null(namedSig.contexts) then s"public static final common.NodeFactory<${fnnt}> factory = new Factory();" else ""} + + public static final class Factory extends common.NodeFactory<${fnnt}> { +${contexts.contextMemberDeclTrans} + + public Factory(${contexts.contextParamTrans}) { +${contexts.contextInitTrans} + } + + @Override + public final ${fnnt} invoke(final common.OriginContext originCtx, final Object[] children, final Object[] annotations) { + return new ${className}(${implode(", ", (if wantsTracking then [newConstructionOriginUsingCtxRef] else []) ++ map(\ c::Context -> decorate c with {boundVariables = namedSig.freeVariables;}.contextRefElem, namedSig.contexts) ++ unpackChildren(0, namedSig.inputElements) ++ unpackAnnotations(0, namedSig.namedInputElements))}); + } + + @Override + public final common.AppTypeRep getType() { +${makeTyVarDecls(3, namedSig.typerep.freeVariables)} + return ${transFreshTypeRep(namedSig.typerep)}; + } + + @Override + public final String toString() { + return "${top.grammarName}:${id.name}"; + } + }; + +} +""")]; + + local otImpl :: String = if wantsTracking then s""" + public ${fnnt} duplicate(Object redex, Object notes) { + if (redex == null || ${if top.config.noRedex then "true" else "false"}) { + return new ${className}(new PoriginOriginInfo(common.OriginsUtil.SET_AT_NEW_OIT, this, notes, true) ${commaIfKids} + ${implode(", ", map(dupChild, namedSig.inputElements))} ${commaIfAnnos} ${implode(", ", map(dupAnno, namedSig.namedInputElements))}); + } else { + return new ${className}(new PoriginAndRedexOriginInfo(common.OriginsUtil.SET_AT_NEW_OIT, this, notes, redex, notes, true) ${commaIfKids} + ${implode(", ", map(dupChild, namedSig.inputElements))} ${commaIfAnnos} ${implode(", ", map(dupAnno, namedSig.namedInputElements))}); + } + } + + public ${fnnt} duplicate(common.OriginContext oc) { + return this.duplicate(oc.lhs, oc.rulesAsSilverList()); + } + + public ${fnnt} copy(Object newRedex, Object newRule) { + Object origin, originNotes, newlyConstructed; + Object roi = this.origin; + if (roi instanceof PoriginOriginInfo) { + PoriginOriginInfo oi = (PoriginOriginInfo)roi; + origin = oi.getChild_origin(); + originNotes = oi.getChild_originNotes(); + newlyConstructed = oi.getChild_newlyConstructed(); + } else if (roi instanceof PoriginAndRedexOriginInfo) { + PoriginAndRedexOriginInfo oi = (PoriginAndRedexOriginInfo)roi; + origin = oi.getChild_origin(); + originNotes = oi.getChild_originNotes(); + newlyConstructed = oi.getChild_newlyConstructed(); + } else { + return this; + } + + if (newRedex instanceof common.DecoratedNode) newRedex = ((common.DecoratedNode)newRedex).undecorate(); + + Object redex = ((common.Node)newRedex); + Object redexNotes = newRule; + return new ${className}(new PoriginAndRedexOriginInfo(common.OriginsUtil.SET_AT_ACCESS_OIT, origin, originNotes, redex, redexNotes, newlyConstructed) ${commaIfKids} + ${implode(", ", map(copyChild, namedSig.inputElements))} ${commaIfAnnos} ${implode(", ", map(copyAnno, namedSig.namedInputElements))}); + } + + public ${fnnt} duplicateForForwarding(Object redex, String note) { + return new ${className}(new PoriginOriginInfo(common.OriginsUtil.SET_AT_FORWARDING_OIT, this, new common.ConsCell(new silver.core.PoriginDbgNote(new common.StringCatter(note)), common.ConsCell.nil), true) ${commaIfKids} + ${implode(", ", map(copyChild, namedSig.inputElements))} ${commaIfAnnos} ${implode(", ", map(copyAnno, namedSig.namedInputElements))}); + } + + """ else ""; + + -- main function signature check TODO: this should probably be elsewhere! + top.errors <- + if id.name == "main" + then [err(top.location, "main should be a function!")] + else []; +} diff --git a/grammars/silver/compiler/translation/java/core/Project.sv b/grammars/silver/compiler/translation/java/core/Project.sv new file mode 100644 index 000000000..19d40c475 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/Project.sv @@ -0,0 +1,73 @@ +grammar silver:compiler:translation:java:core; + +imports silver:compiler:translation:java:type; + +imports silver:compiler:definition:core; +imports silver:compiler:definition:type:syntax; + +imports silver:compiler:definition:env; +imports silver:compiler:definition:type; + +function makeName +String ::= str::String +{ + return substitute(":", ".", str); +} +function makeIdName +String ::= str::String +{ + return substitute(":", "_", str); +} + +function makeProdName +String ::= s::String +{ + return substituteLast(".", ".P", makeName(s)); +} + +function makeNTName +String ::= s::String +{ + return substituteLast(".", ".N", makeName(s)); +} + +function makeAnnoName +String ::= s::String +{ + return substituteLast(".", ".A", makeName(s)); +} + +function makeTerminalName +String ::= s::String +{ + return substituteLast(".", ".T", makeName(s)); +} + +function makeParserName +String ::= s::String +{ + return "Parser_" ++ makeIdName(s); +} + +function makeClassName +String ::= s::String +{ + return substituteLast(".", ".C", makeName(s)); +} + +function makeInstanceName +String ::= g::String s::String t::Type +{ + return substituteLast(".", ".I", makeName(g ++ ":" ++ substitute(":", "_", s))) ++ "_" ++ transTypeName(t); +} + +function substituteLast +String ::= r::String s::String str::String +{ + local attribute i::Integer; + i = lastIndexOf(r, str); + + return if i == -1 then str + else substring(0,i,str) ++ s ++ substring(i+length(r), length(str), str); +} + diff --git a/grammars/silver/compiler/translation/java/core/QName.sv b/grammars/silver/compiler/translation/java/core/QName.sv new file mode 100644 index 000000000..809c2f3a8 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/QName.sv @@ -0,0 +1,10 @@ +grammar silver:compiler:translation:java:core; + +attribute attrOccursIndex, attrOccursInitIndex occurs on QNameAttrOccur; + +aspect production qNameAttrOccur +top::QNameAttrOccur ::= at::QName +{ + top.attrOccursIndex = resolvedDcl.attrOccursIndex; + top.attrOccursInitIndex = resolvedDcl.attrOccursInitIndex; +} diff --git a/grammars/silver/compiler/translation/java/core/Root.sv b/grammars/silver/compiler/translation/java/core/Root.sv new file mode 100644 index 000000000..be74b9a32 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/Root.sv @@ -0,0 +1,54 @@ +grammar silver:compiler:translation:java:core; + +{-- + - Java classes to generate. (filename, contents) + -} +monoid attribute genFiles :: [Pair]; +{-- + - Used for svib files. + -} +monoid attribute genBinaryFiles :: [Pair]; +{-- + - Early initializers: occurs.add, local's inh attr map creation, decorators.add, collection object creation + -} +monoid attribute setupInh :: String; +{-- + - Initialize the attributes maps for each production. + - note to be confused with "production attribute" dcls. + -} +monoid attribute initProd :: String; +{-- + - Global values. + -} +monoid attribute initValues :: String; +{-- + - Late initializers. Decorator application (late because we want all attribute equations to be posted first!!) + -} +monoid attribute postInit :: String; + +synthesized attribute translation :: String; +{-- + - Initial values for early weaving. e.g. counter for # attributes on NT + -} +monoid attribute initWeaving :: String; +{-- + - Values computed by early weaving. e.g. index of attribute in NT arrays + -} +monoid attribute valueWeaving :: String; + +attribute genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving occurs on Root, AGDcls, AGDcl, Grammar; + +propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving on Root, AGDcls, Grammar; + +aspect default production +top::AGDcl ::= +{ + -- Empty values as defaults + propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving; +} + +aspect production appendAGDcl +top::AGDcl ::= h::AGDcl t::AGDcl +{ + propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving; +} diff --git a/grammars/silver/compiler/translation/java/core/RootSpec.sv b/grammars/silver/compiler/translation/java/core/RootSpec.sv new file mode 100644 index 000000000..93f357fbf --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/RootSpec.sv @@ -0,0 +1,80 @@ +grammar silver:compiler:translation:java:core; + +import silver:compiler:driver:util; + +attribute genFiles occurs on RootSpec; +attribute genBinaryFiles occurs on RootSpec; + +aspect production interfaceRootSpec +top::RootSpec ::= _ _ +{ + top.genFiles := []; + top.genBinaryFiles := []; +} + +aspect production errorRootSpec +top::RootSpec ::= _ _ _ _ _ +{ + top.genFiles := []; + top.genBinaryFiles := []; +} + +aspect production grammarRootSpec +top::RootSpec ::= g::Grammar _ _ _ _ _ +{ + top.genBinaryFiles := [ + pair("Silver.svi", top.serInterface) + ]; + + top.genFiles := g.genFiles ++ + [pair("Init.java", s""" +package ${makeName(g.declaredName)}; + +public class Init{ + private static boolean preInit = false; + private static boolean init = false; + private static boolean postInit = false; + + public static void initAllStatics(){ + if(preInit) return; + preInit = true; +${makeOthers(top.allGrammarDependencies, "initAllStatics")} + } + + public static void init(){ + if(init) return; + init = true; + setupInheritedAttributes(); +${makeOthers(top.allGrammarDependencies, "init")} + initProductionAttributeDefinitions(); + } + + public static void postInit(){ + if(postInit) return; + postInit = true; +${makeOthers(top.allGrammarDependencies, "postInit")} +${g.postInit} + } + + private static void setupInheritedAttributes(){ +${g.setupInh} + } + + private static void initProductionAttributeDefinitions(){ +${g.initProd} + } + +${g.initWeaving} +${g.valueWeaving} + final static common.DecoratedNode context = common.TopNode.singleton; // For globals +${g.initValues} +} +""")]; +} + +function makeOthers +String ::= others::[String] nme::String +{ + return if null(others) then "" else s"\t\t${makeName(head(others))}.Init.${nme}();\n${makeOthers(tail(others),nme)}"; +} + diff --git a/grammars/silver/compiler/translation/java/core/TerminalDcl.sv b/grammars/silver/compiler/translation/java/core/TerminalDcl.sv new file mode 100644 index 000000000..f186734f5 --- /dev/null +++ b/grammars/silver/compiler/translation/java/core/TerminalDcl.sv @@ -0,0 +1,61 @@ +grammar silver:compiler:translation:java:core; + +imports silver:compiler:definition:concrete_syntax; +imports silver:compiler:modification:copper; + +aspect production terminalDclDefault +top::AGDcl ::= t::TerminalKeywordModifier id::Name r::RegExpr tm::TerminalModifiers +{ + top.initProd := s"\t\tcommon.RTTIManager.registerTerminal(${makeName(top.grammarName)}.T${id.name}.terminalton);\n\n"; + top.genFiles := terminalTranslation(id.name, top.grammarName, expandTransitiveSuperClasses([], tm.lexerClasses, top.env)); +} + +function terminalTranslation +[Pair] ::= name::String grammarName::String lexerClasses::[String] +{ + local className :: String = "T" ++ name; + local fName :: String = grammarName ++ ":" ++ name; + local lexerClassesStr :: String = implode(", ", map(\ s::String -> s"\"${s}\"", lexerClasses)); + + return [pair(className ++ ".java", s""" +package ${makeName(grammarName)}; + +import edu.umn.cs.melt.copper.runtime.engines.semantics.VirtualLocation; +import silver.core.NLocation; + +public class ${className} extends common.Terminal { + public ${className}(final String lexeme, final VirtualLocation vl, final int index, final int endIndex) { + super(lexeme, vl, index, endIndex); + } + + public ${className}(final common.StringCatter lexeme, final NLocation location) { + super(lexeme, location); + } + + @Override + public String getName() { return "${fName}"; } + + private static String[] lexerclasses = null; + @Override + public String[] getLexerClasses() { + // Avoid doing more work at class-load time, in case we don't need this + if (lexerclasses == null) { + lexerclasses = new String[] {${lexerClassesStr}}; + } + return lexerclasses; + } + + public static final common.RTTIManager.Terminalton<${className}> terminalton = new Terminalton(); + + public static final class Terminalton extends common.RTTIManager.Terminalton<${className}> { + public ${className} construct(final common.StringCatter lexeme, final silver.core.NLocation location) { + return new ${className}(lexeme, location); + } + public String getName() { return "${fName}"; } + } + + public common.RTTIManager.Terminalton<${className}> getTerminalton() { return terminalton; } +} + +""")]; +} diff --git a/grammars/silver/compiler/translation/java/driver/BuildProcess.sv b/grammars/silver/compiler/translation/java/driver/BuildProcess.sv new file mode 100644 index 000000000..38d679125 --- /dev/null +++ b/grammars/silver/compiler/translation/java/driver/BuildProcess.sv @@ -0,0 +1,277 @@ +grammar silver:compiler:translation:java:driver; + +import silver:compiler:translation:java:core; + +import silver:compiler:driver; +import silver:compiler:definition:env; +import silver:compiler:definition:core; + +import silver:util:cmdargs; + +import silver:reflect:nativeserialize; + +synthesized attribute noJavaGeneration :: Boolean occurs on CmdArgs; +synthesized attribute buildSingleJar :: Boolean occurs on CmdArgs; +synthesized attribute relativeJar :: Boolean occurs on CmdArgs; +synthesized attribute includeRTJars :: [String] occurs on CmdArgs; +-- TODO: Should this be a Maybe? +synthesized attribute buildXmlLocation :: [String] occurs on CmdArgs; + +aspect production endCmdArgs +top::CmdArgs ::= _ +{ + top.noJavaGeneration = false; + top.buildSingleJar = false; + top.relativeJar = false; + top.includeRTJars = []; + top.buildXmlLocation = []; +} +abstract production dontTranslateFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.noJavaGeneration = true; + forwards to rest; +} +abstract production onejarFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.buildSingleJar = true; + forwards to rest; +} +abstract production relativejarFlag +top::CmdArgs ::= rest::CmdArgs +{ + top.relativeJar = true; + forwards to rest; +} +abstract production includeRTJarFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.includeRTJars = s :: forward.includeRTJars; + forwards to rest; +} +abstract production buildXmlFlag +top::CmdArgs ::= s::String rest::CmdArgs +{ + top.buildXmlLocation = s :: forward.buildXmlLocation; + forwards to rest; +} + +aspect function parseArgs +Either ::= args::[String] +{ + flags <- [ flagSpec(name="--dont-translate", paramString=nothing(), + help="check for errors without producing Java code", + flagParser=flag(dontTranslateFlag)) + , flagSpec(name="--onejar", paramString=nothing(), + help="a typo of --one-jar", + flagParser=flag(onejarFlag)) + , flagSpec(name="--one-jar", paramString=nothing(), + help="include runtime libraries in the JAR", + flagParser=flag(onejarFlag)) + , flagSpec(name="--relative-jar", paramString=nothing(), + help="assume runtime libraries will be in the same directory as the JAR", + flagParser=flag(relativejarFlag)) + , flagSpec(name="--include-jar", paramString=nothing(), + help="links to an additional JAR", + flagParser=option(includeRTJarFlag)) + , flagSpec(name="--build-xml-location", paramString=nothing(), + help="sets the path the Ant build.xml will be saved to. Used by Eclipse", + flagParser=option(buildXmlFlag)) + ]; +} +aspect production compilation +top::Compilation ::= g::Grammars _ buildGrammars::[String] benv::BuildEnv +{ + -- Main class, jar name, etc. are based on the first specified grammar. + local buildGrammar::String = head(buildGrammars); + + -- This is a little bit of a hack. It's only job is to allow the Eclipse support + -- for Silver to put this file elsewhere than the local directory. + -- e.g. --build-xml-location /path/to/workspace/project/build.xml + local buildXmlLocation :: String = + if null(top.config.buildXmlLocation) then "build.xml" + else head(top.config.buildXmlLocation); + + production attribute keepFiles :: [String] with ++; + keepFiles := []; + + top.postOps <- + [genBuild(buildXmlLocation, buildXml)] ++ + (if top.config.noJavaGeneration then [] + else [genJava(top.config, benv.silverGen, keepFiles, grammarsToTranslate)]); + + -- From here on, it's all build.xml stuff: + + -- Presently, copper, copper_mda, and impide all contribute new targets into build.xml: + production attribute extraTopLevelDecls :: [String] with ++; + extraTopLevelDecls := []; + + -- Presently, impide and copper_mda introduce a new top-level goal: + production attribute extraDistDeps :: [String] with ++; + extraDistDeps := if top.config.noJavaGeneration then [] else ["jars"]; + + -- Presently, unused? + production attribute extraJarsDeps :: [String] with ++; + extraJarsDeps := ["grammars"]; + + -- Presently, copper and copper_mda + production attribute extraGrammarsDeps :: [String] with ++; + extraGrammarsDeps := ["init"]; + + production attribute classpathCompiler :: [String] with ++; + classpathCompiler := ["${sh}/jars/commonmark-0.17.1.jar"]; + + production attribute classpathRuntime :: [String] with ++; + classpathRuntime := ["${sh}/jars/commonmark-0.17.1.jar", "${sh}/jars/SilverRuntime.jar"]; + + -- The --XRTjar hack + classpathRuntime <- top.config.includeRTJars; + + production attribute extraManifestAttributes :: [String] with ++; + extraManifestAttributes := [ + "", + "", + ""]; -- TODO: we "should" make main depend on whether there is a main... + + extraManifestAttributes <- + if top.config.buildSingleJar then [] + else [""]; + + local attribute outputFile :: String; + outputFile = if !null(top.config.outName) then head(top.config.outName) + else (if g.jarName.isJust then g.jarName.fromJust else makeName(buildGrammar)) ++ ".jar"; + + local attribute buildXml :: String; + buildXml = +"\n" ++ +" Generated build script for the grammar " ++ buildGrammar ++ "\n\n" ++ + +" \n" ++ +" \n" ++ +" \n" ++ +" \n" ++ +" \n\n" ++ + +" \n" ++ + flatMap(pathLocation, classpathRuntime) ++ +" \n\n" ++ + +" \n" ++ +" \n" ++ +" \n" ++ + flatMap(pathLocation, classpathCompiler) ++ + flatMap(pathLocation, map(\s::String -> s ++ "bin/", benv.silverHostGen)) ++ +" \n\n" ++ + +implode("\n\n", extraTopLevelDecls) ++ "\n\n" ++ + +" \n" ++ +" \n" ++ +" \n" ++ +" \n" ++ +" \n" ++ +" \n\n" ++ + +" \n" ++ +" \n\n" ++ + +" \n" ++ +-- Uncondintionally compute this, but it's included conditionally as a manifest attribute +" \n" ++ +( + if top.config.relativeJar then +-- Removes all paths from the classpath. This means we expect to find all these +-- jars in the same directory as this jar. +" \n" + else +-- Escape spaces as url-encoded spaces. maybe there's a better way? +-- This solves the problem of spaces in paths, where Class-Path in manifests are split on spaces. +" \n" +) ++ +" \n" ++ +" \n" ++ + flatMap(includeClassFiles, grammarsRelevant) ++ +" \n" ++ +" " ++ implode("\n ", extraManifestAttributes) ++ "\n" ++ +" \n" ++ + +-- If we're building a single jar, then include the runtimes TODO: this method kinda sucks + (if top.config.buildSingleJar then implode("", map(zipfileset, classpathRuntime)) else "") ++ + +" \n" ++ +" \n\n" ++ + +" \n" ++ +" \n" ++ +" \n" ++ + flatMap(includeJavaFiles, grammarsDependedUpon) ++ +" \n" ++ +" \n" ++ +"\n"; +} + +abstract production genJava +top::DriverAction ::= a::Decorated CmdArgs silverGen::String keepFiles::[String] specs::[Decorated RootSpec] +{ + top.run = do { + eprintln("Generating Translation."); + traverse_(writeSpec(silverGen, keepFiles, _), specs); + return 0; + }; + top.order = 4; +} + +abstract production genBuild +top::DriverAction ::= buildFileLocation::String buildXml::String +{ + top.run = do { writeFile(buildFileLocation, buildXml); return 0; }; + top.order = 6; +} + +function writeSpec +IO<()> ::= silverGen::String keepFiles::[String] r::Decorated RootSpec +{ + local srcPath :: String = silverGen ++ "src/" ++ grammarToPath(r.declaredName); + local binPath :: String = silverGen ++ "bin/" ++ grammarToPath(r.declaredName); + + return do { + eprintln("\t[" ++ r.declaredName ++ "]"); + isD::Boolean <- isDirectory(srcPath); + unless(isD, do { + mkDSuccess::Boolean <- mkdir(srcPath); + unless(mkDSuccess, do { + eprintln("Unrecoverable Error: Unable to create directory: " ++ srcPath ++ + "\nWarning: if some interface file writes were successful, but others not, Silver's temporaries are in an inconsistent state. Use the --clean flag next run."); + exit(-5); + }); + }); + oldSrcFiles::[String] <- listContents(srcPath); + deleteFiles(removeAll(keepFiles, oldSrcFiles)); + deleteDirFiles(binPath); + writeFiles(srcPath, r.genFiles); + writeBinaryFiles(srcPath, r.genBinaryFiles); + }; +} + +function zipfileset +String ::= s::String +{ + return " \n"; +} +function pathLocation +String ::= s::String +{ + return " \n"; +} +function includeJavaFiles +String ::= gram::String +{ + return s" \n"; +} +function includeClassFiles +String ::= gram::Decorated RootSpec +{ + return s" \n"; +} + diff --git a/grammars/silver/compiler/translation/java/type/Context.sv b/grammars/silver/compiler/translation/java/type/Context.sv new file mode 100644 index 000000000..656728095 --- /dev/null +++ b/grammars/silver/compiler/translation/java/type/Context.sv @@ -0,0 +1,265 @@ +grammar silver:compiler:translation:java:type; + +import silver:compiler:definition:env; +import silver:compiler:definition:core only QNameAttrOccur, QName, qNameAttrOccur; + +-- Translation of *solved* contexts, not *constraint* contexts +synthesized attribute transContexts::[String] occurs on Contexts; +synthesized attribute transTypeableContexts::[String] occurs on Contexts; -- Hack to inject varTypeRep bindings +synthesized attribute transContextSuperAccessors::String occurs on Contexts; + +aspect production consContext +top::Contexts ::= h::Context t::Contexts +{ + top.transContexts = h.transContext :: t.transContexts; + top.transTypeableContexts = h.transTypeableContext :: t.transTypeableContexts; + top.transContextSuperAccessors = h.transContextSuperAccessor ++ t.transContextSuperAccessors; +} +aspect production nilContext +top::Contexts ::= +{ + top.transContexts = []; + top.transTypeableContexts = []; + top.transContextSuperAccessors = ""; +} + +attribute transType occurs on Context; +synthesized attribute transContext::String occurs on Context; +synthesized attribute transTypeableContext::String occurs on Context; +synthesized attribute transContextDummyInit::String occurs on Context; + +synthesized attribute transContextMemberName::String occurs on Context; +synthesized attribute transContextSuperAccessorName::String occurs on Context; +synthesized attribute transContextSuperAccessor::String occurs on Context; + +aspect default production +top::Context ::= +{ + top.transTypeableContext = top.transContext; -- Shouldn't be demanded? + top.transContextDummyInit = "null"; +} + +aspect production instContext +top::Context ::= fn::String t::Type +{ + top.transType = makeClassName(fn); + + resolvedDcl.transContextDeps = requiredContexts.transContexts; + top.transContext = resolvedDcl.transContext; + + top.transContextMemberName = makeConstraintDictName(fn, t, top.boundVariables); + top.transContextSuperAccessorName = makeInstanceSuperAccessorName(fn); + top.transContextSuperAccessor = s""" + public final ${top.transType} ${top.transContextSuperAccessorName}() { + return ${top.transContext}; + } +"""; +} + +aspect production inhOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + top.transType = "int"; + + resolvedDcl.transContextDeps = requiredContexts.transContexts; + top.transContext = resolvedDcl.attrOccursIndex; + top.transContextDummyInit = "0"; + + top.transContextMemberName = makeConstraintDictName(attr, ntty, top.boundVariables); + top.transContextSuperAccessorName = makeInstanceSuperAccessorName(attr); + top.transContextSuperAccessor = s""" + public final ${top.transType} ${top.transContextSuperAccessorName}() { + return ${top.transContext}; + } +"""; +} + +aspect production synOccursContext +top::Context ::= attr::String args::[Type] atty::Type inhs::Type ntty::Type +{ + top.transType = "int"; + + resolvedDcl.transContextDeps = requiredContexts.transContexts; + top.transContext = resolvedDcl.attrOccursIndex; + top.transContextDummyInit = "0"; + + top.transContextMemberName = makeConstraintDictName(attr, ntty, top.boundVariables); + top.transContextSuperAccessorName = makeInstanceSuperAccessorName(attr); + top.transContextSuperAccessor = s""" + public final ${top.transType} ${top.transContextSuperAccessorName}() { + return ${top.transContext}; + } +"""; +} + +aspect production annoOccursContext +top::Context ::= attr::String args::[Type] atty::Type ntty::Type +{ + -- This doesn't actually encode any runtime information, for now... + top.transType = "Object"; + top.transContext = "null"; + top.transContextMemberName = makeConstraintDictName(attr, ntty, top.boundVariables); + top.transContextSuperAccessorName = makeInstanceSuperAccessorName(attr); + top.transContextSuperAccessor = s""" + public final ${top.transType} ${top.transContextSuperAccessorName}() { + return ${top.transContext}; + } +"""; +} + +aspect production typeableContext +top::Context ::= t::Type +{ + top.transType = "common.TypeRep"; + + t.skolemTypeReps = zipWith(pair, t.freeVariables, requiredContexts.transTypeableContexts); + resolvedDcl.transContextDeps = requiredContexts.transTypeableContexts; + top.transTypeableContext = + case top.resolved, t of + -- We might need translate this context even when resolution fails; + -- in that case fall back to a rigid skolem constant. + | [], skolemType(tv) -> s"new common.BaseTypeRep(\"b${toString(tv.extractTyVarRep)}\")" + | [], _ -> t.transTypeRep + | _, _ -> resolvedDcl.transContext + end; + top.transContext = + if null(t.freeVariables) then top.transTypeableContext + -- Workaround to inject variable bindings + else s"""(new common.Typed() { + public final common.TypeRep getType() { +${makeTyVarDecls(5, t.freeVariables)} + return ${top.transTypeableContext}; + } + }).getType()"""; + + top.transContextMemberName = makeTypeableName(t, top.boundVariables); + top.transContextSuperAccessorName = "getType"; + top.transContextSuperAccessor = s""" + public final common.TypeRep getType() { + return ${top.transTypeableContext}; + } +"""; +} + +aspect production inhSubsetContext +top::Context ::= i1::Type i2::Type +{ + -- This doesn't actually encode any runtime information, for now... + top.transType = "Object"; + top.transContext = "null"; + top.transContextMemberName = makeInhSubsetName(i1, i2, top.boundVariables); + top.transContextSuperAccessorName = "getInhSubset"; + top.transContextSuperAccessor = s""" + public final Object getInhSubset() { + return ${top.transContext}; + } +"""; +} + +aspect production typeErrorContext +top::Context ::= msg::String +{ + -- This should never get translated + top.transType = error("Translation demanded for typeError context!"); + top.transContext = error("Translation demanded for typeError context!"); + top.transContextMemberName = error("Translation demanded for typeError context!"); + top.transContextSuperAccessorName = error("Translation demanded for typeError context!"); + top.transContextSuperAccessor = error("Translation demanded for typeError context!"); +} + +-- The translations of the narrowed forms of the instDcl's contexts are fed back as dcl.transContextDeps +inherited attribute transContextDeps::[String] occurs on InstDclInfo, OccursDclInfo; +attribute transContext occurs on InstDclInfo; + +aspect production instDcl +top::InstDclInfo ::= fn::String bound::[TyVar] contexts::[Context] ty::Type definedMembers::[String] +{ + top.transContext = s"new ${makeInstanceName(top.sourceGrammar, fn, ty)}(${implode(", ", top.transContextDeps)})"; +} +aspect production instConstraintDcl +top::InstDclInfo ::= fntc::String ty::Type tvs::[TyVar] +{ + top.transContext = makeConstraintDictName(fntc, ty, tvs); +} +aspect production sigConstraintDcl +top::InstDclInfo ::= fntc::String ty::Type ns::NamedSignature +{ + top.transContext = s"((${makeProdName(ns.fullName)})(context.undecorate())).${makeConstraintDictName(fntc, ty, ns.freeVariables)}"; +} +aspect production currentInstDcl +top::InstDclInfo ::= fntc::String ty::Type +{ + top.transContext = s"currentInstance()"; +} +aspect production instSuperDcl +top::InstDclInfo ::= fntc::String baseDcl::InstDclInfo +{ + baseDcl.transContextDeps = top.transContextDeps; + top.transContext = baseDcl.transContext ++ s".${makeInstanceSuperAccessorName(fntc)}()"; +} +aspect production typeableInstConstraintDcl +top::InstDclInfo ::= ty::Type tvs::[TyVar] +{ + top.transContext = makeTypeableName(ty, tvs); +} +aspect production typeableSigConstraintDcl +top::InstDclInfo ::= ty::Type ns::NamedSignature +{ + top.transContext = s"((${makeProdName(ns.fullName)})(context.undecorate())).${makeTypeableName(ty, ns.freeVariables)}"; +} +aspect production typeableSuperDcl +top::InstDclInfo ::= baseDcl::InstDclInfo +{ + baseDcl.transContextDeps = top.transContextDeps; + top.transContext = baseDcl.transContext ++ ".getType()"; +} +aspect production inhSubsetInstConstraintDcl +top::InstDclInfo ::= i1::Type i2::Type tvs::[TyVar] +{ + top.transContext = "null"; +} +aspect production inhSubsetSigConstraintDcl +top::InstDclInfo ::= i1::Type i2::Type fnsig::NamedSignature +{ + top.transContext = "null"; +} + +aspect production occursSuperDcl +top::OccursDclInfo ::= fnat::String atty::Type baseDcl::InstDclInfo +{ + baseDcl.transContextDeps = top.transContextDeps; +} + +aspect production qNameAttrOccur +top::QNameAttrOccur ::= at::QName +{ + resolvedDcl.transContextDeps = requiredContexts.transContexts; +} + +function makeConstraintDictName +String ::= s::String t::Type tvs::[TyVar] +{ + t.boundVariables = tvs; + return "d_" ++ substitute(":", "_", s) ++ "_" ++ t.transTypeName; +} + +function makeTypeableName +String ::= t::Type tvs::[TyVar] +{ + t.boundVariables = tvs; + return "typeRep_" ++ t.transTypeName; +} + +function makeInhSubsetName +String ::= i1::Type i2::Type tvs::[TyVar] +{ + i1.boundVariables = tvs; + i2.boundVariables = tvs; + return s"inhSubset_${i1.transTypeName}_${i2.transTypeName}"; +} + +function makeInstanceSuperAccessorName +String ::= s::String +{ + return "getSuper_" ++ substitute(":", "_", s); +} diff --git a/grammars/silver/compiler/translation/java/type/Type.sv b/grammars/silver/compiler/translation/java/type/Type.sv new file mode 100644 index 000000000..3b33601b0 --- /dev/null +++ b/grammars/silver/compiler/translation/java/type/Type.sv @@ -0,0 +1,197 @@ +grammar silver:compiler:translation:java:type; + +imports silver:compiler:definition:type; +imports silver:compiler:translation:java:core; + +-- The Java type corresponding to the Silver Type +synthesized attribute transType :: String; +-- The *covariant* Java generic type argument corresponding to the Silver Type +-- e.g. ? extends Object instead of Object +synthesized attribute transCovariantType :: String; +-- Java has crappy syntax for some things. +-- If we want to statically refer to the class of this type, we cannot use +-- the <> part of the type!! e.g. "Foo.class" is illegal, should be "Foo.class" +synthesized attribute transClassType :: String; +-- An environment mapping skolem constants to their runtime representation translations +autocopy attribute skolemTypeReps :: [(TyVar, String)]; +-- The runtime representation of a type, where all skolems are replaced with their provided representations, used for reification +synthesized attribute transTypeRep :: String; +-- A valid Java identifier, unique to the type +synthesized attribute transTypeName :: String; + +function transFreshTypeRep +String ::= t::Type +{ + t.skolemTypeReps = map(\ tv::TyVar -> (tv, s"freshTypeVar_${toString(tv.extractTyVarRep)}"), t.freeSkolemVars); + return t.transTypeRep; +} + +function transTypeRepWith +String ::= t::Type skolemTypeReps::[(TyVar, String)] +{ + t.skolemTypeReps = skolemTypeReps; + return t.transTypeRep; +} + +function transTypeName +String ::= te::Type +{ + te.boundVariables = te.freeVariables; + return te.transTypeName; +} + +function transTypeNameWith +String ::= te::Type tvs::[TyVar] +{ + te.boundVariables = tvs; + return te.transTypeName; +} + +attribute transType, transCovariantType, transClassType, transTypeRep, skolemTypeReps, transTypeName occurs on Type; + +aspect default production +top::Type ::= +{ + top.transType = top.transClassType; + top.transCovariantType = top.transType; +} + +aspect production varType +top::Type ::= tv::TyVar +{ + top.transCovariantType = "? extends Object"; + top.transClassType = "Object"; + top.transTypeRep = s"freshTypeVar_${toString(tv.extractTyVarRep)}"; + top.transTypeName = "a" ++ toString(positionOf(tv, top.boundVariables)); +} + +aspect production skolemType +top::Type ::= tv::TyVar +{ + top.transCovariantType = "? extends Object"; + top.transClassType = "Object"; + top.transTypeRep = lookup(tv, top.skolemTypeReps).fromJust; + top.transTypeName = "a" ++ toString(positionOf(tv, top.boundVariables)); +} + +aspect production appType +top::Type ::= c::Type a::Type +{ + top.transType = + case c.baseType of + | functionType(_, _) -> "common.NodeFactory<" ++ top.outputType.transCovariantType ++ ">" + | _ -> c.transType + end; + top.transCovariantType = + case c.baseType of + | functionType(_, _) -> "? extends common.NodeFactory<" ++ top.outputType.transCovariantType ++ ">" + | _ -> c.transCovariantType + end; + top.transClassType = c.transClassType; + top.transTypeRep = s"new common.AppTypeRep(${c.transTypeRep}, ${a.transTypeRep})"; + top.transTypeName = c.transTypeName ++ "_" ++ a.transTypeName; +} + +aspect production errorType +top::Type ::= +{ + local oops :: String = error("Attempting to translate in presence of errors"); + top.transClassType = oops; + top.transTypeRep = oops; + top.transTypeName = oops; +} + +aspect production intType +top::Type ::= +{ + top.transClassType = "Integer"; + top.transTypeRep = "new common.BaseTypeRep(\"Integer\")"; + top.transTypeName = "Integer"; +} + +aspect production boolType +top::Type ::= +{ + top.transClassType = "Boolean"; + top.transTypeRep = "new common.BaseTypeRep(\"Boolean\")"; + top.transTypeName = "Boolean"; +} + +aspect production floatType +top::Type ::= +{ + top.transClassType = "Float"; + top.transTypeRep = "new common.BaseTypeRep(\"Float\")"; + top.transTypeName = "Float"; +} + +aspect production stringType +top::Type ::= +{ + top.transClassType = "common.StringCatter"; + top.transTypeRep = "new common.BaseTypeRep(\"String\")"; + top.transTypeName = "String"; +} + +aspect production terminalIdType +top::Type ::= +{ + top.transClassType = "Integer"; + top.transTypeRep = "new common.BaseTypeRep(\"TerminalId\")"; + top.transTypeName = "TerminalId"; +} + +aspect production nonterminalType +top::Type ::= fn::String _ _ +{ + -- untightened version would be "common.Node", but we prefer the generated + -- class, e.g. silver.definition.core.NExpr + top.transClassType = makeNTName(fn); + top.transTypeRep = s"new common.BaseTypeRep(\"${fn}\")"; + top.transTypeName = substitute(":", "_", fn); +} + +aspect production terminalType +top::Type ::= fn::String +{ + top.transClassType = makeTerminalName(fn); + top.transTypeRep = s"new common.BaseTypeRep(\"${fn}\")"; + top.transTypeName = substitute(":", "_", fn); +} + +aspect production inhSetType +top::Type ::= inhs::[String] +{ + top.transClassType = error("Demanded translation of InhSet type"); + top.transTypeRep = error("Demanded TypeRep translation of InhSet type"); + top.transTypeName = substitute(":", "_", implode("_", inhs)); +} + +aspect production decoratedType +top::Type ::= te::Type i::Type +{ + -- TODO: this should probably be a generic. e.g. "DecoratedNode" + top.transType = "common.DecoratedNode"; + top.transClassType = "common.DecoratedNode"; + top.transTypeRep = s"new common.DecoratedTypeRep(${te.transTypeRep})"; + top.transTypeName = "Decorated_" ++ te.transTypeName; +} + +aspect production partiallyDecoratedType +top::Type ::= te::Type i::Type +{ + -- TODO: this should probably be a generic. e.g. "DecoratedNode" + top.transType = "common.DecoratedNode"; + top.transClassType = "common.DecoratedNode"; + top.transTypeRep = s"new common.DecoratedTypeRep(${te.transTypeRep})"; + top.transTypeName = "Decorated_" ++ te.transTypeName; +} + +aspect production functionType +top::Type ::= params::Integer namedParams::[String] +{ + top.transClassType = "common.NodeFactory"; + top.transTypeRep = + s"new common.FunctionTypeRep(${toString(params)}, new String[] {${implode(", ", map(\ n::String -> s"\"${n}\"", namedParams))}})"; + top.transTypeName = "Fn_" ++ toString(params) ++ "_" ++ implode("_", namedParams); +} diff --git a/grammars/silver/composed/Default/Main.sv b/grammars/silver/composed/Default/Main.sv deleted file mode 100644 index 085b88a6b..000000000 --- a/grammars/silver/composed/Default/Main.sv +++ /dev/null @@ -1,13 +0,0 @@ -grammar silver:composed:Default; - -import silver:host; - -parser svParse::Root { - silver:host; -} - -function main -IOVal ::= args::[String] ioin::IO -{ - return cmdLineRun(args, svParse, ioin); -} diff --git a/grammars/silver/composed/DocConfig.sv b/grammars/silver/composed/DocConfig.sv deleted file mode 100644 index 4c0b7ef75..000000000 --- a/grammars/silver/composed/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:composed; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Composed\nmenu_title: Composed\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/composed/extendedorigins/Main.sv b/grammars/silver/composed/extendedorigins/Main.sv deleted file mode 100644 index 732087ace..000000000 --- a/grammars/silver/composed/extendedorigins/Main.sv +++ /dev/null @@ -1,14 +0,0 @@ -grammar silver:composed:extendedorigins; - -import silver:host; - -parser svParse::Root { - silver:host; - silver:extension:extendedorigins; -} - -function main -IOVal ::= args::[String] ioin::IO -{ - return cmdLineRun(args, svParse, ioin); -} diff --git a/grammars/silver/composed/idetest/Analyze.sv b/grammars/silver/composed/idetest/Analyze.sv deleted file mode 100644 index 0292addc4..000000000 --- a/grammars/silver/composed/idetest/Analyze.sv +++ /dev/null @@ -1,115 +0,0 @@ --- This file defines the error demanding function that can be interfaced by IDE plugin written in Java. - ---grammar silver:analysis:binding:driver; -grammar silver:composed:idetest; - -import silver:driver; -import silver:util:cmdargs; - -import silver:definition:core; -import silver:definition:env; - -import silver:rewrite; - --- This function is mostly copied from function cmdLineRun in driver/BuildProcess.sv -function ideAnalyze -IOVal<[Message]> ::= args::[String] svParser::SVParser ioin::IO -{ - -- Figure out arguments - local argResult :: Either = parseArgs(args); - local a :: Decorated CmdArgs = case argResult of right(t) -> t end; - local argErrors :: [String] = case argResult of | left(s) -> [s] | _ -> [] end; - - -- Figure out build env from environment and args - local benvResult :: IOVal> = determineBuildEnv(a, ioin); - local benv :: BuildEnv = case benvResult.iovalue of left(t) -> t end; - local envErrors :: [String] = case benvResult.iovalue of | right(s) -> s | _ -> [] end; - - -- Let's start preparing to build - local buildGrammar :: String = head(a.buildGrammar); - local checkbuild :: IOVal<[String]> = - checkPreBuild(a, benv, buildGrammar, benvResult.io); - - -- Build! - local buildrun :: IOVal = - buildRun(svParser, a, benv, buildGrammar, checkbuild.io); - local unit :: Decorated Compilation = buildrun.iovalue; - - ---- DIFFERENCE: We do *not* run the actions in the functions. Only check for errors. - - local messages :: [Message] = flatMap(rewriteMessages, unit.allGrammars); - - return if !null(argErrors) then - ioval(ioin, [err(system_location, "Parsing failed during build. If source code/resources are changed outside IDE, refresh and rebuild is needed.")]) - else if !null(envErrors) then - ioval(benvResult.io, getSysMessages(envErrors)) - else if null(unit.grammarList) then - ioval(buildrun.io, [err(system_location, - (if buildGrammar=="" - then "No grammar is specified for compilation. Check configuration for this project." - else ("The specified grammar \"" ++ buildGrammar ++ "\" could not be found. Check configuration for this project.")) - )]) - else ioval(buildrun.io, messages); -} - --- This function is mostly copied from function cmdLineRun in driver/BuildProcess.sv -function ideGenerate -IOVal<[Message]> ::= args::[String] svParser::SVParser ioin::IO -{ - -- Figure out arguments - local argResult :: Either = parseArgs(args); - local a :: Decorated CmdArgs = case argResult of right(t) -> t end; - local argErrors :: [String] = case argResult of | left(s) -> [s] | _ -> [] end; - - -- Figure out build env from environment and args - local benvResult :: IOVal> = determineBuildEnv(a, ioin); - local benv :: BuildEnv = case benvResult.iovalue of left(t) -> t end; - local envErrors :: [String] = case benvResult.iovalue of | right(s) -> s | _ -> [] end; - - -- Let's start preparing to build - local buildGrammar :: String = head(a.buildGrammar); - local checkbuild :: IOVal<[String]> = - checkPreBuild(a, benv, buildGrammar, benvResult.io); - - -- Build! - local buildrun :: IOVal = - buildRun(svParser, a, benv, buildGrammar, checkbuild.io); - local unit :: Decorated Compilation = buildrun.iovalue; - - -- Run the resulting build actions - local actions :: IOVal = runAll(sortUnits(unit.postOps), buildrun.io); - - return ioval(actions.io, []); -- TODO: the original code did no error checking here, so I've preserved it. but wtf? - -- it seems this is only called if the previous does NOT fail. So there should be no additional errors. -} - -function getSysMessages -[Message] ::= es::[String] -{ - return if null(es) - then [] - else [err(system_location, head(es))] ++ getSysMessages(tail(es)); -} - -function rewriteMessages -[Message] ::= spec::Decorated RootSpec -{ - return map(rewriteMessage(spec.grammarSource, _), - if !null(spec.parsingErrors) - then flatMap(snd, spec.parsingErrors) - else flatMap(snd, spec.grammarErrors)); -} - --- Message for the IDE require full paths. -function rewriteMessage -Message ::= path::String m::Message -{ - return - rewriteWith( - allTopDown( - rule on Location of - | loc(file, a, b, c, d, e, f) -> loc(path ++ "/" ++ file, a, b, c, d, e, f) - end), - m).fromJust; -} - diff --git a/grammars/silver/composed/idetest/Folding.sv b/grammars/silver/composed/idetest/Folding.sv deleted file mode 100644 index a9256c709..000000000 --- a/grammars/silver/composed/idetest/Folding.sv +++ /dev/null @@ -1,90 +0,0 @@ -grammar silver:composed:idetest; - -import silver:definition:core; -import silver:definition:concrete_syntax; --concreteProductionDcl -import silver:modification:defaultattr; --aspectDefaultProduction - -synthesized attribute foldableRanges :: [Location]; -attribute foldableRanges occurs on AGDcl, AGDcls, Root; - --- Root -aspect production root -top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls -{ - -- TODO: we need a space function or something. - local span :: Location = - loc(gdcl.location.filename, - gdcl.location.line, gdcl.location.column, - ims.location.endLine, ims.location.endColumn, - gdcl.location.index, ims.location.endIndex); - top.foldableRanges = span :: ags.foldableRanges; -} - --- AGDcls -aspect production nilAGDcls -top::AGDcls ::= -{ - top.foldableRanges = []; -} - -aspect production consAGDcls -top::AGDcls ::= h::AGDcl t::AGDcls -{ - top.foldableRanges = h.foldableRanges ++ t.foldableRanges; -} - --- AGDcl -aspect production emptyAGDcl -top::AGDcl ::= -{ - top.foldableRanges = []; -} - -aspect production appendAGDcl -top::AGDcl ::= h::AGDcl t::AGDcl -{ - top.foldableRanges = h.foldableRanges ++ t.foldableRanges; -} - -aspect default production -top::AGDcl ::= -{ - top.foldableRanges = []; -} - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - -aspect production concreteProductionDcl -top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - -aspect production aspectDefaultProduction -top::AGDcl ::= 'aspect' 'default' 'production' lhs::Name '::' _ '::=' body::ProductionBody -{ - top.foldableRanges = [body.location]; -} - diff --git a/grammars/silver/composed/idetest/Main.sv b/grammars/silver/composed/idetest/Main.sv deleted file mode 100644 index 57fddaa96..000000000 --- a/grammars/silver/composed/idetest/Main.sv +++ /dev/null @@ -1,155 +0,0 @@ -grammar silver:composed:idetest; - -import silver:definition:env; -import silver:host; -import silver:util:cmdargs; -import silver:util:raw:treemap as tm; - --- NOTE: this is needed for the correct generation of IDE, --- even if we just use an empty IDE declaration block. -import ide; - --- Just re-use these parser declarations, instead of duplicating them here. -import silver:composed:Default only svParse; - --- This function is not used by IDE -function main -IOVal ::= args::[String] ioin::IO -{ - return cmdLineRun(args, svParse, ioin); -} - --- IDE declaration block -temp_imp_ide_dcl svParse ".sv" { - builder analyze; - postbuilder generate; - exporter export; - folder fold; - - property grammar_to_compile string required display="Grammar"; - property enable_mwda string default="false" display="Enable MWDA"; - - wizard new file { - stub generator getStubForNewFile; --a function whose signature must be "String ::= args::[IdeProperty]" - property declared_grammar string required display="Grammar"; - } - - name "Silver"; - version "0.2.3"; - resource grammars "../../../../grammars/"; -- I have "../grammars" to be explicit about what's going on here. - resource jars "../../../../jars/"; -} - --- Declarations of IDE functions referred in decl block. - -function analyze -IOVal<[Message]> ::= project::IdeProject args::[IdeProperty] i::IO -{ - local argio :: IOVal<[String]> = getArgStrings(args, project, i); - - local ru :: IOVal<[Message]> = ideAnalyze(argio.iovalue, svParse, argio.io); - - return ru; -} - -function generate -IOVal<[Message]> ::= project::IdeProject args::[IdeProperty] i::IO -{ - local argio :: IOVal<[String]> = getArgStrings(args, project, i); - - local ru :: IOVal<[Message]> = ideGenerate(argio.iovalue, svParse, argio.io); - - return ru; - -} - -global system_location :: Location = loc("", -1, -1, -1, -1, -1, -1); - -function export -IOVal<[Message]> ::= project::IdeProject args::[IdeProperty] i::IO -{ - local proj_path :: IOVal = getProjectPath(project, i); - local gen_path :: IOVal = getGeneratedPath(project, proj_path.io); - - local pkgName :: String = makeName(head(getGrammarToCompile(args))); - local buildFile :: String = gen_path.iovalue ++ "/build.xml"; - local jarFile :: String = gen_path.iovalue ++ "/" ++ pkgName ++ ".jar"; - local targetFile :: String = proj_path.iovalue ++ "/" ++ pkgName ++ ".jar"; - - local fileExists :: IOVal = isFile(buildFile, gen_path.io); - - local jarExists :: IOVal = isFile(jarFile, ant(buildFile, "", "", fileExists.io)); - - return if !fileExists.iovalue then - ioval(fileExists.io, [err(system_location, "build.xml doesn't exist. Has the project been successfully built before?")]) - else if !jarExists.iovalue then - ioval(jarExists.io, [err(system_location, "Ant failed to generate the jar.")]) - else - ioval(refreshProject(project, copyFile(jarFile, targetFile, jarExists.io)), []); -} - -function fold -[Location] ::= cst::Root -{ - -- Dummy values - cst.config = decorate errorCmdArgs("") with {}; - cst.compiledGrammars = tm:empty(compareString); - cst.grammarName = ""; - cst.env = emptyEnv(); - cst.globalImports = emptyEnv(); - cst.grammarDependencies = []; - return cst.foldableRanges; -- see ./Folding.sv -} - -function getStubForNewFile -String ::= args::[IdeProperty] -{ - local gram :: Maybe = lookupIdeProperty("declared_grammar", args); - return if gram.isJust - then "grammar " ++ gram.fromJust ++ ";\n\n" - else ""; -} - -function getArgStrings -IOVal<[String]> ::= args::[IdeProperty] project::IdeProject io::IO -{ - local jarsio :: IOVal = getIdeResource("jars", io); - local grammarsio :: IOVal = getIdeResource("grammars", jarsio.io); - local proj_path :: IOVal = getProjectPath(project, grammarsio.io); - local gen_path :: IOVal = getGeneratedPath(project, proj_path.io); - - local compile_args :: [String] = - [ - "--silver-home", jarsio.iovalue ++ "..", - "-G", gen_path.iovalue, - "-I", proj_path.iovalue, - --"-I", grammarsio.iovalue, -- This actually get automatically added, by virtue of silver home finding grammars under it - "--build-xml-location", gen_path.iovalue ++ "/build.xml"] ++ - (if getEnableMWDA(args) then ["--warn-all"] else []) ++ - getGrammarToCompile(args); - - return ioval(gen_path.io, compile_args); -} - -function getGrammarToCompile -[String] ::= args::[IdeProperty] -{ - return - if(null(args)) - then [] - else if head(args).propName == "grammar_to_compile" - then [head(args).propValue] - else getGrammarToCompile(tail(args)); -} - -function getEnableMWDA -Boolean ::= args::[IdeProperty] -{ - return - if(null(args)) - then false - else if head(args).propName == "enable_mwda" - then head(args).propValue == "true" - else getEnableMWDA(tail(args)); -} - diff --git a/grammars/silver/core/AST.sv b/grammars/silver/core/AST.sv new file mode 100644 index 000000000..5a26680a5 --- /dev/null +++ b/grammars/silver/core/AST.sv @@ -0,0 +1,66 @@ +grammar silver:core; + +@@{- + - @warning This grammar contains only the definitions of the AST nonterminals, needed by the runtime library. + - The full reflection library that users should import is `silver:reflect` + -} + +tracked nonterminal AST; + +abstract production nonterminalAST +top::AST ::= prodName::String children::ASTs annotations::NamedASTs +{} + +abstract production terminalAST +top::AST ::= terminalName::String lexeme::String location::Location +{} + +abstract production listAST +top::AST ::= vals::ASTs +{} + +abstract production stringAST +top::AST ::= s::String +{} + +abstract production integerAST +top::AST ::= i::Integer +{} + +abstract production floatAST +top::AST ::= f::Float +{} + +abstract production booleanAST +top::AST ::= b::Boolean +{} + +abstract production anyAST +top::AST ::= x::a +{} + +tracked nonterminal ASTs; + +abstract production consAST +top::ASTs ::= h::AST t::ASTs +{} + +abstract production nilAST +top::ASTs ::= +{} + +tracked nonterminal NamedASTs; + +abstract production consNamedAST +top::NamedASTs ::= h::NamedAST t::NamedASTs +{} + +abstract production nilNamedAST +top::NamedASTs ::= +{} + +tracked nonterminal NamedAST; + +abstract production namedAST +top::NamedAST ::= n::String v::AST +{} diff --git a/grammars/silver/core/Alternative.sv b/grammars/silver/core/Alternative.sv new file mode 100644 index 000000000..ebabda7dc --- /dev/null +++ b/grammars/silver/core/Alternative.sv @@ -0,0 +1,48 @@ +grammar silver:core; + +@{- +The Alt type class identifies an associative operation on a type constructor. +It is similar to Semigroup, except that it applies to types of kind * -> *, +like Maybe or [], rather than concrete types String or [a]. + +Instances should satisfy the following: + +Associativity + alt(alt(x, y), z) == alt(x, alt(y, z)) +Distributivity + map(f, alt(x, y)) == alt(map(f, x), map(f, y)) +-} +class Functor f => Alt f { + alt :: (f ::= f f); +} + +@{- +The Plus type class extends the Alt type class with a value that should be the +left and right identity for (<|>). + +It is similar to Monoid, except that it applies to types of kind * -> *, +like Array or List, rather than concrete types like String or Number. + +Instances should satisfy the following laws: + +Left identity + alt(empty, x) == x +Right identity + alt(x, empty) == x +Annihilation + map(f, empty) == empty +-} +class Alt f => Plus f { + empty :: f; +} + +@{- +The Alternative class is for members of Plus that are also Applicative functors, +and specifies some additional laws: + +Distributivity + ap(alt(f, g), x) == alt(ap(f, x), ap(g, x)) +Annihilation + ap(empty, x) = empty +-} +class Applicative f, Plus f => Alternative f {} diff --git a/grammars/silver/core/Applicative.sv b/grammars/silver/core/Applicative.sv new file mode 100644 index 000000000..bf533a7aa --- /dev/null +++ b/grammars/silver/core/Applicative.sv @@ -0,0 +1,50 @@ +grammar silver:core; + +@{- +Applicative functors extend Apply with a means to lift a value of type a into an f; +thus together with the apply operation, it is possible to lift a function of artibrary arity to +work on values wrapped in the type constructor f. + +Instances should satisfy the following: + +Identity + ap(pure(id), x) = x +Composition + ap(ap(ap(pure(compose), f), g), h) = ap(ap(f, g), h) +Homomorphism + ap(pure(f), pure(x)) = pure(f(x)) +Interchange + ap(u, pure(y)) = ap(pure(\f -> f(y)), u) +-} +class Apply f => Applicative f { + pure :: (f ::= a); +} + +@{- + - Prefer `map` to this function. However, it can be useful to get a `Functor` + - instance for free, given an existing `Applicative` instance. + -} +function liftA1 +Applicative f => f ::= f::(b ::= a) x::f +{ return ap(pure(f), x); } + +function when_ +Applicative f => f ::= cond::Boolean body::f +{ return if cond then body else pure(unit()); } + +function unless +Applicative f => f ::= cond::Boolean body::f +{ return if cond then pure(unit()) else body; } + +-- These should be factored out into a Traversable type class, eventually. +function traverseA +Applicative m => m<[b]> ::= f::(m ::= a) lst::[a] +{ return foldr(lift2(cons, _, _), pure([]), map(f, lst)); } + +function traverse_ +Applicative m => m<()> ::= f::(m<()> ::= a) lst::[a] +{ return foldr(applySecond, pure(()), map(f, lst)); } + +function sequence +Applicative m => m<[a]> ::= lst::[m] +{ return foldr(lift2(cons, _, _), pure([]), lst); } diff --git a/grammars/silver/core/Apply.sv b/grammars/silver/core/Apply.sv new file mode 100644 index 000000000..53c766795 --- /dev/null +++ b/grammars/silver/core/Apply.sv @@ -0,0 +1,38 @@ +grammar silver:core; + +@{- +Functors with an apply operation, that can be used to lift functions of two or more arguments to +work on values wrapped in the type constructor f. + +Instances should satisfy the following: + +Associative Composition + ap(ap(map(compose, f), g), h) = ap(f, ap(g, h)) +-} +class Functor f => Apply f { + ap :: (f ::= f<(b ::= a)> f); +} + +function applyFirst +Apply f => f ::= a::f b::f +{ return ap(map(\x::a -> \y::b -> x, a), b); } + +function applySecond +Apply f => f ::= a::f b::f +{ return ap(map(\x::a -> \y::b -> y, a), b); } + +function lift2 +Apply f => f ::= f::(c ::= a b) x::f y::f +{ return ap(map(curry(f), x), y); } + +function lift3 +Apply f => f ::= f::(d ::= a b c) x::f y::f z::f +{ return ap(ap(map(curry3(f), x), y), z); } + +function lift4 +Apply f => f ::= f::(e ::= a b c d) x::f y::f z::f p::f +{ return ap(ap(ap(map(curry4(f), x), y), z), p); } + +function lift5 +Apply f => f ::= f::(g ::= a b c d e) x::f y::f z::f p::f q::f +{ return ap(ap(ap(ap(map(curry5(f), x), y), z), p), q); } diff --git a/grammars/silver/core/Bind.sv b/grammars/silver/core/Bind.sv new file mode 100644 index 000000000..9df043f5b --- /dev/null +++ b/grammars/silver/core/Bind.sv @@ -0,0 +1,34 @@ +grammar silver:core; + +@{- +Extends the Apply type class with a "bind" operation that composes computations in sequence, +using the return value of one computation to determine the next computation. + +Instances should satisfy the following: + +Associativity + bind(bind(x, f), g) = bind(x, \k -> bind(f(k), g)) +-} +class Apply m => Bind m { + bind :: (m ::= m (m ::= a)); +} + +function bindFlipped +Bind m => m ::= f::(m ::= a) x::m +{ return bind(x, f); } + +function join +Bind m => m ::= x::m> +{ return bind(x, \x::m -> x); } + +function composeKleisli +Bind m => m ::= f::(m ::= a) g::(m ::= b) x::a +{ return bind(f(x), g); } + +function composeKleisliFlipped +Bind m => m ::= g::(m ::= b) f::(m ::= a) x::a +{ return composeKleisli(f, g, x); } + +function ifM +Bind m => m ::= c::m t::m e::m +{ return bind(c, \c::Boolean -> if c then t else e); } diff --git a/grammars/silver/core/BooleanAlgebra.sv b/grammars/silver/core/BooleanAlgebra.sv new file mode 100644 index 000000000..509f7b221 --- /dev/null +++ b/grammars/silver/core/BooleanAlgebra.sv @@ -0,0 +1,12 @@ +grammar silver:core; + +@{- This typeclass guarantees that a Heyting algebra behaves exactly like the + - booleans. It doesn't provide any operators, but may be useful for algorithms. + - + - Laws: + - + - * Excluded Middle: `disj(x, not(x)) = true` + - * Inverse Excluded Middle: `conj(x, not(x)) = false` + - * Double Negation Elimination: `not(not(x)) = x` + -} +class HeytingAlgebra a => BooleanAlgebra a {} diff --git a/grammars/silver/core/CommutativeRing.sv b/grammars/silver/core/CommutativeRing.sv new file mode 100644 index 000000000..e6b8af88f --- /dev/null +++ b/grammars/silver/core/CommutativeRing.sv @@ -0,0 +1,11 @@ +grammar silver:core; + +@{- A typeclass for types where multiplication is guaranteed to be commutative. + - + - Laws: + - + - * Commutativity of Multiplication: `mul(x, y) = mul(y, x)` + -} +class Ring a => CommutativeRing a {} + +instance CommutativeRing Integer {} diff --git a/grammars/silver/core/ConvertablePrim.sv b/grammars/silver/core/ConvertablePrim.sv new file mode 100644 index 000000000..9f529b828 --- /dev/null +++ b/grammars/silver/core/ConvertablePrim.sv @@ -0,0 +1,104 @@ +grammar silver:core; + +@{- + Primitive types that can be converted into other primitive types. + + This class provides the overloaded toString, toInteger, toFloat and toBoolean functions. +-} +class ConvertiblePrim a { + @{- Convert a value into a String -} + toString :: (String ::= a); + + @{- Convert a value into an Integer -} + toInteger :: (Integer ::= a); + + @{- Convert a value into a Float -} + toFloat :: (Float ::= a); + + @{- Convert a value into a Boolean -} + toBoolean :: (Boolean ::= a); +} + +instance ConvertiblePrim String { + toString = id; + toInteger = stringToInteger; + toFloat = stringToFloat; + toBoolean = stringToBoolean; +} + +function stringToInteger +Integer ::= x::String +{ + return error("foreign function"); +} foreign { + "java" : return "Integer.valueOf(%x%.toString())"; +} + +function stringToFloat +Float ::= x::String +{ + return error("foreign function"); +} foreign { + "java" : return "Float.valueOf(%x%.toString())"; +} + +function stringToBoolean +Boolean ::= x::String +{ + return error("foreign function"); +} foreign { + "java" : return "Boolean.valueOf(%x%.toString())"; +} + +instance ConvertiblePrim Integer { + toString = integerToString; + toInteger = id; + toFloat = integerToFloat; + toBoolean = \ x::Integer -> x != 0; +} + +function integerToString +String ::= x::Integer +{ + return error("foreign function"); +} foreign { + "java" : return "new common.StringCatter(String.valueOf(%x%))"; +} + +function integerToFloat +Float ::= x::Integer +{ + return error("foreign function"); +} foreign { + "java" : return "Float.valueOf(((Integer)%x%).floatValue())"; +} + +instance ConvertiblePrim Float { + toString = floatToString; + toInteger = floatToInteger; + toFloat = id; + toBoolean = \ x::Float -> x != 0.0; +} + +function floatToString +String ::= x::Float +{ + return error("foreign function"); +} foreign { + "java" : return "new common.StringCatter(String.valueOf(%x%))"; +} + +function floatToInteger +Integer ::= x::Float +{ + return error("foreign function"); +} foreign { + "java" : return "Integer.valueOf(((Float)%x%).intValue())"; +} + +instance ConvertiblePrim Boolean { + toString = \ x::Boolean -> if x then "true" else "false"; + toInteger = \ x::Boolean -> if x then 1 else 0; + toFloat = \ x::Boolean -> if x then 1.0 else 0.0; + toBoolean = id; +} diff --git a/grammars/silver/core/DivisionRing.sv b/grammars/silver/core/DivisionRing.sv new file mode 100644 index 000000000..9ae4e0f35 --- /dev/null +++ b/grammars/silver/core/DivisionRing.sv @@ -0,0 +1,31 @@ +grammar silver:core; + +@{- A typeclass for rings that have a reciprocal operation, which gives the + - multiplicative inverse of non-zero elements. + - + - Laws: + - + - * Non-Zero Ring: `one != zero` + - * Non-Zero Multiplicative Left Inverse: `(a != 0) -> mul(recip(a), a) = one` + - * Non-Zero Multiplicative Right Inverse: `(a != 0) -> mul(a, recip(a)) = one` + -} +class Ring a => DivisionRing a { + @{- The reciprocal, i.e. the inverse for the multiplicative group. -} + recip :: (a ::= a); +} + +@{- Division implemented as `(1/r) * l`. + - + - Iff the type is a commutative ring, this is equivalent to `rightDiv`. + -} +function leftDiv +DivisionRing a => a ::= l::a r::a +{ return mul(recip(r), l); } + +@{- Division implemented as `l * (1/r)`. + - + - Iff the type is a commutative ring, this is equivalent to `leftDiv`. + -} +function rightDiv +DivisionRing a => a ::= l::a r::a +{ return mul(l, recip(r)); } diff --git a/grammars/silver/core/DocConfig.sv b/grammars/silver/core/DocConfig.sv new file mode 100644 index 000000000..3ae3562cd --- /dev/null +++ b/grammars/silver/core/DocConfig.sv @@ -0,0 +1,7 @@ +grammar silver:core; + +@@{- + - @config split + - @config grammarWeight -1 + - @config grammarTitle "[silver:core] (stdlib)" + -} diff --git a/grammars/silver/core/Either.sv b/grammars/silver/core/Either.sv new file mode 100644 index 000000000..6f5155054 --- /dev/null +++ b/grammars/silver/core/Either.sv @@ -0,0 +1,234 @@ +grammar silver:core; + +synthesized attribute fromLeft :: a; +synthesized attribute fromRight :: a; +synthesized attribute isLeft :: Boolean; +synthesized attribute isRight :: Boolean; + +@{-- + - The basic sum type, counterpart to Pair. + - + - Occasionally used as a poor-quality "result or error" type. + - By convention, the error type is the FIRST type, and the + - expected return value is the second. + - e.g. `Either` + - + - Inspect it's state with isLeft::Boolean and isRight::Boolean, and + - access it's state with fromLeft::a, fromRight::b (which panic if incorrect) + -} +nonterminal Either with fromLeft, fromRight, isLeft, isRight; + +@{- + - Left case for Either. + -} +abstract production left +top::Either ::= value::a +{ + top.fromLeft = value; + top.fromRight = error("fromRight accessed on a Either that was actually left!"); + top.isLeft = true; + top.isRight = false; +} + +@{- + - Right case for Either. + -} +abstract production right +top::Either ::= value::b +{ + top.fromLeft = error("fromRight accessed on a Either that was actually left!"); + top.fromRight = value; + top.isLeft = false; + top.isRight = true; +} + +instance Functor Either { + map = \ f::(c ::= b) e::Either -> + case e of + | right(x) -> right(f(x)) + | left(x) -> left(x) + end; +} + +instance Apply Either { + ap = \ ef::Either e::Either -> + case ef of + | left(x) -> left(x) + | right(f) -> map(f, e) + end; +} + +instance Applicative Either { + pure = right; +} + +instance Bind Either { + bind = \ e::Either fn::(Either ::= b) -> + case e of + | left(x) -> left(x) + | right(x) -> fn(x) + end; +} + +instance Monad Either {} + +instance MonadFail Either { + fail = left; +} + +function mfixEither +Either ::= f::(Either ::= b) +{ + local x::Either = f(x.fromRight); + return x; +} + +instance MonadFix Either { + mfix = mfixEither; +} + +instance Alt Either { + alt = \ e1::Either e2::Either -> + case e1, e2 of + | right(x), _ -> right(x) + | _, right(x) -> right(x) + -- If they're both left, arbitrarily take the first one + | _, _ -> e1 + end; +} + +@{- + - Monad transformer corresponding to Either. + - Note that the type arguments are in this order because Silver type + - constructors are curried and must be partially applied in order, and we need to have + - instance Functor MaybeT (and friends) and instance MonadTrans EitherT. + - + - @param e The "error" result type, corresponding to the left constructor + - @param m The monad type to be transformed + - @param a The "success" result type, corresponding to the right constructor + -} +nonterminal EitherT *) a> with run>>; +abstract production eitherT +top::EitherT ::= x::m> +{ + top.run = x; +} + +@{-- + - Transform the computation inside an EitherT. + -} +function mapEitherT +EitherT ::= f::(n> ::= m>) x::EitherT +{ + return eitherT(f(x.run)); +} + +instance Functor m => Functor EitherT { + map = \ f::(b ::= a) x::EitherT -> mapEitherT(map(map(f, _), _), x); +} + +instance Monad m => Apply EitherT { + ap = \ mf::EitherT mx::EitherT -> eitherT( + do { + fRes::Either <- mf.run; + case fRes of + | left(y) -> pure(left(y)) + | right(f) -> do { + xRes::Either <- mx.run; + case xRes of + | left(y) -> pure(left(y)) + | right(x) -> pure(right(f(x))) + end; + } + end; + }); +} + +@{-- + - Return an "error" result. This is analagous to left for Either, + - as pure is analagous to right for Either. + -} +global throwError::Applicative m => (EitherT ::= e) = compose(eitherT, compose(pure, left)); + +instance Monad m => Applicative EitherT { + pure = compose(eitherT, compose(pure, right)); +} + +instance Monad m => Bind EitherT { + bind = \ x::EitherT f::(EitherT ::= a) -> eitherT( + do { + res :: Either <- x.run; + case res of + | left(y) -> pure(left(y)) + | right(val) -> f(val).run + end; + }); +} + +instance Monad m => Monad EitherT {} + +instance Monad m => MonadFail EitherT { + fail = \ msg::String -> eitherT(pure(left(msg))); +} + +instance Monad m => Alt EitherT { + alt = \ x::EitherT y::EitherT -> eitherT( + do { + v :: Either <- x.run; + case v of + | left(_) -> y.run + | right(_) -> pure(v) + end; + }); +} + +instance MonadTrans EitherT { + lift = \ x::m -> eitherT(map(right, x)); +} + +@{-- + - Order preserving partitioning of a list of eithers into a pair + - of lists of the two different results. + -} +function partitionEithers +Pair<[a] [b]> ::= l::[Either] +{ + local recurse :: Pair<[a] [b]> = partitionEithers(tail(l)); + + return case l of + | [] -> pair([], []) + | left(a) :: _ -> pair(a :: recurse.fst, recurse.snd) + | right(b) :: _ -> pair(recurse.fst, b :: recurse.snd) + end; +} + +@{-- + - Returns the left value, or the default if there is no left value. + - + - @param e The either being discriminated + - @param o The fallback value + -} +function fromLeft +a ::= e::Either o::a +{ + return case e of + | left(a) -> a + | right(_) -> o + end; +} + +@{-- + - Returns the right value, or the default if there is no right value. + - + - @param e The either being discriminated + - @param o The fallback value + -} +function fromRight +b ::= e::Either o::b +{ + return case e of + | left(_) -> o + | right(b) -> b + end; +} + diff --git a/grammars/silver/core/Eq.sv b/grammars/silver/core/Eq.sv new file mode 100644 index 000000000..8cd98e836 --- /dev/null +++ b/grammars/silver/core/Eq.sv @@ -0,0 +1,207 @@ +grammar silver:core; + +@{- +Eq represents equality/inequality relationships between data. + +Laws are based on Haskell's Eq type class: + +Reflexivity + x == x = true +Symmetry + x == y = y == x +Transitivity + if x == y && y == z = true, then x == z = true +Substitutivity + if x == y = true and f is a "public" function whose return type is an instance of Eq, then f x == f y = true +Negation + x != y = !(x == y) + +Minimal complete definition: either == or !=. +-} +class Eq a { + eq :: (Boolean ::= a a) = \ x::a y::a -> !(x != y); + neq :: (Boolean ::= a a) = \ x::a y::a -> !(x == y); +} + +destruct attribute compareTo; +equality attribute isEqual with compareTo; + +instance attribute compareTo occurs on a, + attribute isEqual {compareTo} occurs on a + => Eq a { + eq = \ x::a y::a -> decorate x with {compareTo = decorate y with {};}.isEqual; +} + +instance typeError "Equality is not supported for Decorated types" + => Eq Decorated a with i { + eq = error("type error"); +} + +instance Eq Integer { + eq = eqInteger; + neq = neqInteger; +} + +function eqInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% == (int)%y%)"; +} + +function neqInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% != (int)%y%)"; +} + +instance Eq Float { + eq = eqFloat; + neq = neqFloat; +} + +function eqFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% == (float)%y%)"; +} + +function neqFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% != (float)%y%)"; +} + +instance Eq Boolean { + eq = eqBoolean; + neq = neqBoolean; +} + +function eqBoolean +Boolean ::= x::Boolean y::Boolean +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% == (boolean)%y%)"; +} + +function neqBoolean +Boolean ::= x::Boolean y::Boolean +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% != (boolean)%y%)"; +} + +instance Eq String { + eq = eqString; + neq = neqString; +} + +function eqString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x%.equals(%y%))"; +} + +function neqString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(!%x%.equals(%y%))"; +} + +instance Eq TerminalId { + eq = eqTerminalId; + neq = neqTerminalId; +} + +function eqTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% == (int)%y%)"; +} + +function neqTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% != (int)%y%)"; +} + +instance Eq ByteArray { + eq = eqByteArray; +} + +function eqByteArray +Boolean ::= x::ByteArray y::ByteArray +{ + return error("Foreign function"); +} foreign { + "java" : return "java.util.Arrays.equals(%x%, %y%)"; +} + +instance Eq a => Eq [a] { + eq = \ x::[a] y::[a] -> length(x) == length(y) && all(zipWith(eq, x, y)); + neq = \ x::[a] y::[a] -> length(x) != length(y) || any(zipWith(neq, x, y)); +} + +instance Eq a => Eq Maybe { + eq = \ x::Maybe y::Maybe -> + case x, y of + | just(w), just(z) -> w == z + | nothing(), nothing() -> true + | _, _ -> false + end; +} + +instance Eq a, Eq b => Eq Pair { + eq = \ x::Pair y::Pair -> x.fst == y.fst && x.snd == y.snd; + neq = \ x::Pair y::Pair -> x.fst != y.fst || x.snd != y.snd; +} + +instance Eq a, Eq b => Eq Either { + eq = \ x::Either y::Either -> + case x, y of + | left(w), left(z) -> w == z + | right(w), right(z) -> w == z + | _, _ -> false + end; +} + +instance Eq Unit { + eq = \ Unit Unit -> true; +} + +instance Eq Location { + eq = \ l1::Location l2::Location -> + -- TODO: We could probably just compare based on filename and index + -- For the moment, though, use line & column instead. + l1.filename == l2.filename && + l1.line == l2.line && + l1.column == l2.column; +} + +@{- + - Compute the fixed point of a function by repeatedly applying it + - until its result remains constant. + -} +function fixpoint +Eq a => a ::= f::(a ::= a) x::a +{ + local fx :: a = f(x); + return if fx == x then fx else fixpoint(f, fx); +} diff --git a/grammars/silver/core/EuclideanRing.sv b/grammars/silver/core/EuclideanRing.sv new file mode 100644 index 000000000..c508fb4e9 --- /dev/null +++ b/grammars/silver/core/EuclideanRing.sv @@ -0,0 +1,47 @@ +grammar silver:core; + +@{- Provides the `/` and `%` operators. + - + - Laws: + - + - * Integral Domain (TODO: are these equivalent?): + - * `one != zero` + - * `(a != zero && b != zero) -> mul(a, b) != zero` + - * Non-negativity of `degree`: `degree(x) >= 0` + - * Quotient: `(b != zero) -> (add(mul(div(a, b), b), mod(a, b)) = a)` + - * `degree`-`mod` interaction: `(b != zero) -> (mod(a, b) = 0 || degree(mod(a, b)) < degree(b))` + - * `degree`-`mul` interaction: `(a != zero && b != zero) -> (degree(a) <= degree(mul(a, b)))` + -} +class CommutativeRing a => EuclideanRing a { + degree :: (Integer ::= a); + div :: (a ::= a a); + mod :: (a ::= a a); +} + +instance EuclideanRing Integer { + degree = \n::Integer -> if n < 0 then -n else n; + div = \a::Integer b::Integer -> a / b; + mod = \a::Integer b::Integer -> a % b; +} + +@{- Computes the greatest common divisor of two numbers. -} +function gcd +Eq a, EuclideanRing a => a ::= a::a b::a +{ + return + if b == zero then + a + else + gcd(b, mod(a, b)); +} + +@{- Computes the least common multiple of two numbers. -} +function lcm +Eq a, EuclideanRing a => a ::= a::a b::a +{ + return + if a == zero || b == zero then + zero + else + div(mul(a, b), gcd(a, b)); +} diff --git a/grammars/silver/core/Field.sv b/grammars/silver/core/Field.sv new file mode 100644 index 000000000..10e54576a --- /dev/null +++ b/grammars/silver/core/Field.sv @@ -0,0 +1,14 @@ +grammar silver:core; + +@{- See [Wikipedia](https://en.wikipedia.org/wiki/Field_(mathematics)). + - + - All types that are both a `DivisionRing` and a `CommutativeRing` are + - automatically `Field`s: the `EuclideanRing` instance can choose `leftDiv` + - or `rightDiv` as its `div`, `const 0` as its `mod`, and `const 1` as its + - `degree`. (TODO: This and `EuclideanRing` should be derivable based on that.) + - + - Laws: + - + - * Non-Zero Multiplicative Inverse: `mod(a, b) = zero` + -} +class DivisionRing a, EuclideanRing a => Field a {} diff --git a/grammars/silver/core/Function.sv b/grammars/silver/core/Function.sv new file mode 100644 index 000000000..b009923bf --- /dev/null +++ b/grammars/silver/core/Function.sv @@ -0,0 +1,43 @@ +grammar silver:core; + +function id +a ::= x::a +{ return x; } + +function compose +(c ::= a) ::= f1::(c ::= b) f2::(b ::= a) +{ + return \ x::a -> f1(f2(x)); +} + +function curry +((c ::= b) ::= a) ::= f::(c ::= a b) +{ return \x::a -> \y::b -> f(x, y); } + +function curry3 +(((d ::= c) ::= b) ::= a) ::= f::(d ::= a b c) +{ return \x::a -> \y::b -> \z::c -> f(x, y, z); } + +function curry4 +((((e ::= d) ::= c) ::= b) ::= a) ::= f::(e ::= a b c d) +{ return \x::a -> \y::b -> \z::c -> \p::d -> f(x, y, z, p); } + +function curry5 +(((((f ::= e) ::= d) ::= c) ::= b) ::= a) ::= f::(f ::= a b c d e) +{ return \x::a -> \y::b -> \z::c -> \p::d -> \q::e -> f(x, y, z, p, q); } + +function uncurry +c ::= f::((c ::= b) ::= a) x::a y::b +{ return f(x)(y); } + +function uncurry3 +d ::= f::(((d ::= c) ::= b) ::= a) x::a y::b z::c +{ return f(x)(y)(z); } + +function uncurry4 +e ::= f::((((e ::= d) ::= c) ::= b) ::= a) x::a y::b z::c p::d +{ return f(x)(y)(z)(p); } + +function uncurry5 +f ::= f::(((((f ::= e) ::= d) ::= c) ::= b) ::= a) x::a y::b z::c p::d q::e +{ return f(x)(y)(z)(p)(q); } diff --git a/grammars/silver/core/Functor.sv b/grammars/silver/core/Functor.sv new file mode 100644 index 000000000..46b992623 --- /dev/null +++ b/grammars/silver/core/Functor.sv @@ -0,0 +1,35 @@ +grammar silver:core; + +@{- +A type f is a Functor if it provides a function map which, given any types a and b lets you apply +any function from (a -> b) to turn an f into an f, preserving the structure of f. +Furthermore f needs to adhere to the following: + +Identity + map(id, x) == x +Composition + map(compose(f, g), x) == map(f, map(g, x)) +-} +class Functor f { + map :: (f ::= (b ::= a) f); +} + +function mapFlipped +Functor f => f ::= x::f f::(b ::= a) +{ return map(f, x); } + +function void +Functor f => f ::= x::f +{ return map(\y::a -> unit(), x); } + +function voidLeft +Functor f => f ::= x::f y::b +{ return map(\z::a -> y, x); } + +function voidRight +Functor f => f ::= x::a y::f +{ return map(\z::b -> x, y); } + +function flap +Functor f => f ::= fs::f<(b ::= a)> x::a +{ return map(\f::(b ::= a) -> f(x), fs); } diff --git a/grammars/silver/core/Group.sv b/grammars/silver/core/Group.sv new file mode 100644 index 000000000..3eba7196d --- /dev/null +++ b/grammars/silver/core/Group.sv @@ -0,0 +1,39 @@ +grammar silver:core; + +@{- They're from math. Yeah. See [Wikipedia](https://en.wikipedia.org/wiki/Group_(mathematics)). + - + - Laws: + - + - * Left Cancellation: `append(invert(x), x) = mempty` + - * Right Cancellation: `append(x, invert(x)) = mempty` + -} +class Monoid g => Group g { + invert :: (g ::= g); +} + +@{- Computes the integer exponent of some group (treating the semigroup as + - multiplication, as is standard in algebra). + -} +function power +Group g => g ::= x::g n::Integer +{ + return + if n < 0 then + invert(powerHelper(x, -n)) + else + powerHelper(x, n); +} + +@{- @hide -} +function powerHelper +Group g => g ::= x::g n::Integer +{ + local xPowN2 :: g = powerHelper(x, n / 2); + return + if n == 0 then + mempty + else if n % 2 == 0 then + append(xPowN2, xPowN2) + else + append(x, append(xPowN2, xPowN2)); +} diff --git a/grammars/core/HackyUnParse.sv b/grammars/silver/core/HackyUnParse.sv similarity index 94% rename from grammars/core/HackyUnParse.sv rename to grammars/silver/core/HackyUnParse.sv index 6c7579b20..8ee345554 100644 --- a/grammars/core/HackyUnParse.sv +++ b/grammars/silver/core/HackyUnParse.sv @@ -1,6 +1,6 @@ -grammar core; +grammar silver:core; -{-- +@{-- - hackUnparse takes any value, and produce a string that represents - that value. It is mainly useful for debugging. - diff --git a/grammars/silver/core/HeytingAlgebra.sv b/grammars/silver/core/HeytingAlgebra.sv new file mode 100644 index 000000000..eee0b4a9b --- /dev/null +++ b/grammars/silver/core/HeytingAlgebra.sv @@ -0,0 +1,54 @@ +grammar silver:core; + +@{- This is the typeclass that provides the `true` and `false` constants, as + - well as the `&&`, `||`, and `!` operators. + - + - Laws: + - + - * Associativity: + - * `conj(x, conj(y, z)) = conj(conj(x, y), z)` + - * `disj(x, disj(y, z)) = disj(disj(x, y), z)` + - * Commutativity: + - * `conj(x, y) = conj(y, x)` + - * `disj(x, y) = disj(y, x)` + - * Absorption: + - * `conj(x, disj(x, y)) = x` + - * `disj(x, conj(x, y)) = x` + - * Idempotence: + - * `conj(x, x) = x` + - * `disj(x, x) = x` + - * Identity: + - * `conj(x, true) = x` + - * `disj(x, false) = x` + - * Implication: + - * `implies(x, x) = true` + - * `conj(x, implies(x, y)) = conj(x, y)` + - * `conj(y, implies(x, y)) = y` + - * `implies(x, conj(y, z)) = conj(implies(x, y), implies(x, z))` + - * Complement: `not(x) = implies(x, false)` + -} +class HeytingAlgebra a { + -- TODO: Rename to false + @{- The false constant. -} + ff :: a; + -- TODO: Rename to true + @{- The true constant. -} + tt :: a; + @{- Implication. + - + - Note that it is not the case that `implies(x, y) = disj(not(p), q)` for + - all Heyting algebras. If you require this property, you want a + - `BooleanAlgebra`. + -} + implies :: (a ::= a a); + @{- Conjunction. This function corresponds to the `&&` operator. -} + conj :: (a ::= a a); + @{- Disjunction. This function corresponds to the `||` operator. -} + disj :: (a ::= a a); + @{- Complement. This function corresponds to the `!` operator. + - + - Note that it is not the case that `not(not(x)) = x` for all Heyting + - algebras. If you require this property, you want a `BooleanAlgebra`. + -} + not :: (a ::= a); +} diff --git a/grammars/silver/core/IO.sv b/grammars/silver/core/IO.sv new file mode 100644 index 000000000..39b803001 --- /dev/null +++ b/grammars/silver/core/IO.sv @@ -0,0 +1,316 @@ +grammar silver:core; + +@{- + - Representation of a monadic IO action. + - + - The stateIn/stateOut threading exists to ensure that IO actions happen in + - the proper order, thus the invariant that stateOut should be demanded before stateVal. + - Note that unsafeInterleaveIO intentionally violates this, causing IO actions to be evaluated when + - their value is demanded. + -} +closed nonterminal IO with stateIn, stateOut, stateVal; + +abstract production bindIO +top::IO ::= st::IO fn::(IO ::= a) +{ + st.stateIn = top.stateIn; + local newState::IO = fn(st.stateVal); + newState.stateIn = st.stateOut; + -- Using unsafeTrace here to ensure that when top.stateOut is demanded, + -- we demand st.stateOut before st.stateVal + top.stateOut = unsafeTrace(newState.stateOut, st.stateOut); + top.stateVal = newState.stateVal; +} + +abstract production returnIO +top::IO ::= x::a +{ + top.stateOut = top.stateIn; + top.stateVal = x; +} + +@{- + - Create a self-referential IO action. + - Note that fn should be lazy in its argument, to avoid infinite recursion. + - This can be achieved using unsafeInterleaveIO. + - + - @param fn A function creating an IO action from its own result. + -} +abstract production fixIO +top::IO ::= fn::(IO ::= a) +{ + -- Using unsafeTrace here to ensure that when fn demands its argument, + -- st.stateOut has been demanded. + local st::IO = fn(unsafeTrace(st.stateVal, st.stateOut)); + st.stateIn = top.stateIn; + top.stateOut = st.stateOut; + top.stateVal = st.stateVal; +} + +instance Functor IO { + map = liftM1; +} + +instance Apply IO { + ap = apM; +} + +instance Applicative IO { + pure = returnIO; +} + +instance Bind IO { + bind = bindIO; +} + +instance Monad IO {} + +instance MonadFix IO { + mfix = fixIO; +} + +function runIO +IOToken ::= st::IO ioIn::IOToken +{ + return evalIO(st, ioIn).io; +} + +function evalIO +IOVal ::= st::IO ioIn::IOToken +{ + st.stateIn = ioIn; + return ioval(st.stateOut, st.stateVal); +} + +function unsafeEvalIO +a ::= st::IO +{ + local res::IOVal = evalIO(st, unsafeIO()); + return unsafeTrace(res.iovalue, res.io); +} + +@{- + - Defer an IO action to be lazily evaluated when its result value is demanded. + - + - @param i An IO action to defer. + -} +abstract production unsafeInterleaveIO +top::IO ::= i::IO +{ + i.stateIn = unsafeIO(); + top.stateOut = top.stateIn; + top.stateVal = i.stateVal; +} + +-- Monadic IO wrappers +abstract production stateIO +this::IO ::= f::(IOVal ::= IOToken) +{ + local out::IOVal = f(this.stateIn); + this.stateOut = out.io; + this.stateVal = out.iovalue; +} + +abstract production stateIOUnit +this::IO ::= f::(IOToken ::= IOToken) +{ + local out::IOToken = f(this.stateIn); + this.stateOut = out; + -- Using unsafeTrace here to demand stateOut is evaluated before evaluating stateVal + this.stateVal = unsafeTrace((), this.stateOut); +} + +abstract production print +top::IO ::= s::String +{ + top.stateOut = printT(s, top.stateIn); + top.stateVal = unit(); +} + +function println +IO ::= str::String +{ return stateIOUnit(printlnT(str, _)); } + +function eprint +IO ::= str::String +{ return stateIOUnit(eprintT(str, _)); } + +function eprintln +IO ::= str::String +{ return stateIOUnit(eprintlnT(str, _)); } + +abstract production readLineStdin +top::IO> ::= +{ + local res::IOVal> = readLineStdinT(top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +-- Having a polymorphic return type lets us write code like: +-- +-- if !null(errs) { +-- print(showErrs(errs)); +-- exit(1); +-- } else { +-- return value; +-- } +abstract production exit +top::IO ::= val::Integer +{ + top.stateOut = exitT(val, top.stateIn); + top.stateVal = error("stateOut should've been evaluated first?"); +} + +abstract production mkdir +top::IO ::= s::String +{ + local res::IOVal = mkdirT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production system +top::IO ::= s::String +{ + local res::IOVal = systemT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production writeFile +top::IO ::= file::String contents::String +{ + top.stateOut = writeFileT(file, contents, top.stateIn); + top.stateVal = unit(); +} + +abstract production writeBinaryFile +top::IO ::= file::String contents::ByteArray +{ + top.stateOut = writeBinaryFileT(file, contents, top.stateIn); + top.stateVal = unit(); +} + +abstract production appendFile +top::IO ::= file::String contents::String +{ + top.stateOut = appendFileT(file, contents, top.stateIn); + top.stateVal = unit(); +} + +abstract production fileTime +top::IO ::= s::String +{ + local res::IOVal = fileTimeT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production isFile +top::IO ::= s::String +{ + local res::IOVal = isFileT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production isDirectory +top::IO ::= s::String +{ + local res::IOVal = isDirectoryT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production readFile +top::IO ::= s::String +{ + local res::IOVal = readFileT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production readBinaryFile +top::IO ::= s::String +{ + local res::IOVal = readBinaryFileT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production cwd +top::IO ::= +{ + local res::IOVal = cwdT(top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production envVar +top::IO ::= s::String +{ + local res::IOVal = envVarT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production listContents +top::IO<[String]> ::= s::String +{ + local res::IOVal<[String]> = listContentsT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production deleteFile +top::IO ::= s::String +{ + local res::IOVal = deleteFileT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production deleteFiles +top::IO ::= files::[String] +{ + local res::IOVal = deleteFilesT(files, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production deleteDirFiles +top::IO ::= s::String +{ + local res::IOVal = deleteDirFilesT(s, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +abstract production deleteTree +top::IO ::= s::String +{ + top.stateOut = deleteTreeT(s, top.stateIn); + top.stateVal = unit(); +} + +abstract production copyFile +top::IO ::= src::String dst::String +{ + top.stateOut = copyFileT(src, dst, top.stateIn); + top.stateVal = unit(); +} + +abstract production touchFile +top::IO ::= file::String +{ + top.stateOut = touchFileT(file, top.stateIn); + top.stateVal = unit(); +} + +abstract production touchFiles +top::IO ::= files::[String] +{ + top.stateOut = touchFilesT(files, top.stateIn); + top.stateVal = unit(); +} diff --git a/grammars/silver/core/IOMisc.sv b/grammars/silver/core/IOMisc.sv new file mode 100644 index 000000000..0070c58b9 --- /dev/null +++ b/grammars/silver/core/IOMisc.sv @@ -0,0 +1,116 @@ +grammar silver:core; + +@@{- + +The miscellaneous IO functions. + +-} + +@@{- # IO Misc -} + +@{-- + - Die with the stated error message and a stack trace. Note that Silver stacks + - may be hard to read (it's a lazy language.) + - + - @warning Does not return! Does not do any cleanup! + - + - @param msg The path to list the contents of. + - @return Does not return. + -} +function error +a ::= msg::String +{ + return error("Not Yet Implemented: error"); -- lol +} foreign { + "java" : return "common.Util.error(%msg%.toString())"; +} + +@{-- + - Generate an integer unique to this run of this process. Starts from 0 and just + - counts up each call. + - + - @return An integer unique to this process. + -} +function genInt +Integer ::= +{ + return error("Not Yet Implemented: genInt"); +} foreign { + "java" : return "common.Util.genInt()"; +} + +@{-- + - Generates a random number between [0, 1) + -} +function genRand +Float ::= +{ + return error("Not Yet Implemented: genRand"); +} foreign { + "java" : return "((float)Math.random())"; +} + +@{-- + - Print string when a value is demanded by the Silver runtime. + - When this gets executed may be unpredictable. + - + - @param val The value to evaluate to, after the IO action is performed. + - @param str The string to print. + - @return val, unchanged. + - @warning see @link[unsafeIO] + -} +function unsafeTracePrint +a ::= val::a str::String +{ + return unsafeTrace(val, printT(str, unsafeIO())); +} + +@{-- + - Print a stringification of a value when it is demanded by the Silver runtime. + - When this gets executed may be unpredictable. + - + - @param val The value to evaluate to, printed when evaluated. + - @return val, unchanged. + - @warning see @link[unsafeIO] + -} +function unsafeTraceDump +a ::= val::a +{ + return unsafeTrace(val, printlnT(hackUnparse(val), unsafeIO())); +} + + + +-- Function for manipulating strings representing file and directory names. + +function dirNameInFilePath +String ::= filePath::String +{ + return if indexOfLastSlash == -1 then filePath + else substring(0, indexOfLastSlash, filePath); + + local attribute indexOfLastSlash :: Integer; + indexOfLastSlash = lastIndexOf("/", filePath); +} + +function fileNameInFilePath +String ::= filePath::String +{ + return if indexOfLastSlash == -1 then filePath + else substring(indexOfLastSlash+1, length(filePath), filePath); + + local attribute indexOfLastSlash :: Integer; + indexOfLastSlash = lastIndexOf("/", filePath); +} + + +function splitFileNameAndExtension +Pair ::= filePath::String +{ + return if indexOfLastDot == -1 then pair(filePath, "") + else pair(substring(0, indexOfLastDot, filePath) , + substring(indexOfLastDot+1, length(filePath), filePath)); + + local attribute indexOfLastDot :: Integer; + indexOfLastDot = lastIndexOf(".", filePath); +} diff --git a/grammars/silver/core/IOToken.sv b/grammars/silver/core/IOToken.sv new file mode 100644 index 000000000..2c0d87227 --- /dev/null +++ b/grammars/silver/core/IOToken.sv @@ -0,0 +1,467 @@ +grammar silver:core; + +@@{- + +The silver IO system is a IO-token based system. + +# IO Types -} + +@{-- + - The type representing the world-state token of an IO action. + -} +synthesized attribute io :: IOToken; + +@{-- + - The resulting value of an IO action. + -} +synthesized attribute iovalue :: a; + +@{-- + - A container for the results of IO actions. + - + - @param a The type of value returned by the IO action. + -} +nonterminal IOVal with io, iovalue; + +type ByteArray foreign = "byte[]"; + +@{-- + - The sole constructor of IOVal results. + - + - @param i The resulting world-state token. + - @param v The resulting value. + -} +abstract production ioval +top::IOVal ::= i::IOToken v::a +{ + top.io = i; + top.iovalue = v; +} + +@{-- + - IOToken is the IO Token used to sequence actions. + -} +type IOToken foreign = "common.IOToken"; + +@@{- # IO Actions -} + +@{-- + - Displays a string on standard out. Newlines are NOT automatically added. + - + - @param s The string to print. + - @param i The "before" world-state token. + - @return The "after" world-state token. + -} +function printT +IOToken ::= s::String i::IOToken +{ + return error("Not Yet Implemented: printT"); +} foreign { + "java" : return "%i%.print(%s%)"; +} + +@{-- + - Like printT, but adds a trailing newline automatically. + -} +function printlnT +IOToken ::= str::String ioIn::IOToken +{ + return printT(str ++ "\n", ioIn); +} + +@{-- + - Like printT, but for stderr. + -} +function eprintT +IOToken ::= str::String ioIn::IOToken +{ + return error("Unimplemented foreign function: eprintT"); +} foreign { + "java" : return "%ioIn%.eprint(%str%)"; +} + +@{-- + - Like eprintT, but adds a trailing newline automatically. + -} +function eprintlnT +IOToken ::= str::String ioIn::IOToken +{ + return eprintT(str ++ "\n", ioIn); +} + +@{-- + - Read a line from standard input. + - + - @param i The "before" world-state token. + - @return The "after" world-state token. + -} +function readLineStdinT +IOVal> ::= i::IOToken +{ + return error ("Not Yet Implemented: readLineStdinT"); +} foreign { + "java" : return "%i%.readLineStdin()"; +} + +@{-- + - Terminates with the specified error code. + - + - @warning Never returns! Does not do any cleanup! + - + - @param val The error code to terminate with. (0 is considered "success") + - @param i The "before" world-state token. + - @return Does not actually return! + -} +function exitT +IOToken ::= val::Integer i::IOToken +{ + return error("Not Yet Implemented: exitT"); +} foreign { + "java" : return "%i%.exit(%val%)"; +} + +@{-- + - Creates a directory, including any parents that need to be created along the way. + - Similar to 'mkdir -p'. If it fails, it may create only some of them. + - + - @param s The path to create. + - @param i The "before" world-state token. + - @return true if completely successful. false if an error occurred along the way. + -} +function mkdirT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: mkdirT"); +} foreign { + "java" : return "%i%.mkdir(%s%)"; +} + +@{-- + - Executes a shell command. Specifically executes 'bash -c'. So, not fully cross-platform. + - + - @warning Avoid using this if possible. If you need an IO action not present, request it, please. + - + - Access to command's output is not directly available, but it is run in a shell. You can + - redirect to a file and read that. + - + - @param s The string for the shell to execute. + - @param i The "before" world-state token. + - @return The exit value of the subprocess. + -} +function systemT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: systemT"); +} foreign { + "java" : return "%i%.system(%s%)"; +} + +@{-- + - Write a string to a file, replacing whatever is there already. + - + - @param file The filename to write to. + - @param contents The string to write to the file. + - @param i The "before" world-state token. + - @return The "after" world-state token. May throw a java IO exception, which cannot be caught by Silver. + -} +function writeFileT +IOToken ::= file::String contents::String i::IOToken +{ + return error("Not Yet Implemented: writeFileT"); +} foreign { + "java" : return "%i%.writeFile(%file%, %contents%)"; +} + +@{-- + - Append a string to a file. + - + - @param file The filename to append to. + - @param contents The string to append to the file. + - @param i The "before" world-state token. + - @return The "after" world-state token. May throw a java IO exception, which cannot be caught by Silver. + -} +function appendFileT +IOToken ::= file::String contents::String i::IOToken +{ + return error("Not Yet Implemented: appendFileT"); +} foreign { + "java" : return "%i%.appendFile(%file%, %contents%)"; +} + +@{-- + - Write a bytearray to a file, replacing whatever is there already. + - + - @param file The filename to write to. + - @param contents The bytearray to write to the file. + - @param i The "before" world-state token. + - @return The "after" world-state token. May throw a java IO exception, which cannot be caught by Silver. + -} +function writeBinaryFileT +IOToken ::= file::String contents::ByteArray i::IOToken +{ + return error("Not Yet Implemented: writeBinaryFileT"); +} foreign { + "java" : return "%i%.writeByteFile(%file%, %contents%)"; +} + +@{-- + - Read the entire contents of a file. All instances of "\r\n" are replaced by "\n" + - for compatibility reasons. + - + - @param s The file to read. + - @param i The "before" world-state token. + - @return The contents of the file. May throw a java IO exception, which cannot be caught by Silver. + -} +function readBinaryFileT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: readBinaryFileT"); +} foreign { + "java" : return "%i%.readByteFile(%s%)"; +} + + +@{-- + - The time, in seconds since 1970, when this file (or directory) was last modified. + - + - @param s The file to query. + - @param i The "before" world-state token. + - @return The modification time of this file. Or 0 if file was not found. + -} +function fileTimeT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: fileTimeT"); +} foreign { + "java" : return "%i%.fileTime(%s%)"; +} + +@{-- + - Checks if a file is an ordinary file. (non-directory, non-special) + - + - @param s The file to query. + - @param i The "before" world-state token. + - @return true if if the file is ordinary. false otherwise. + -} +function isFileT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: isFileT"); +} foreign { + "java" : return "%i%.isFile(%s%)"; +} + +@{-- + - Checks if a path is a directory. + - + - @param s The path to query. + - @param i The "before" world-state token. + - @return true if if the exists and is a directory. false otherwise. + -} +function isDirectoryT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: isDirectoryT"); +} foreign { + "java" : return "%i%.isDirectory(%s%)"; +} + +@{-- + - Read the entire contents of a file. All instances of "\r\n" are replaced by "\n" + - for compatibility reasons. + - + - @param s The file to read. + - @param i The "before" world-state token. + - @return The contents of the file. May throw a java IO exception, which cannot be caught by Silver. + -} +function readFileT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: readFileT"); +} foreign { + "java" : return "%i%.readFile(%s%)"; +} + +@{-- + - Return the current working directory. + - + - @param i The "before" world-state token. + - @return The current working directory of the process. + -} +function cwdT +IOVal ::= i::IOToken +{ + return error("Not Yet Implemented: cwdT"); +} foreign { + "java" : return "%i%.cwd()"; +} + +@{-- + - Obtain the value of an environment variable. + - + - @param s The name of the environment variable to read. + - @param i The "before" world-state token. + - @return The variables string. Empty string if the key doesn't exist. + -} +function envVarT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: envVarT"); +} foreign { + "java" : return "%i%.envVar(%s%)"; +} + +@{-- + - List the contents of a directory. Returns empty list if not a directory or + - other IO error. + - + - @param s The path to list the contents of. + - @param i The "before" world-state token. + - @return All files and directories in the named directory. Or [] on error. + -} +function listContentsT +IOVal<[String]> ::= s::String i::IOToken +{ + return error("Not Yet Implemented: listContentsT"); +} foreign { + "java" : return "%i%.listContents(%s%)"; +} + +@{-- + - Delete a file, or an empty directory. + - + - @param s The path to file to delete. + - @param i The "before" world-state token. + - @return true if the file is deleted successfully. false otherwise. + -} +function deleteFileT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: deleteFileT"); +} foreign { + "java" : return "%i%.deleteFile(%s%)"; +} + +@{-- + - Delete a set of files. + - + - @param s The list of paths to files to delete. + - @param i The "before" world-state token. + - @return true if all files are deleted successfully. false otherwise. + -} +function deleteFilesT +IOVal ::= s::[String] i::IOToken +{ + return error("Not Yet Implemented: deleteFilesT"); +} foreign { + "java" : return "%i%.deleteFiles(%s%)"; +} + +@{-- + - Empty a directory of all normal files (i.e. leaving subdirectories alone) + - + - @param s The path to the directory to empty + - @param i The "before" world-state token. + - @return true if contents are deleted successfully. false otherwise. + -} +function deleteDirFilesT +IOVal ::= s::String i::IOToken +{ + return error("Not Yet Implemented: deleteDirFilesT"); +} foreign { + "java" : return "%i%.deleteDirFiles(%s%)"; +} + +@{-- + - Delete a non-empty directory and all subdirectories and files. + - + - @param s The path to the directory to delete + - @param i The "before" world-state token. + - @return The IOToken token. Errors (other than non-existence of the path) are fatal. + -} +function deleteTreeT +IOToken ::= s::String i::IOToken +{ + return error("Not Yet Implemented: deleteTreeT"); +} foreign { + "java" : return "%i%.deleteTree(%s%)"; +} + +@{-- + - Copy a file from src to dst. + - + - @param src The path of the file to copy. + - @param dst The path of the file to write, or the directory to copy the file to. + - @param i The "before" world-state token. + - @return the IOToken token. Errors are fatal. + -} +function copyFileT +IOToken ::= src::String dst::String i::IOToken +{ + return error("Not Yet Implemented: copyFileT"); +} foreign { + "java" : return "%i%.copyFile(%src%, %dst%)"; +} + +@{-- + - Update a file's modification time to the current time. + - + - @param file The file to update the modification time of. + - @param i The IOToken token. + - @return The IOToken token. Errors are suppressed. + -} +function touchFileT +IOToken ::= file::String i::IOToken +{ + return error("Not Yet Implemented: touchFileT"); +} foreign { + "java" : return "%i%.touchFile(%file%)"; +} + +@{-- + - Update a set of files' modification time to the current time. + - + - @param files The list of files to update the modification time of. + - @param i The IOToken token. + - @return The IOToken token. Errors are suppressed. + -} +function touchFilesT +IOToken ::= files::[String] i::IOToken +{ + return error("Not Yet Implemented: touchFilesT"); +} foreign { + "java" : return "%i%.touchFiles(%files%)"; +} + + +@@{- # IO Misc -} + +@{-- + - Create a bogus world-state token, for use with @link[unsafeTrace]. + - + - @return A fake world-state token. + -} +function unsafeIO +IOToken ::= +{ + return error("Not Yet Implemented: unsafeIO"); +} foreign { + "java" : return "common.IOToken.singleton"; +} + +@{-- + - Execute an IO action when a value is demanded by the Silver runtime. + - When this gets executed may be unpredictable. + - + - @param val The value to evaluate to, after the IO action is performed. + - @param act The world-state token to demand and consume. + - @return val, unchanged. + - @warning see @link[unsafeIO] + -} +function unsafeTrace +a ::= val::a act::IOToken +{ + return error("Not Yet Implemented: unsafeTrace"); +} foreign { + "java" : return "common.Util.io(%act%, %val%)"; +} diff --git a/grammars/silver/core/List.sv b/grammars/silver/core/List.sv new file mode 100644 index 000000000..746e448a6 --- /dev/null +++ b/grammars/silver/core/List.sv @@ -0,0 +1,650 @@ +grammar silver:core; + +instance Functor [] { + map = \ f::(b ::= a) l::[a] -> + if null(l) then [] + else f(head(l)) :: map(f, tail(l)); +} + +instance Apply [] { + ap = apM; +} + + +instance Applicative [] { + pure = \ x::a -> [x]; +} + +instance Bind [] { + bind = \ x::[a] y::([b] ::= a) -> flatMap(y, x); +} + +instance Monad [] {} + +instance MonadFail [] { + fail = \ String -> []; +} + +instance Alt [] { + alt = appendList; +} + +instance Plus [] { + empty = []; +} + +instance Alternative [] {} + +instance MonadZero [] {} +instance MonadPlus [] {} + +function mfixList +[a] ::= f::([a] ::= a) +{ + local x::[a] = f(head(x)); + return + case x of + | [] -> [] + | h :: _ -> h :: mfixList(compose(tail, f)) + end; +} + +instance MonadFix [] { + mfix = mfixList; +} + +@{- + Types with a notion of length. +-} +-- TODO: In Haskell, length is defined by the Foldable type class. +-- Consider moving this if we add Foldable in the future. +class Length a { + length :: (Integer ::= a); +} + +instance Length [a] { + length = listLength; +} + +@{-- + - Applies a function to each element of a list, and returns a list containing + - all the results that are just. The same as Haskell's 'mapMaybe' and Rust's + - filter_map. + -} +function filterMap +[b] ::= f::(Maybe ::= a) lst::[a] +{ + return flatMap( + \x::a -> case f(x) of + | just(y) -> [y] + | nothing() -> [] + end, + lst); +} + +@{-- + - Applies an operator right-associatively over a list. + - (i.e. replaces cons with 'f', nil with 'i' in the list) + - + - @param f The operator to apply + - @param i The "end element" to use in place of 'nil' + - @param l The list to fold + - @return The result of the function applied right-associatively to the list. + -} +function foldr +b ::= f::(b ::= a b) i::b l::[a] +{ + return if null(l) then i + else f(head(l), foldr(f, i, tail(l))); +} + +@{-- + - Applies an operator left-associatively over a list. + - + - @param f The operator to apply + - @param i The value to "start with" + - @param l The list to fold + - @return The result of the function applied left-associatively to the list. + -} +function foldl +b ::= f::(b ::= b a) i::b l::[a] +{ + return if null(l) then i + else foldl(f, f(i, head(l)), tail(l)); +} + +@{-- + - Right-fold, assuming there is always one element, and leaving that element + - unchanged for single element lists. See @link[foldr]. + -} +function foldr1 +a ::= f::(a ::= a a) l::[a] +{ + return if null(l) then error("Applying foldr1 to empty list.") + else if null(tail(l)) then head(l) + else f(head(l), foldr1(f, tail(l))); +} + +@{- + - @param f The fold function for combining an element and your accumulator + - @param i The last element function to apply to the last single element in your list + - @param l The list being folded over. + - @return An element that is the result of your combining functions applied to the list elements. + - Right-Fold, assuming there is always at least one element, and also takes in a function a->b to apply to the last element of a list, and applies that function to the last element. +-} +function foldrLastElem +b ::= f::(b ::= a b) i::(b ::= a) l::[a] +{ + return case l of + | [elem] -> i(elem) + | h::t -> f(h, foldrLastElem(f,i,t)) + | [] -> error("You can't call foldrLastElem with an empty list") + end; +} + + +@{-- + - Left-fold, assuming there is always one element, and leaving that element + - unchanged for single element lists. See @link[foldl]. + -} +function foldl1 +a ::= f::(a ::= a a) l::[a] +{ + return if null(l) then error("Applying foldl1 to empty list.") + else foldl(f, head(l), tail(l)); +} + +@{-- + - Filter out elements of a list. + - + - @param f The filter function + - @param lst The input list to filter + - @return Only those elements of 'lst' that 'f' returns true for, in the + - same order as they appeared in 'lst' + -} +function filter +[a] ::= f::(Boolean ::= a) lst::[a] +{ + return if null(lst) + then [] + else if f(head(lst)) + then head(lst) :: filter(f, tail(lst)) + else filter(f, tail(lst)); +} + +@{-- + - Partition a list in two + - + - @param f Decision function + - @param lst The list to partition + - @return A pair of all elements returning true, and all elements returning false. + -} +function partition +Pair<[a] [a]> ::= f::(Boolean ::= a) lst::[a] +{ + local attribute recurse :: Pair<[a] [a]>; + recurse = partition(f, tail(lst)); + + return if null(lst) then pair([],[]) + else if f(head(lst)) + then pair(head(lst) :: recurse.fst, recurse.snd) + else pair(recurse.fst, head(lst) :: recurse.snd); +} + +@{-- + - Determine if an element appears in a list. + - + - @param eq The equality function to use + - @param elem The element to search for + - @param lst The list to search + - @return True if the equality function returns true for some element of the list, + - false otherwise. + -} +function containsBy +Boolean ::= eq::(Boolean ::= a a) elem::a lst::[a] +{ + return (!null(lst)) && (eq(elem, head(lst)) || containsBy(eq, elem, tail(lst))); +} + +@{-- + - Determine if an element appears in a list. + - + - @param elem The element to search for + - @param lst The list to search + - @return True if == is true for some element of the list, false otherwise. + -} +function contains +Eq a => Boolean ::= elem::a lst::[a] +{ + return containsBy(eq, elem, lst); +} + +@{-- + - Removes all duplicates from a list. + - + - @param eq The equality function to use + - @param xs The list to remove duplicates from + - @return A list containing no duplicates, according to the equality function. + -} +function nubBy +[a] ::= eq::(Boolean ::= a a) xs::[a] +{ + return if null(xs) then [] + else head(xs) :: nubBy(eq, removeBy(eq, head(xs), tail(xs))); +} + +@{-- + - Removes all duplicates from a list. + - + - @param xs The list to remove duplicates from + - @return A list containing no duplicates, according to ==. + -} +function nub +Eq a => [a] ::= xs::[a] +{ + return nubBy(eq, xs); +} + +@{-- + - Removes all instances of an element from a list. + - + - @param eq The equality function to use + - @param x The element to remove + - @param xs The list to remove the element from + - @return A list with no remaining instances of 'x' according to 'eq' + -} +function removeBy +[a] ::= eq::(Boolean ::= a a) x::a xs::[a] +{ + return if null(xs) then [] + else (if eq(x,head(xs)) then [] else [head(xs)]) ++ removeBy(eq, x, tail(xs)); +} + +@{-- + - Removes all instances of an element from a list. + - + - @param x The element to remove + - @param xs The list to remove the element from + - @return A list with no remaining instances of 'x' according to == + -} +function remove +Eq a => [a] ::= x::a xs::[a] +{ + return removeBy(eq, x, xs); +} + +@{-- + - Removes all instances of several elements from a list: xs - ys + - + - @param eq The equality function to use + - @param ys The list of elements to remove + - @param xs The list to remove elements from + - @return A list with no remaining instances in 'ys' according to 'eq' + -} +function removeAllBy +[a] ::= eq::(Boolean ::= a a) ys::[a] xs::[a] +{ + return if null(ys) then xs + else removeAllBy(eq, tail(ys), removeBy(eq, head(ys), xs)); +} + +@{-- + - Removes all instances of several elements from a list: xs - ys + - + - @param ys The list of elements to remove + - @param xs The list to remove elements from + - @return A list with no remaining instances in 'ys' according to 'eq' + -} +function removeAll +Eq a => [a] ::= ys::[a] xs::[a] +{ + return removeAllBy(eq, ys, xs); +} + +@{-- + - Returns the initial elements of a list. + - + - @param lst The list to examine + - @return The initial elements of 'lst'. If 'lst' is empty, crash. + -} +function init +[a] ::= lst::[a] +{ + return if null(tail(lst)) + then [] + else head(lst)::init(tail(lst)); +} + +@{-- + - Returns the last element of a list. + - + - @param lst The list to examine + - @return The last element of 'lst'. If 'lst' is empty, crash. + -} +function last +a ::= lst::[a] +{ + return if null(tail(lst)) then head(lst) + else last(tail(lst)); +} + +function drop +[a] ::= number::Integer lst::[a] +{ + return if null(lst) || number <= 0 then lst + else drop(number-1, tail(lst)); +} +function take +[a] ::= number::Integer lst::[a] +{ + return if null(lst) || number <= 0 then [] + else head(lst) :: take(number-1, tail(lst)); +} +function dropWhile +[a] ::= f::(Boolean::=a) lst::[a] +{ + return if null(lst) || !f(head(lst)) then lst + else dropWhile(f, tail(lst)); +} +function takeWhile +[a] ::= f::(Boolean::=a) lst::[a] +{ + return if null(lst) || !f(head(lst)) then [] + else head(lst) :: takeWhile(f, tail(lst)); +} +function takeUntil +[a] ::= f::(Boolean::=a) lst::[a] +{ + return if null(lst) || f(head(lst)) + then [] + else head(lst) :: takeUntil(f, tail(lst)); +} + +function positionOfBy +Integer ::= eq::(Boolean ::= a a) x::a xs::[a] +{ + return positionOfHelper(eq,x,xs,0); +} + +function positionOfHelper +Integer ::= eq::(Boolean ::= a a) x::a xs::[a] currentPos::Integer +{ + return if null(xs) then -1 + else if eq(x, head(xs)) then currentPos + else positionOfHelper(eq, x, tail(xs), currentPos+1); +} + +function positionOf +Eq a => Integer ::= x::a xs::[a] +{ + return positionOfBy(eq, x, xs); +} + +function repeat +[a] ::= v::a times::Integer +{ + return if times <= 0 then [] + else v :: repeat(v, times-1); +} + +function range +[Integer] ::= lower::Integer upper::Integer +{ + return if lower >= upper then [] else lower :: range(lower + 1, upper); +} + +function zipWith +[c] ::= f::(c ::= a b) l1::[a] l2::[b] +{ + return if null(l1) || null(l2) then [] + else f(head(l1), head(l2)) :: zipWith(f, tail(l1), tail(l2)); +} + +function reverse +[a] ::= lst::[a] +{ + return reverseHelp(lst, []); +} +function reverseHelp -- do not use +[a] ::= lst::[a] sofar::[a] +{ + return if null(lst) then sofar + else reverseHelp(tail(lst), head(lst) :: sofar); +} + +function sortBy +[a] ::= lte::(Boolean ::= a a) lst::[a] +{ + return sortByHelp(lte, lst, length(lst)); +} + +function sortByKey +Ord b => [a] ::= key::(b ::= a) lst::[a] +{ + return sortBy(\l::a r::a -> key(l) <= key(r), + lst); +} + +function sort +Ord a => [a] ::= lst::[a] +{ + return sortByHelp(lte, lst, length(lst)); +} + +function sortByHelp -- do not use +[a] ::= lte::(Boolean ::= a a) lst::[a] upTo::Integer +{ + return if upTo == 0 then [] + else if upTo == 1 then [head(lst)] + else mergeBy(lte, front_half, back_half); + + local attribute front_half :: [a]; + front_half = sortByHelp(lte, lst, middle); + + local attribute back_half :: [a]; + back_half = sortByHelp(lte, drop(middle, lst), upTo - middle); + + local attribute middle :: Integer; + middle = toInteger(toFloat(upTo) / 2.0); +} +function mergeBy -- do not use +[a] ::= lte::(Boolean ::= a a) l1::[a] l2::[a] +{ + return if null(l1) then l2 + else if null(l2) then l1 + else if lte(head(l1), head(l2)) + then head(l1) :: mergeBy(lte, tail(l1), l2) + else head(l2) :: mergeBy(lte, l1, tail(l2)); +} + +function groupBy +[[a]] ::= eq::(Boolean ::= a a) l::[a] +{ + local attribute helpercall :: Pair<[a] [a]>; + helpercall = groupByHelp(eq, head(l), l); + + return if null(l) then [] + else helpercall.fst :: if null(helpercall.snd) then [] + else groupBy(eq, helpercall.snd); +} +function groupByHelp -- do not use +Pair<[a] [a]> ::= eq::(Boolean ::= a a) f::a l::[a] +{ + -- f is the representative element we're comparing with, but is not considered + -- included when we're called. + local attribute recurse :: Pair<[a] [a]>; + recurse = groupByHelp(eq, f, tail(l)); + + return if null(l) || !eq(f, head(l)) + then pair([], l) + else pair(head(l) :: recurse.fst, recurse.snd); +} + +function group +Eq a => [[a]] ::= l::[a] +{ + return groupBy(eq, l); +} + +@{-- + - Inserts the separator in between all elements of the list. + -} +function intersperse +[a] ::= sep::a xs::[a] +{ return if null(xs) then [] + else if null(tail(xs)) then xs + else head(xs) :: sep :: intersperse(sep, tail(xs)); +} + + +-- Set operations +function unionBy +[a] ::= eq::(Boolean ::= a a) l::[a] r::[a] +{ + return if null(l) then r + else + (if containsBy(eq, head(l), r) + then [] + else [head(l)]) + ++ unionBy(eq, tail(l), r); +} + +function union +Eq a => [a] ::= l::[a] r::[a] +{ + return unionBy(eq, l, r); +} + +function intersectBy +[a] ::= eq::(Boolean ::= a a) l::[a] r::[a] +{ + return if null(l) then [] + else + (if containsBy(eq, head(l), r) + then [head(l)] + else []) + ++ intersectBy(eq, tail(l), r); +} + +function intersect +Eq a => [a] ::= l::[a] r::[a] +{ + return intersectBy(eq, l, r); +} + +function unionsBy +[a] ::= eq::(Boolean ::= a a) ss::[[a]] +{ + return nubBy(eq, concat(ss)); +} + +function unions +Eq a => [a] ::= ss::[[a]] +{ + return nub(concat(ss)); +} + +function powerSet +[[a]] ::= xs::[a] +{ + return + case xs of + | h :: t -> + let rest::[[a]] = powerSet(t) + in rest ++ map(cons(h, _), rest) + end + | [] -> [[]] + end; +} + + +-- Boolean list operations +function all +Boolean ::= l::[Boolean] +{ + return foldr(\ a::Boolean b::Boolean -> a && b, true, l); +} + +function any +Boolean ::= l::[Boolean] +{ + return foldr(\ a::Boolean b::Boolean -> a || b, false, l); +} + +-------------------------------------------------------------------------------- + +function nil +[a] ::= +{ + -- Foreign function expected to handle this here + -- Needs a new implementation if non-java translation is made. + return error("foreign function"); +} foreign { + "java" : return "common.ConsCell.nil"; +} + +function cons +[a] ::= h::a t::[a] +{ + -- Foreign function expected to handle this here + -- Needs a new implementation if non-java translation is made. + return error("foreign function"); +} foreign { + "java" : return "new common.ConsCell(%?h?%, %?t?%)"; +} + +function appendList +[a] ::= l1::[a] l2::[a] +{ + return case l1 of + | h :: t -> cons(h, append(t, l2)) + | [] -> l2 + end; +} foreign { + "java" : return "common.AppendCell.append(%l1%, %?l2?%)"; +} + + +function null +Boolean ::= l::[a] +{ + return case l of + | [] -> true + | _ :: _ -> false + end; +} foreign { + "java" : return "%l%.nil()"; +} + +function listLength -- not called 'length' since this is a builtin language feature, but thats how you should call it. +Integer ::= l::[a] +{ + return case l of + | _ :: t -> 1 + listLength(t) + | [] -> 0 + end; +} foreign { + "java" : return "Integer.valueOf(%l%.length())"; +} + +function head +a ::= l::[a] +{ + return case l of + | h :: _ -> h + | [] -> error("requested head of nil") + end; +} foreign { + "java" : return "%l%.head()"; +} + +function tail +[a] ::= l::[a] +{ + return case l of + | _ :: t -> t + | [] -> error("requested tail of nil") + end; +} foreign { + "java" : return "%l%.tail()"; +} diff --git a/grammars/silver/core/Location.sv b/grammars/silver/core/Location.sv new file mode 100644 index 000000000..4de4d6483 --- /dev/null +++ b/grammars/silver/core/Location.sv @@ -0,0 +1,106 @@ +grammar silver:core; + +annotation location :: Location; + +@{-- + - Data structure storing location information on tree nodes from a parse. + -} +nonterminal Location with filename, line, column, endLine, endColumn, index, endIndex, compareTo, isEqual; +propagate compareTo, isEqual on Location; + +synthesized attribute filename :: String; +synthesized attribute line :: Integer; +synthesized attribute column :: Integer; +synthesized attribute endLine :: Integer; +synthesized attribute endColumn :: Integer; +synthesized attribute index :: Integer; +synthesized attribute endIndex :: Integer; + +@{-- + - The main constructor for location information. + - + - filename, line and column can be mutated by action blocks during parsing, + - but character index cannot. + - + - @param filename The "virtual filename". Initially whatever the parser is given. + - @param line (Beginning) line number, inclusive. Lines are numbered starting with 1. + - @param column (Beginning) column number, inclusive. Columns are numbered starting with 0. (For now.) + - @param endLine (Ending) line number, inclusive. + - @param endColumn (Ending) column number, exclusive. + - @param index (Beginning) character index, inclusive. + - @param endIndex (Ending) character index, exclusive. + - + - e.g. "Hi" as an entire file contents would have its entire location as: + - `(_, 1, 0, 1, 2, 0, 2)` + -} +abstract production loc +top::Location ::= filename::String line::Integer column::Integer + endLine::Integer endColumn::Integer + index::Integer endIndex::Integer +{ + top.filename = filename; + top.line = line; + top.column = column; + top.endLine = endLine; + top.endColumn = endColumn; + top.index = index; + top.endIndex = endIndex; +} + +@{-- + - A secondary constructor for location information, for locations not from source code + - + - @param text The text to return as unparse as defined in langutil + -} +abstract production txtLoc +top::Location ::= text::String +{ + top.filename = "N/A"; + top.line = -1; + top.column = -1; + top.endLine = -1; + top.endColumn = -1; + top.index = -1; + top.endIndex = -1; +} + + +@{-- + - Offset one location "inside" another. For use when e.g. parsing a doc comment grammar out of a single + - terminal in the host language. use linesOffset, firstLineColsOffset, allLinesColsOffset, indexOffset if some + - part of the terminal is munged before being passed to the child parser (e.g. the {- and -} are removed from a comment.) + -} +function childParserLoc +Location ::= parent::Location child::Location linesOffset::Integer firstLineColsOffset::Integer allLinesColsOffset::Integer indexOffset::Integer +{ + return loc( + parent.filename, + parent.line - 1 + linesOffset + child.line, + allLinesColsOffset + (if child.line == 1 then parent.column + firstLineColsOffset + child.column else child.column), + parent.endLine - 1 + linesOffset + child.endLine, + allLinesColsOffset + (if child.endLine == 1 then parent.endColumn + firstLineColsOffset + child.endColumn else child.endColumn), + parent.index + indexOffset + child.index, + parent.endIndex + indexOffset + child.endIndex + ); +} + + +@{-- + - A helper constructor for location information, for built-in locations + - + - @param module The name of the extension/modifcation/module defining the location + -} +function builtinLoc +Location ::= module::String +{ + return txtLoc("Built in from " ++ module); +} + +@{-- + - A helper constructor for location information, for invalid or undefined bogus locations + -} +function bogusLoc +Location ::= +{ + return txtLoc("Invalid or undefined bogus location"); +} diff --git a/grammars/silver/core/Maybe.sv b/grammars/silver/core/Maybe.sv new file mode 100644 index 000000000..084e3d6d1 --- /dev/null +++ b/grammars/silver/core/Maybe.sv @@ -0,0 +1,248 @@ +grammar silver:core; + +synthesized attribute fromJust :: a; +synthesized attribute isJust :: Boolean; + +nonterminal Maybe with fromJust, isJust; + +abstract production just +top::Maybe ::= v::a +{ + top.fromJust = v; + top.isJust = true; +} + +abstract production nothing +top::Maybe ::= +{ + top.fromJust = error("fromJust accessed on a Maybe that was actually nothing!"); + top.isJust = false; +} + +instance Functor Maybe { + map = \ f::(b ::= a) m::Maybe -> + case m of + | just(x) -> just(f(x)) + | nothing() -> nothing() + end; +} + +instance Apply Maybe { + ap = \ mf::Maybe<(b ::= a)> m::Maybe -> + case mf of + | just(f) -> map(f, m) + | nothing() -> nothing() + end; +} + +instance Applicative Maybe { + pure = just; +} + +instance Bind Maybe { + bind = \ m::Maybe fn::(Maybe ::= a) -> + case m of + | just(x) -> fn(x) + | nothing() -> nothing() + end; +} + +instance Monad Maybe {} + +instance MonadFail Maybe { + fail = \ String -> nothing(); +} + +instance Alt Maybe { + alt = orElse; +} + +instance Plus Maybe { + empty = nothing(); +} + +instance Alternative Maybe {} + +instance MonadZero Maybe {} +instance MonadPlus Maybe {} + +function mfixMaybe +Maybe ::= f::(Maybe ::= a) +{ + local x::Maybe = f(x.fromJust); + return x; +} + +instance MonadFix Maybe { + mfix = mfixMaybe; +} + +@{- + - Monad transformer corresponding to Maybe. + - + - @param m The monad type to be transformed + - @param a The optional result type + -} +nonterminal MaybeT<(m :: * -> *) a> with run>>; +abstract production maybeT +top::MaybeT ::= x::m> +{ + top.run = x; +} + +@{-- + - Transform the computation inside a MaybeT. + -} +function mapMaybeT +MaybeT ::= f::(n> ::= m>) x::MaybeT +{ + return maybeT(f(x.run)); +} + +instance Functor m => Functor MaybeT { + map = \ f::(b ::= a) x::MaybeT -> mapMaybeT(map(map(f, _), _), x); +} + +instance Monad m => Apply MaybeT { + ap = \ mf::MaybeT mx::MaybeT -> maybeT( + do { + maybeF::Maybe<(b ::= a)> <- mf.run; + case maybeF of + | nothing() -> pure(nothing()) + | just(f) -> do { + maybeX::Maybe <- mx.run; + case maybeX of + | nothing() -> pure(nothing()) + | just(x) -> pure(just(f(x))) + end; + } + end; + }); +} + +instance Monad m => Applicative MaybeT { + pure = compose(maybeT, compose(pure, just)); +} + +instance Monad m => Bind MaybeT { + bind = \ x::MaybeT f::(MaybeT ::= a) -> maybeT( + do { + maybeVal :: Maybe <- x.run; + case maybeVal of + | nothing() -> pure(nothing()) + | just(val) -> f(val).run + end; + }); +} + +instance Monad m => Monad MaybeT {} + +instance Monad m => MonadFail MaybeT { + fail = \ String -> maybeT(pure(nothing())); +} + +instance Monad m => Alt MaybeT { + alt = \ x::MaybeT y::MaybeT -> maybeT( + do { + v :: Maybe <- x.run; + case v of + | nothing() -> y.run + | just(_) -> pure(v) + end; + }); +} + +instance Monad m => Plus MaybeT { + empty = maybeT(pure(nothing())); +} + +instance Monad m => Alternative MaybeT {} + +instance Monad m => MonadZero MaybeT {} +instance Monad m => MonadPlus MaybeT {} + +instance MonadTrans MaybeT { + lift = \ x::m -> maybeT(map(just, x)); +} + +-------------------------------------------------------------------------------- + +@{-- + - The corresponding fold for Maybes. + - + - @param otherwise The element to return if 'ifJust' is 'nothing' + - @param ifJust The maybe value to scrutinize + - @return Either the contents of the Maybe (if 'just'), or the otherwise element. + -} +function fromMaybe +a ::= otherwise::a ifJust::Maybe +{ + return if ifJust.isJust then ifJust.fromJust else otherwise; +} + +@{-- + - Selects the first existing element, favoring the left. + - + - @param l The first element + - @param r The second element + - @return A wrapped element, if any, favoring 'l' + -} +function orElse +Maybe ::= l::Maybe r::Maybe +{ + return if l.isJust then l else r; +} + +@{-- + - The eliminator for Maybe. Runs ifJust on the wrapped value if there is one, + - otherwise returns ifNothing. + -} +function mapOrElse +b ::= ifNothing::b ifJust::(b ::= a) value::Maybe +{ + return + case value of + | just(x) -> ifJust(x) + | nothing() -> ifNothing + end; +} + +@{-- + - Maybe cons a value to a list, or not. + - + - @param h If a value, the value to cons onto the list. + - @param t The list to amend, if there's a value + - @return The list, possibly with a new value at its head. + -} +function consMaybe +[a] ::= h::Maybe t::[a] +{ + return if h.isJust then h.fromJust :: t else t; +} + +@{-- + - Turn a list of possible values into a list of values, skipping over + - any 'nothing's. + - + - @param l A list of optional values + - @return The list with all absent values removed, and present values unwrapped. + -} +function catMaybes +[a] ::= l::[Maybe] +{ + return foldr(consMaybe, [], l); +} + +@{-- + - Finds the first value matching a predicate. + -} +function find +Maybe ::= f::(Boolean ::= a) l::[a] +{ + return if null(l) then + nothing() + else if f(head(l)) then + just(head(l)) + else + find(f, tail(l)); +} diff --git a/grammars/silver/core/Monad.sv b/grammars/silver/core/Monad.sv new file mode 100644 index 000000000..68b8b5bf5 --- /dev/null +++ b/grammars/silver/core/Monad.sv @@ -0,0 +1,102 @@ +grammar silver:core; + +@{- +Monads support both lifting functions/values of arbitrary arity and sequential compostion. + +Instances should satisfy the following: + +Left Identity + bind(pure(x), f) = f(x) +Right Identity + bind(x, pure) = x +-} +class Applicative m, Bind m => Monad m {} + +-- Prefer `fmap` to this function. However, it can be useful to get a `Functor` +-- instance for free, given an existing `Monad` instance. +function liftM1 +Monad m => m ::= f::(b ::= a) x::m +{ return bind(x, \x::a -> pure(f(x))); } + +-- Prefer `ap` to this function. However, it can be useful to get an +-- `Applicative` instance for free, given an existing `Monad` instance. +function apM +Monad m => m ::= f::m<(b ::= a)> x::m +{ return bind(f, \f::(b ::= a) -> bind(x, \x::a -> pure(f(x)))); } + +function whenM +Monad m => m ::= cond::m body::m +{ return bind(cond, when_(_, body)); } + +function unlessM +Monad m => m ::= cond::m body::m +{ return bind(cond, unless(_, body)); } + +@{- +Monads that support failure with an error message. + +Instances should satisfy the following: + +Annihilation + bind(fail(s), f) = fail(s) +-} +class Monad m => MonadFail m { + fail :: (m ::= String); +} + +@{- +The MonadZero type class has no members of its own; it just specifies that the type has both Monad and Alternative instances. + +Instances should satisfy the following: + +Annihilation + bind(empty, f) = empty +-} +class Monad m, Alternative m => MonadZero m {} + +@{- +The MonadPlus type class has no members of its own; it just extends MonadZero with an additional law. + +Instances should satisfy the following: + +Distributivity + bind(alt(x, y), f) = alt(bind(x, f), bind(y, f)) +-} +class MonadZero m, Alternative m => MonadPlus m {} + +@{- +Monads having fixed points with a 'knot-tying' semantics. + +Instances should satisfy the following: + +Purity + mfix(compose(pure, h)) = pure(fix(h)) + +Left shrinking (or Tightening) + mfix(\x -> bind(a, \y -> f(x, y))) = bind(a, \y -> mfix(\x -> f(x, y))) + +Sliding + mfix(compose(map(h, _), f)) = map(h, (mfix(compose(f, h)))), for strict h. + +Nesting + mfix(\x -> mfix(\y -> f(x, y))) = mfix(\x -> f(x, x)) +-} +class Monad m => MonadFix m { + mfix :: (m ::= (m ::= a)); +} + +@{- +Monad transformers lift a monadic computation into an additional monad. + +Instances should satisfy the following: + compose(lift, pure) = pure + lift(bind(m, f)) = bind(lift(m), compose(lift, f)) +-} +class MonadTrans (t :: (* -> *) -> * -> *) { + lift :: Monad m => (t ::= m); +} + +@{- +Can be used to extract the monadic value from a MonadTrans instance value. + -} +synthesized attribute run::a; diff --git a/grammars/silver/core/Monoid.sv b/grammars/silver/core/Monoid.sv new file mode 100644 index 000000000..c00b2f06b --- /dev/null +++ b/grammars/silver/core/Monoid.sv @@ -0,0 +1,87 @@ +grammar silver:core; + +@{- +The class of monoids (types with an associative binary operation that has an identity). Instances should satisfy the following: + +Right identity + append(x, mempty) = x +Left identity + append(mempty, x) = x +Associativity + append(x, append(y, z)) = append(append(x, y), z) (Semigroup law) +Concatenation + concat = foldr(append, mempty, _) + +Minimal complete definition: mempty +-} +class Semigroup a => Monoid a { + mempty :: a; + concat :: (a ::= [a]) = foldr(append, mempty, _); +} + +-- e.g. [] and Maybe +instance Plus m => Monoid m { + mempty = empty; +} + +instance Monoid a, Monoid b => Monoid (a, b) { + mempty = (mempty, mempty); +} + +instance Monoid String { + mempty = ""; +} + +instance Monoid Unit { + mempty = unit(); +} + +@{-- + - Map a function over a list, and then concatenates the results together. + - + - @param f A function to apply to each element of a list, returning a monoid. + - @param lst A list + - @return The combined list + -} +function flatMap +Monoid m => m ::= f::(m ::= a) lst::[a] +{ + return concat(map(f, lst)); +} + +@{- Computes the integer exponent of some monoid (treating the semigroup as + - multiplication, as is standard in algebra). + - + - Note that for negative `n`, this errors out. If your type is a `Group`, use + - `power` instead. + -} +function mpower +Monoid m => m ::= x::m n::Integer +{ + return case power(mPowerHelper(x), n) of mPowerHelper(y) -> y end; +} + +@{- @hide -} +nonterminal MPowerHelper; + +@{- @hide -} +abstract production mPowerHelper +top::MPowerHelper ::= m +{} + +@{- @hide -} +function mPowerHelperAppend +Semigroup m => MPowerHelper ::= xH::MPowerHelper yH::MPowerHelper +{ + return + case xH, yH of + | mPowerHelper(x), mPowerHelper(y) -> + mPowerHelper(append(x, y)) + end; +} + +instance Semigroup m => Semigroup MPowerHelper { append = mPowerHelperAppend; } +instance Monoid m => Monoid MPowerHelper { mempty = mPowerHelper(mempty); } +instance Monoid m => Group MPowerHelper { + invert = error("Cannot perform a negative mpower"); +} diff --git a/grammars/silver/core/Ord.sv b/grammars/silver/core/Ord.sv new file mode 100644 index 000000000..bdc9fa107 --- /dev/null +++ b/grammars/silver/core/Ord.sv @@ -0,0 +1,286 @@ +grammar silver:core; + +@{- +Ord represents ordering relationships between data. + +Laws are based on Haskell's Ord type class: + +Transitivity + if x <= y && y <= z = true, then x <= z = true +Reflexivity + x <= x = true +Antisymmetry + if x <= y && y <= x = true, then x == y = true + +Note that the following operator interactions are expected to hold: +1. x >= y = y <= x +2. x < y = x <= y && x != y +3. x > y = y < x +4. x < y = compare x y < 0 +5. x > y = compare x y > 0 +6. x == y = compare x y == 0 +7. min(x, y) == if x <= y then x else y = true +8. max(x, y) == if x >= y then x else y = true + +Note that (7.) and (8.) do not require min and max to return either of their arguments. The result is merely required to equal one of the arguments in terms of (==). + +Minimal complete definition: either compare or <=. Using compare can be more efficient for complex types. +-} +class Eq a => Ord a { + compare :: (Integer ::= a a) = \ x::a y::a -> + if x == y then 0 else if x <= y then -1 else 1; + + lt :: (Boolean ::= a a) = \ x::a y::a -> compare(x, y) < 0; + lte :: (Boolean ::= a a) = \ x::a y::a -> compare(x, y) <= 0; + gt :: (Boolean ::= a a) = \ x::a y::a -> compare(x, y) > 0; + gte :: (Boolean ::= a a) = \ x::a y::a -> compare(x, y) >= 0; + + max :: (a ::= a a) = \ x::a y::a -> if x <= y then y else x; + min :: (a ::= a a) = \ x::a y::a -> if x >= y then y else x; +} + +ordering attribute compareKey, compare with compareTo; + +instance attribute compareTo occurs on a, + attribute isEqual {compareTo} occurs on a, -- Needed by Eq superclass + --attribute compareKey {compareTo} occurs on a, -- Typically present, but not needed as a constraint + attribute compare {compareTo} occurs on a + => Ord a { + compare = \ x::a y::a -> decorate x with {compareTo = decorate y with {};}.compare; +} + +instance typeError "Comparison is not supported for Decorated types" + => Ord Decorated a with i { + compare = error("type error"); +} + +instance Ord Integer { + compare = \ x::Integer y::Integer -> x - y; + lt = ltInteger; + lte = lteInteger; + gt = gtInteger; + gte = gteInteger; +} +function ltInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% < (int)%y%)"; +} +function lteInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% <= (int)%y%)"; +} +function gtInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% > (int)%y%)"; +} +function gteInteger +Boolean ::= x::Integer y::Integer +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% >= (int)%y%)"; +} + +instance Ord Float { + lt = ltFloat; + lte = lteFloat; + gt = gtFloat; + gte = gteFloat; +} +function ltFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% < (float)%y%)"; +} +function lteFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% <= (float)%y%)"; +} +function gtFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% > (float)%y%)"; +} +function gteFloat +Boolean ::= x::Float y::Float +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% >= (float)%y%)"; +} + +instance Ord Boolean { + lt = ltBoolean; + lte = lteBoolean; + gt = gtBoolean; + gte = gteBoolean; +} +function ltBoolean +Boolean ::= x::Boolean y::Boolean +{ + return x == false && y == true; +} +function lteBoolean +Boolean ::= x::Boolean y::Boolean +{ + return x != true || y != false; +} +function gtBoolean +Boolean ::= x::Boolean y::Boolean +{ + return x == true && y == false; +} +function gteBoolean +Boolean ::= x::Boolean y::Boolean +{ + return x != true || y != false; +} + +instance Ord String { + compare = compareString; + lt = ltString; + lte = lteString; + gt = gtString; + gte = gteString; +} +function compareString +Integer ::= l::String r::String +{ + return if l <= r then if l == r then 0 else -1 else 1; +} foreign { + "java" : return "Integer.valueOf(%l%.toString().compareTo(%r%.toString()))"; +} + +function ltString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x%.toString().compareTo(%y%.toString()) < 0)"; +} +function lteString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x%.toString().compareTo(%y%.toString()) <= 0)"; +} +function gtString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x%.toString().compareTo(%y%.toString()) > 0)"; +} +function gteString +Boolean ::= x::String y::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x%.toString().compareTo(%y%.toString()) >= 0)"; +} + +instance Ord TerminalId { + compare = compareTerminalId; + lt = ltTerminalId; + lte = lteTerminalId; + gt = gtTerminalId; + gte = gteTerminalId; +} +function compareTerminalId +Integer ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% - (int)%y%)"; +} +function ltTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% < (int)%y%)"; +} +function lteTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% <= (int)%y%)"; +} +function gtTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% > (int)%y%)"; +} +function gteTerminalId +Boolean ::= x::TerminalId y::TerminalId +{ + return error("Foreign function"); +} foreign { + "java" : return "(%x% >= (int)%y%)"; +} + +instance Ord a => Ord [a] { + lte = \ x::[a] y::[a] -> + case x, y of + | h1::t1, h2::t2 -> if h1 == h2 then t1 <= t2 else h1 < h2 + | [], _ -> true + | _, _ -> false + end; +} + +instance Ord a => Ord Maybe { + lte = \ x::Maybe y::Maybe -> + case x, y of + | just(w), just(z) -> w <= z + | nothing(), _ -> true + | _, _ -> false + end; +} + +instance Ord a, Ord b => Ord Pair { + lte = \ x::Pair y::Pair -> x.fst <= y.fst && x.snd <= y.snd; +} + +instance Ord a, Ord b => Ord Either { + compare = \ x::Either y::Either -> + case x, y of + | left(w), left(z) -> compare(w, z) + | left(_), right(_) -> -1 + | right(_), left(_) -> 1 + | right(w), right(z) -> compare(w, z) + end; +} + +instance Ord Unit { + compare = \ Unit Unit -> 0; +} + +instance Ord Location { + lte = \ l1::Location l2::Location -> + -- TODO: We could probably just compare based on filename and index + -- For the moment, though, use line & column instead. + l1.filename < l2.filename || (l1.filename == l2.filename && + (l1.line < l2.line || (l1.line == l2.line && + (l1.column < l2.column)))); +} + diff --git a/grammars/silver/core/Origins.sv b/grammars/silver/core/Origins.sv new file mode 100644 index 000000000..dc7455fd4 --- /dev/null +++ b/grammars/silver/core/Origins.sv @@ -0,0 +1,360 @@ +grammar silver:core; + +-- WARNING: Many of the nonterminals and productions in this file are runtime- and/or compiler-blessed. +-- Don't change their names, grammar locations, or parameters unless you know what your doing +-- (and have made the appropriate runtime and compiler changes!) + +nonterminal OriginInfo; +nonterminal OriginInfoType; +nonterminal OriginNote; + +synthesized attribute notepp :: String occurs on OriginNote; + +synthesized attribute isNewlyConstructed :: Boolean occurs on OriginInfo; +synthesized attribute originNotes :: [OriginNote] occurs on OriginInfo; +synthesized attribute originType :: OriginInfoType occurs on OriginInfo; + +synthesized attribute isBogus :: Boolean occurs on OriginInfoType; + +@@{- ## Origin info types + - + - Single instances of the following are constructed once in OriginsUtil.java in the runtime and used + - to indicate when the origin information was computed. + -} + +@{- Information was computed at the site of invoking a constructor (this is "normal") -} +abstract production setAtConstructionOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- Result of calling new(x) on a tracked nonterminal (including children of x that were also new-ed) -} +abstract production setAtNewOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- + - Result of forwarding to a nonterminal. This is a little weird because there's an extra indirection. + - The attached origin info has an origin pointing to the value that was computed for the production + - to forward to. At forwarding time (in evalForward) it's copied and has an origin attached of this + - type. This is so that it's possible to tell something was forwarded to. + -} +abstract production setAtForwardingOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- Result of doing foo.bar (this is "normal") -} +abstract production setAtAccessOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- The origin was set when constructing a concrete production in the parser (will be a parsedOriginInfo) -} +abstract production setFromParserOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- The origin was set in something constructed in a parser action block -} +abstract production setFromParserActionOIT +top::OriginInfoType ::= +{ + top.isBogus = true; +} + +@{- + - This is a catchall for stuff constructed in java (really only used in the SilverComparator and in the XML lib) + - where the java library dosen't keep track of origins info meaningfully + -} +abstract production setFromFFIOIT +top::OriginInfoType ::= +{ + top.isBogus = true; +} + +@{- This originates from something via a call to `reflect` -} +abstract production setFromReflectionOIT +top::OriginInfoType ::= +{ + top.isBogus = true; +} + +@{- This originates from it's reflective representation via a call to `reify` -} +abstract production setFromReificationOIT +top::OriginInfoType ::= +{ + top.isBogus = true; +} + +@{- + - This was constructed in `main` or in a function called from `main` without + - passing through a context with a meaningful nonterminal to use instead + -} +abstract production setFromEntryOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + +@{- This is a global -} +abstract production setInGlobalOIT +top::OriginInfoType ::= +{ + top.isBogus = false; +} + + +@@{- ## OriginInfo represent the origin information contained in nodes/values -} + +@{- 'catchall' for origins that don't encode other info -} +abstract production otherOriginInfo +top::OriginInfo ::= typ::OriginInfoType source::String notes::[OriginNote] +{ + top.isNewlyConstructed = true; + top.originNotes = notes; + top.originType = typ; +} + +@{- The production originated from a sequence of tokens at `source` in Copper -} +abstract production parsedOriginInfo +top::OriginInfo ::= typ::OriginInfoType source::Location notes::[OriginNote] +{ + top.isNewlyConstructed = true; + top.originNotes = notes; + top.originType = typ; +} + +@@{- The following two are the same modulo if a redex is set or not + - `origin` is the node that this node originated from, `originNotes` are + - notes set on the control-flow path to where the origin was set. + - `redex` is the node that catalyzed the movement of this node to where it + - is now (i.e. where a `foo.bar` happaned that 'moved' the `bar` in the new + - tree. `redexNotes` are similarly the notes set on the control-flow path to + - where the tree motion that set the redex occurred. + - `newlyConstructed` is `er` from the paper, and represents if the node + - is not the result of a basically no-op transformation. + -} + +@{- See above -} +abstract production originOriginInfo +top::OriginInfo ::= typ::OriginInfoType + origin :: a + originNotes :: [OriginNote] + newlyConstructed :: Boolean +{ + top.isNewlyConstructed = newlyConstructed; + top.originNotes = originNotes; + top.originType = typ; +} + +@{- See above -} +abstract production originAndRedexOriginInfo +top::OriginInfo ::= typ::OriginInfoType + origin :: a + originNotes :: [OriginNote] + redex :: b + redexNotes :: [OriginNote] + newlyConstructed :: Boolean +{ + top.isNewlyConstructed = newlyConstructed; + top.originNotes = originNotes; + top.originType = typ; +} + + + +-- These are some simple builtin node types, and may be generated automatically +-- inside the compiler. + +aspect default production +top::OriginNote ::= +{ + top.notepp = "<" ++ hackUnparse(top) ++ ">"; +} + +abstract production traceNote +top::OriginNote ::= loc::String +{ + +} + +abstract production originDbgNote +top::OriginNote ::= string::String +{ + +} + +abstract production dbgNote +top::OriginNote ::= string::String +{ + +} + +abstract production logicalLocationNote +top::OriginNote ::= loc::Location +{ + +} + +@{- + - Can be attached automatically by the compiler to show the control-flow path leading to where an origin + - was set. Actually pretty useful for debugging client code too. + -} +abstract production ruleLocNote +top::OriginNote ::= attributeName::String sourceGrammar::String prod::String nt::String sourceLocation::Location +{ + +} + + +@{- + - Compute the 'chain' of origins leading back to whatever the first thing without an origin (really without + - an origin that has an `origin` field.) -} +function getOriginInfoChain +[OriginInfo] ::= l::a +{ + return case getOriginInfo(l) of + | just(info) -> + case info of + | originOriginInfo(_, o, _, _) -> info :: getOriginInfoChain(o) + | originAndRedexOriginInfo(_, o, _, _, _, _) -> info :: getOriginInfoChain(o) + | _ -> [info] + end + | _ -> [] + end; +} + +@{- Low level accessor for getting OriginInfo (maybe) from a node. -} +function getOriginInfo +Maybe ::= arg::a +{ + return javaGetOrigin(arg); +} + +@{- Walk back to the first thing with an origin in the history of `a`. -} +function getUrOrigin +Maybe ::= arg::a +{ + return case getOriginInfoChain(arg) of + | [] -> nothing() + | l -> just(last(l)) + end; +} + +@{- Try to walk back to a parsedOriginInfo and extract the location the node came from in the source -} +function getParsedOriginLocation +Maybe ::= arg::a +{ + return getParsedOriginLocation_helper(getOriginInfoChain(arg)); +} + +@{- @hide -} +function getParsedOriginLocation_helper +Maybe ::= chain::[OriginInfo] +{ + return case chain of + | [] -> nothing() + | link::rest -> + case link of + | parsedOriginInfo(_, l, _) -> just(l) + | other -> case getParsedOriginLocation_findLogicalLocationNote(other.originNotes) of + | nothing() -> getParsedOriginLocation_helper(rest) + | x -> x + end + end + end; +} + +@{- @hide -} +function getParsedOriginLocation_findLogicalLocationNote +Maybe ::= notes::[OriginNote] +{ + return case notes of + | [] -> nothing() + | logicalLocationNote(l)::_ -> just(l) + | x::r -> getParsedOriginLocation_findLogicalLocationNote(r) + end; +} + +@{- + - Try to walk back to a parsedOriginInfo and extract the location the node came from in the source, + - giving diagnostic garbage if failed. + -} +function getParsedOriginLocationOrFallback +Location ::= arg::a +{ + return case getParsedOriginLocation(arg) of + | just(l) -> l + | _ -> txtLoc("") + end; +} + +@{- + - Dump out two objects in a format for svdraw2 to consume and draw their + - structure and the origins links that connect them (and any intermediate + - objects. The only difference between `start` and `stop` is that they will + - be specially colored in the visualization diagram.) + -} +function printObjectPairForOriginsViz +IOToken ::= start::a stop::b io::IOToken +{ + return printT( + "\n\n\n---SVDRAW2 START---" ++ + "\n" ++ sexprify(start) ++ + "\n" ++ sexprify(stop) ++ + "\n" ++ "---SVDRAW2 END---\n\n\n", io); +} + +@{- @hide -} +function sexprify +String ::= nt::a +{ + return error("Not impl"); +} foreign { + "java" : return "(common.OriginsUtil.sexprify(%nt%))"; +} + +@{- @hide -} +function javaGetOrigin +Maybe ::= arg::a +{ + return error("Not impl"); +} foreign { + "java" : return "common.OriginsUtil.polyGetOrigin(%arg%)"; +} + +@{- @hide -} +function javaGetOriginLink +Maybe ::= arg::OriginInfo +{ + return error("Not impl"); +} foreign { + "java" : return "common.OriginsUtil.getOriginLink(%arg%)"; +} + +closed tracked nonterminal AmbientOriginNT; + +@{- Useful for accessing the "ambient" origin, i.e., what origin does a created node get? Create one and find out! -} +abstract production ambientOrigin +top::AmbientOriginNT ::= +{ + +} + +@{- Call fn in a context where notes have been added to the origins context -} +function callWithListOfNotes +a ::= notes::[OriginNote] fn::(a::=) +{ + return case notes of + | [] -> fn() + | x::xs -> attachNote x on callWithListOfNotes(xs, fn) end + end; +} diff --git a/grammars/core/Pair.sv b/grammars/silver/core/Pair.sv similarity index 76% rename from grammars/core/Pair.sv rename to grammars/silver/core/Pair.sv index 14aa43196..5b472f618 100644 --- a/grammars/core/Pair.sv +++ b/grammars/silver/core/Pair.sv @@ -1,9 +1,9 @@ -grammar core; +grammar silver:core; synthesized attribute fst :: a; synthesized attribute snd :: a; -{-- +@{-- - The basic product type, counterpart to Either. -} nonterminal Pair with fst, snd; @@ -23,7 +23,7 @@ function snd b ::= p::Pair { return p.snd; } -{-- +@{-- - Look up an element in an association list, using the specified equality - function. - @@ -43,6 +43,20 @@ Maybe ::= eqf::(Boolean ::= a a) elem::a lst::[Pair] else lookupBy(eqf, elem, tail(lst)); } +@{-- + - Look up an element in an association list, using ==. + - + - @param elem The element to look up + - @param lst The list of assocation pairs + - @return The first association pair found in the list, where the element + - equaled the first element of the pair. + -} +function lookup +Eq a => Maybe ::= elem::a lst::[Pair] +{ + return lookupBy(eq, elem, lst); +} + function lookupAllBy [b] ::= eqf::(Boolean ::= a a) elem::a lst::[Pair] { @@ -53,13 +67,18 @@ function lookupAllBy else lookupAllBy(eqf, elem, tail(lst)); } -{-- +function lookupAll +Eq a => [b] ::= elem::a lst::[Pair] +{ + return lookupAllBy(eq, elem, lst); +} + +@{-- - Decomposes a list of pairs into a pair of lists. - - unzipPairs(zipWith(pair, lst)) == lst - - @param lst A list to decompose into two lists. - - @return -} function unzipPairs Pair<[a] [b]> ::= lst::[Pair] diff --git a/grammars/core/ParseResult.sv b/grammars/silver/core/ParseResult.sv similarity index 93% rename from grammars/core/ParseResult.sv rename to grammars/silver/core/ParseResult.sv index ba090b0c3..a2b3096e2 100644 --- a/grammars/core/ParseResult.sv +++ b/grammars/silver/core/ParseResult.sv @@ -1,41 +1,42 @@ -grammar core; +grammar silver:core; -{-- +@{-- - true if parsing successfully produced a syntax tree. false if a parse error occurred. -} synthesized attribute parseSuccess :: Boolean; -{-- +@{-- - The ParseError which parseErrors is generated from. -} synthesized attribute parseError :: ParseError; -{-- +@{-- - A string containing the parse errors reported by copper. The format is unspecified, yet. -} synthesized attribute parseErrors :: String; -{-- +@{-- - The parse tree, if parsing was successful. -} synthesized attribute parseTree :: a; -{-- +@{-- - A list of terminals parsed. -} synthesized attribute parseTerminals :: [TerminalDescriptor]; -{-- +@{-- - A container type for the result of calling a parser. - - @param a The start nonterminal type. -} nonterminal ParseResult with parseSuccess, parseError, parseErrors, parseTree, parseTerminals; -{-- +@{-- - Parse failure constructor. - - @param e The error string reported by the parser. + - @param terminals TODO -} abstract production parseFailed top::ParseResult ::= e::ParseError terminals::[TerminalDescriptor] @@ -47,10 +48,11 @@ top::ParseResult ::= e::ParseError terminals::[TerminalDescriptor] top.parseTerminals = terminals; } -{-- +@{-- - Parse success constructor. - - @param t The syntax tree returned by the parser. + - @param terminals TODO -} abstract production parseSucceeded top::ParseResult ::= t::a terminals::[TerminalDescriptor] @@ -63,28 +65,28 @@ top::ParseResult ::= t::a terminals::[TerminalDescriptor] } -{-- +@{-- - Make parsers behave like they used to in previous versions of Silver. - - Exits and prints parse errors if parsing fails, without a stack trace. - - - @deprecated + - @warning Deprecated! - @param pr The ParseResult returned by the parser - @return The syntax tree reported by the parser. Does not return if parsing fails. -} function parseTreeOrDieWithoutStackTrace a ::= pr::ParseResult { - return unsafeTrace(pr.parseTree, if pr.parseSuccess then unsafeIO() else exit(-1, print(pr.parseErrors ++ "\n\n", unsafeIO()))); + return unsafeTrace(pr.parseTree, if pr.parseSuccess then unsafeIO() else exitT(-1, printT(pr.parseErrors ++ "\n\n", unsafeIO()))); } -{-- +@{-- - Representation of a parser error. -} nonterminal ParseError with parseErrors; -{-- +@{-- - This production as currently designed matches up exactly with what copper raises in its syntax error exception. - - @param diagnosticString An un-pretty but convenient way of printing out this parser error. @@ -102,7 +104,7 @@ top::ParseError ::= top.parseErrors = diagnosticString; } -{-- +@{-- - This production accomodates an unknown type of parser error. - - @param diagnosticString A string describing the error diff --git a/grammars/silver/core/Random.sv b/grammars/silver/core/Random.sv new file mode 100644 index 000000000..959e0e168 --- /dev/null +++ b/grammars/silver/core/Random.sv @@ -0,0 +1,65 @@ +grammar silver:core; + +-- Monad for computations involving random number generation. +-- This must live in silver:core because the runtime depends on these productions. +-- Associated utilities are defined in silver:util:random. +nonterminal RandomGen; + +-- Pure random "token" for use with threaded attributes, represented as just a seed value. +-- Unlike IOToken, this is essentially safe to reuse, besides the possibility of +-- repeating random values. +type RandomToken foreign = "Long"; + +production mapRandomGen +top::RandomGen ::= (b ::= a) RandomGen +{} + +production apRandomGen +top::RandomGen ::= RandomGen<(b ::= a)> RandomGen +{} + +production pureRandomGen +top::RandomGen ::= a +{} + +production bindRandomGen +top::RandomGen ::= RandomGen (RandomGen ::= a) +{} + +production randomInteger +top::RandomGen ::= +{} + +production randomRangeInteger +top::RandomGen ::= Integer Integer +{} + +production randomFloat +top::RandomGen ::= +{} + +production randomBoolean +top::RandomGen ::= +{} + +production randomToken_ +top::RandomGen ::= +{} + +instance Functor RandomGen { + map = mapRandomGen; +} + +instance Apply RandomGen { + ap = apRandomGen; +} + +instance Applicative RandomGen { + pure = pureRandomGen; +} + +instance Bind RandomGen { + bind = bindRandomGen; +} + +instance Monad RandomGen {} diff --git a/grammars/silver/core/Ring.sv b/grammars/silver/core/Ring.sv new file mode 100644 index 000000000..a29923919 --- /dev/null +++ b/grammars/silver/core/Ring.sv @@ -0,0 +1,32 @@ +grammar silver:core; + +@{- This is the typeclass that provides the unary and binary `-` operators. + - + - Laws: + - + - * Left Inverse: `sub(x, x) = zero` + - * Right Inverse: `add(sub(zero, x), x) = zero` + - * Negate: `neg(x) = sub(zero, x)` + -} +class Semiring a => Ring a { + @{- The function corresponding to the binary `-` operator. -} + sub :: (a ::= a a); + + @{- The function corresponding to the unary `-` operator. -} + negate :: (a ::= a) = \x::a -> sub(zero, x); +} + +instance Ring Integer { + sub = \a::Integer b::Integer -> a - b; +} + +@{- Converts an integer into an arbitrary ring. -} +function fromInteger +Ring a => a ::= n::Integer +{ + return + if n < 0 then + negate(fromNonnegativeInteger(-n)) + else + fromNonnegativeInteger(n); +} diff --git a/grammars/silver/core/Semigroup.sv b/grammars/silver/core/Semigroup.sv new file mode 100644 index 000000000..13b859725 --- /dev/null +++ b/grammars/silver/core/Semigroup.sv @@ -0,0 +1,42 @@ +grammar silver:core; + +@{- +The class of semigroups (types with an associative binary operation). + +Instances should satisfy the following: + +Associativity + append(x, append(y, z)) = append(append(x, y), z) +-} +class Semigroup a { + append :: (a ::= a a); +} + +-- e.g. [] +instance Alt m => Semigroup m { + append = alt; +} + +instance Semigroup a, Semigroup b => Semigroup (a, b) { + append = \x::(a, b) y::(a, b) -> (append(x.1, y.1), append(x.2, y.2)); +} + +instance Semigroup String { + append = stringAppend; +} + +instance Semigroup a => Semigroup Maybe { + append = \ x::Maybe y::Maybe -> + case x, y of + | just(a), just(b) -> just(a ++ b) + | just(_), nothing() -> x + | nothing(), _ -> y + end; +} + +instance Semigroup Unit { + append = \ x::Unit y::Unit -> + case x, y of + | unit(), unit() -> unit() + end; +} diff --git a/grammars/silver/core/Semiring.sv b/grammars/silver/core/Semiring.sv new file mode 100644 index 000000000..0c5bab98a --- /dev/null +++ b/grammars/silver/core/Semiring.sv @@ -0,0 +1,52 @@ +grammar silver:core; + +@{- Provides operator overloads for the `+` and `*` operators. + - + - Laws: + - + - * Commutative monoid under addition: + - * Associativity: `add(add(x, y), z) = add(x, add(y, z))` + - * Left Identity: `add(zero, x) = x` + - * Right Identity: `add(x, zero) = x` + - * Commutativity: `add(x, y) = add(y, x)` + - * Monoid under multiplication: + - * Associativity: `mul(mul(x, y), z) = mul(x, mul(y, z))` + - * Left Identity: `mul(one, x) = x` + - * Right Identity: `mul(x, one) = x` + - * Multiplication distributes over addition: + - * Left Distributivity: `mul(x, add(y, z)) = add(mul(x, y), mul(x, z))` + - * Right Distributivity: `mul(add(x, y), z) = add(mul(x, z), mul(y, z))` + - * Left Annihilation: `mul(zero, x) = zero` + - * Right Annihilation: `mul(x, zero) = zero` + -} +class Semiring a { + @{- The function corresponding to the `+` operator. -} + add :: (a ::= a a); + @{- The value corresponding to the `0` constant. -} + zero :: a; + @{- The function corresponding to the `*` operator. -} + mul :: (a ::= a a); + @{- The value corresponding to the `1` constant. -} + one :: a; +} + +instance Semiring Integer { + add = \a::Integer b::Integer -> a + b; + zero = 0; + mul = \a::Integer b::Integer -> a * b; + one = 1; +} + +@{- Converts a non-negative integer into an arbitrary semiring. -} +function fromNonnegativeInteger +Semiring a => a ::= n::Integer +{ + local fromN2::a = fromNonnegativeInteger(n / 2); + return + if n == 0 then + zero + else if n % 2 == 0 then + add(fromN2, fromN2) + else + add(add(fromN2, one), fromN2); +} diff --git a/grammars/silver/core/State.sv b/grammars/silver/core/State.sv new file mode 100644 index 000000000..103a56e4c --- /dev/null +++ b/grammars/silver/core/State.sv @@ -0,0 +1,90 @@ +grammar silver:core; + +nonterminal State with stateIn, stateOut, stateVal; + +inherited attribute stateIn::s; +synthesized attribute stateOut::s; +synthesized attribute stateVal::a; + +abstract production bindState +top::State ::= st::State fn::(State ::= a) +{ + st.stateIn = top.stateIn; + local newState::State = fn(st.stateVal); + newState.stateIn = st.stateOut; + top.stateOut = newState.stateOut; + + top.stateVal = newState.stateVal; +} + +abstract production returnState +top::State ::= x::a +{ + top.stateOut = top.stateIn; + top.stateVal = x; +} + +abstract production getState +top::State ::= +{ + top.stateOut = top.stateIn; + top.stateVal = top.stateIn; +} + +abstract production setState +top::State ::= newState::s +{ + top.stateOut = newState; + top.stateVal = unit(); +} + +abstract production modifyState +top::State ::= fun::(s ::= s) +{ + top.stateOut = fun(top.stateIn); + top.stateVal = unit(); +} + +abstract production fixState +top::State ::= fn::(State ::= a) +{ + local st::State = fn(st.stateVal); + st.stateIn = top.stateIn; + top.stateOut = st.stateOut; + top.stateVal = st.stateVal; +} + +instance Functor State { + map = liftM1; +} + +instance Apply State { + ap = apM; +} + +instance Applicative State { + pure = returnState; +} + +instance Bind State { + bind = bindState; +} + +instance Monad State {} + +instance MonadFix State { + mfix = fixState; +} + +function runState +Pair ::= st::State initialState::s +{ + st.stateIn = initialState; + return pair(st.stateOut, st.stateVal); +} + +function evalState +a ::= st::State initialState::s +{ + return runState(st, initialState).snd; +} \ No newline at end of file diff --git a/grammars/silver/core/String.sv b/grammars/silver/core/String.sv new file mode 100644 index 000000000..78babe358 --- /dev/null +++ b/grammars/silver/core/String.sv @@ -0,0 +1,413 @@ +grammar silver:core; + +instance Length String { + length = stringLength; +} + +@{-- + - Compute the length of a string. + - + - @param s The string to compute the length of. + - @return The length of the string. + -} +function stringLength +Integer ::= s::String +{ + return error("foreign function"); +} foreign { + "java": return "Integer.valueOf(%s%.length())"; +} + +@{-- + - Fold a list of strings into one string, by interspersing a separator. + - + - @param sep The separator to place between each string. + - @param lst The list of string to collapse. + - @return The combined string. + -} +function implode +String ::= sep::String lst::[String] +{ + return if null(lst) + then "" + else head(lst) ++ if null(tail(lst)) + then "" + else sep ++ implode(sep, tail(lst)); +} + +@{-- + - Split a string into a list of strings by a separator. If the separtor + - is the empty string then the string is split into single character strings. + - + - @param sep The separator between each of the resulting strings. + - @param str The original string. + - @return The list of strings separated by sep in the original string. + -} +function explode +[String] ::= sep::String str::String +{ + return if sep=="" then explodeSingle(str) + else if str == "" then [] + else explodeNormal(sep, str); +} +function explodeNormal -- do not use +[String] ::= sep::String str::String +{ + local attribute i :: Integer; + i = indexOf(sep, str); + + return if i == -1 + then [str] + else substring(0, i, str) :: + explodeNormal(sep, substring(i+length(sep), length(str), str)); +} +function explodeSingle -- do not use +[String] ::= str::String +{ + return if length(str) == 0 + then [] + else substring(0,1,str) :: + explodeSingle (substring(1,length(str),str)); +} + +@{-- + - Find the index of a needle in the haystack. (Indices are 0-based.) + - + - @param needle The string to find. + - @param haystack The string to find it in. + - @return The index the string occurs at, or -1 if not found. + -} +function indexOf +Integer ::= needle::String haystack::String +{ + return error("Not Yet Implemented: indexOf"); +} foreign { + "java" : return "Integer.valueOf(%haystack%.toString().indexOf(%needle%.toString()))"; +} + +@{-- + - Find the LAST index of a needle in the haystack. (Indices are 0-based.) + - + - @param needle The string to find. + - @param haystack The string to find it in. + - @return The index the string occurs at, or -1 if not found. + -} +function lastIndexOf +Integer ::= needle::String haystack::String +{ + return error("Not Yet Implemented: lastIndexOf"); +} foreign { + "java" : return "Integer.valueOf(%haystack%.toString().lastIndexOf(%needle%.toString()))"; +} + +@{-- + - Return a substring of the original. Indices are 0-based. + - + - @param start The 0-based index to start at. Inclusive. + - @param endl The 0-based index to end before. (Exclusive.) + - @param str The original string. + - @return The resulting substring. + -} +function substring +String ::= start::Integer endl::Integer str::String +{ + return error("Not Yet Implemented: substring"); +} foreign { + "java" : return "(new common.StringCatter(%str%.toString().substring(%start%, %endl%)))"; +} + +@{-- + - Tests if one string is a prefix of another + - + - @param pre The prefix. + - @param s The string to check the prefix of. + - @return true if pre is a prefix of s. false otherwise. + -} +function startsWith +Boolean ::= pre::String s::String +{ + return error("Not Yet Implemented: startsWith"); +} foreign { + "java" : return "Boolean.valueOf(%s%.toString().startsWith(%pre%.toString()))"; +} + +@{-- + - Tests if one string is a postfix of another + - + - @param post The postfix. + - @param s The string to check the postfix of. + - @return true if post is a postfix of s. false otherwise. + -} +function endsWith +Boolean ::= post::String s::String +{ + return error("Not Yet Implemented: endsWith"); +} foreign { + "java" : return "Boolean.valueOf(%s%.toString().endsWith(%post%.toString()))"; +} + +@{-- + - Replaces all instances of 'search' with 'replace' in 'str' + - + - @param search The string to replace + - @param replace The string to substitute in + - @param str The string to operate on + - @return The modified form of 'str' + -} +function substitute +String ::= search::String replace::String str::String +{ + return error("Not Yet Implemented: substitute"); +} foreign { + "java" : return "new common.StringCatter(%str%.toString().replace((CharSequence)%search%.toString(),(CharSequence)%replace%.toString()))"; +} + +@{-- + - Return a string with 's' repeated 'n' times. + - + - @param n The number of times to repeat the string + - @param s The string to repeat + - @return The string with 'n' copies of 's' + -} +function replicate +String ::= n::Integer s::String +{ return error("Not Yet Implemented: replicate"); } +foreign { + "java" : return "new common.StringCatter(new String(new char[%n%.intValue()]).replace(\"\\0\", %s%.toString()))"; +} + + +@{-- + - Tests if all characters of a string are digits. Partially unicode aware. + - See java's Character.isDigit(char). + - + - @param str The string to check + - @return true if all characters are digits. false otherwise. + -} +function isDigit +Boolean ::= str::String +{ + return error("Not Yet Implemented: isDigit"); +} foreign { + "java" : return "common.Util.isDigit(%str%.toString())"; +} + +@{-- + - Tests if all characters of a string are letters. Partially unicode aware. + - See java's Character.isLetter(char). + - + - @param str The string to check + - @return true if all characters are letters. false otherwise. + -} +function isAlpha +Boolean ::= str::String +{ + return error("Not Yet Implemented: isAlpha"); +} foreign { + "java" : return "common.Util.isAlpha(%str%.toString())"; +} + +@{-- + - Tests if all characters of a string are whitespace. Partially unicode aware. + - See java's Character.isWhitespace(char). + - + - Includes space, tab, newline, carriage return, and more. + - + - @param str The string to check + - @return true if all characters are whitespace. false otherwise. + -} +function isSpace +Boolean ::= str::String +{ + return error("Not Yet Implemented: isSpace"); +} foreign { + "java" : return "common.Util.isSpace(%str%.toString())"; +} + +@{-- + - Tests if all characters of a string are lower case. Partially unicode aware. + - See java's Character.isLowerCase(char). + - + - @param str The string to check + - @return true if all characters are lower case. false otherwise. + -} +function isLower +Boolean ::= str::String +{ + return error("Not Yet Implemented: isLower"); +} foreign { + "java" : return "common.Util.isLower(%str%.toString())"; +} + +@{-- + - Tests if all characters of a string are upper case. Partially unicode aware. + - See java's Character.isUpperCase(char). + - + - @param str The string to check + - @return true if all characters are upper case. false otherwise. + -} +function isUpper +Boolean ::= str::String +{ + return error("Not Yet Implemented: isUpper"); +} foreign { + "java" : return "common.Util.isUpper(%str%.toString())"; +} + +@{-- + - Safely converts a string to an integer. + - + - @param str The string to convert + - @return The converted integer wrapped in just, or nothing if the + - conversion failed (e.g. not a number, or the number was too large) + -} +function toIntSafe +Maybe ::= str::String +{ + return error("Not Yet Implemented: toIntSafe"); +} foreign { + "java" : return "common.Util.safetoInt(%str%.toString())"; +} + +@{-- + - String append. Use overloaded append or ++ instead. + -} +function stringAppend +String ::= s1::String s2::String +{ + return error("Foreign function"); +} foreign { + "java" : return "new common.StringCatter(%s1%, %s2%)"; +} + +@{-- + - Converts a list of code points to a string. Note that due to Java's use of + - UCS-2, code points greater than 0xFFFF (i.e. and characters outside the Basic + - Multilingual Plane) aren't supported. + -} +function charsToString +String ::= chars::[Integer] +{ + return error("Foreign Function"); +} foreign { + "java" : return "common.StringCatter.fromChars(%chars%)"; +} + +@{-- + - Converts a string to a list of its UCS-2 characters. Note that this means + - that surrogate pairs are (probably?) not supported, and characters outside + - the Basic Multilingual Plane aren't as a consequence. + -} +function stringToChars +[Integer] ::= str::String +{ + return error("Foreign Function"); +} foreign { + "java" : return "%str%.toChars()"; +} + +@{-- + - Replace all special characters in a string with their escape sequences. + -} +function escapeString +String ::= s::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(common.Util.escapeString(%s%))"; +} + +@{-- + - Replace all escape sequences in a string with corresponding special characters. + -} +function unescapeString +String ::= s::String +{ + return error("Foreign function"); +} foreign { + "java" : return "(common.Util.unescapeString(%s%))"; +} + +@{-- + - Strips extra leading and trailing whitespace from a string. + -} +function stripExtraWhiteSpace +String ::= str::String +{ return implode ("", stripExtraWhiteSpaceHelper( + woLeadingOrEndingWhiteSpace)) ; + + local attribute woLeadingOrEndingWhiteSpace :: [String] ; + woLeadingOrEndingWhiteSpace + = reverse((dropWhile(isSpace, + reverse(dropWhile(isSpace, explode("",str)))))) ; +} + +function stripExtraWhiteSpaceHelper +[String] ::= ss::[String] +{ return if null(ss) + then [ ] + else + if hd==" " || hd=="\n" || hd=="\t" + then (if null(tail(ss)) + then [ ] + else (if nxt==" " || nxt=="\n" || nxt=="\t" + then stripExtraWhiteSpaceHelper(tail(ss)) -- drop hd + else " " :: stripExtraWhiteSpaceHelper(tail(ss)) + -- replace hd with " " + ) + ) + else hd :: stripExtraWhiteSpaceHelper(tail(ss)) ; + + local attribute hd::String ; + hd = head(ss) ; + + local attribute nxt::String ; + nxt = head(tail(ss)) ; +} + +@{-- + - Strips all whitespace from a string. + -} +function stripWhiteSpace +String ::= s::String +{ return implode ("", stripWhiteSpaceHelper(explode("",s))) ; } + +function stripWhiteSpaceHelper +[String] ::= ss::[String] +{ return if null(ss) + then [ ] + else + if hd==" " || hd=="\n" || hd=="\t" + then stripWhiteSpaceHelper(tail(ss)) + else hd :: stripWhiteSpaceHelper(tail(ss)) ; + + local attribute hd::String ; + hd = head(ss) ; +} + +@{-- + - Adds line numbers to a string + -} +function addLineNumbers +String ::= code::String +{ return addLineNums(1, 2, lines) ; + local lines::[String] = explode("\n",code) ; +} + +function addLineNums +String ::= next::Integer width::Integer lines::[String] +{ return if null(lines) + then "" + else pad ++ ln ++ ": " ++ head(lines) ++ "\n" ++ + addLineNums(next+1, width, tail(lines)) ; + local ln::String = toString(next); + local pad::String = implode("", repeat(" ", width - length(ln)) ) ; +} + +function hashString +Integer ::= s::String +{ + return error("Foreign function"); +} foreign { + "java": return "%s%.toString().hashCode()"; +} diff --git a/grammars/core/TerminalDescriptor.sv b/grammars/silver/core/TerminalDescriptor.sv similarity index 82% rename from grammars/core/TerminalDescriptor.sv rename to grammars/silver/core/TerminalDescriptor.sv index a998ec552..925789b64 100644 --- a/grammars/core/TerminalDescriptor.sv +++ b/grammars/silver/core/TerminalDescriptor.sv @@ -1,11 +1,12 @@ -grammar core; +grammar silver:core; synthesized attribute lexeme :: String; synthesized attribute lexerClasses :: [String]; synthesized attribute terminalLocation :: Location; synthesized attribute terminalName :: String; -nonterminal TerminalDescriptor with lexeme, lexerClasses, terminalLocation, terminalName; +nonterminal TerminalDescriptor with lexeme, lexerClasses, terminalLocation, terminalName, compareTo, isEqual; +propagate compareTo, isEqual on TerminalDescriptor; abstract production terminalDescriptor top::TerminalDescriptor ::= lexeme::String lexerClasses::[String] terminalName::String terminalLocation::Location diff --git a/grammars/silver/core/TerminalId.sv b/grammars/silver/core/TerminalId.sv new file mode 100644 index 000000000..5184e2835 --- /dev/null +++ b/grammars/silver/core/TerminalId.sv @@ -0,0 +1,15 @@ +grammar silver:core; + +-- TODO: Should these be generic list functions? +function terminalSetEq +Boolean ::= ts1::[TerminalId] ts2::[TerminalId] +{ + return length(ts1) == length(ts2) && all(zipWith(eq, sort(ts1), sort(ts2))); +} + +function terminalSubset +Boolean ::= ts1::[TerminalId] ts2::[TerminalId] +{ + -- Probably more efficient than sorting if ts1 is small? + return all(map(contains(_, ts2), ts1)); +} diff --git a/grammars/silver/core/Undecorate.sv b/grammars/silver/core/Undecorate.sv new file mode 100644 index 000000000..76ea32594 --- /dev/null +++ b/grammars/silver/core/Undecorate.sv @@ -0,0 +1,22 @@ +grammar silver:core; + +function new +a ::= x::Decorated a with i +{ return error("foreign function"); } +foreign { + "java": return "common.OriginsUtil.duplicatePoly(%x%.undecorate(), originCtx)"; +} + +function newPartial +a ::= x::PartiallyDecorated a with i +{ return error("foreign function"); } +foreign { + "java": return "common.OriginsUtil.duplicatePoly(%x%.undecorate(), originCtx)"; +} + +function castRef +i1 subset i2 => Decorated a with i1 ::= x::Decorated a with i2 +{ return error("foreign function"); } +foreign { + "java": return "%x%"; +} diff --git a/grammars/silver/core/Unit.sv b/grammars/silver/core/Unit.sv new file mode 100644 index 000000000..dc59f1e92 --- /dev/null +++ b/grammars/silver/core/Unit.sv @@ -0,0 +1,7 @@ +grammar silver:core; + +nonterminal Unit; + +abstract production unit +top::Unit ::= +{} \ No newline at end of file diff --git a/grammars/silver/definition/DocConfig.sv b/grammars/silver/definition/DocConfig.sv deleted file mode 100644 index ccb812ae2..000000000 --- a/grammars/silver/definition/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:definition; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Definition\nmenu_title: Definition\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/definition/concrete_syntax/NonTerminalDcl.sv b/grammars/silver/definition/concrete_syntax/NonTerminalDcl.sv deleted file mode 100644 index 2ef7a8673..000000000 --- a/grammars/silver/definition/concrete_syntax/NonTerminalDcl.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar silver:definition:concrete_syntax; - -aspect production nonterminalDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - -- TODO: We are building this for every nonterminal declaration, when it should - -- be the same for all nonterminals in the grammar - local med :: ModuleExportedDefs = - moduleExportedDefs(top.location, top.compiledGrammars, top.grammarDependencies, [top.grammarName], []); - local syntax::Syntax = foldr(consSyntax, nilSyntax(), med.syntaxAst); - syntax.containingGrammar = error("This shouldn't be needed..."); - syntax.cstEnv = error("This shouldn't be needed..."); - syntax.cstNTProds = error("This shouldn't be needed..."); - syntax.classTerminals = error("This shouldn't be needed..."); - syntax.parserAttributeAspects = error("This shouldn't be needed..."); - syntax.layoutTerms = error("This shouldn't be needed..."); - syntax.prefixesForTerminals = error("This shouldn't be needed..."); - syntax.superClasses = error("This shouldn't be needed..."); - syntax.subClasses = error("This shouldn't be needed..."); - - local exportedLayoutTerms::[String] = syntax.allIgnoreTerminals; - local exportedProds::[String] = map((.fullName), syntax.allProductions); - - top.syntaxAst := - [syntaxNonterminal( - nonterminalType(fName, tl.types), nilSyntax(), - exportedProds, exportedLayoutTerms, - foldr(consNonterminalMod, nilNonterminalMod(), nm.nonterminalModifiers))]; -} - -monoid attribute nonterminalModifiers :: [SyntaxNonterminalModifier] with [], ++; -attribute nonterminalModifiers occurs on NonterminalModifiers, NonterminalModifierList, NonterminalModifier; -propagate nonterminalModifiers on NonterminalModifiers, NonterminalModifierList; diff --git a/grammars/silver/definition/concrete_syntax/ParserSpec.sv b/grammars/silver/definition/concrete_syntax/ParserSpec.sv deleted file mode 100644 index 162cc6180..000000000 --- a/grammars/silver/definition/concrete_syntax/ParserSpec.sv +++ /dev/null @@ -1,80 +0,0 @@ -grammar silver:definition:concrete_syntax; - -import silver:driver:util only computeDependencies; -- TODO this is a bad dependency!! - -{-- - - An abstract representation of a parser declaration. - -} -nonterminal ParserSpec with - sourceGrammar, sourceLocation, fullName, - compiledGrammars, - cstAst, startNT, moduleNames; - -{-- - - Given compiledGrammars, gives back the SyntaxRoot representing this parser. - -} -synthesized attribute cstAst :: SyntaxRoot; - -{-- - - The full name of the start nonterminal of this parser spec. - -} -synthesized attribute startNT :: String; - -{-- - - Prefixes to inject onto terminals in the composed parser. - -} -monoid attribute terminalPrefixes :: [Pair] with [], ++; - -{-- - - Prefixes to inject onto the marking terminals of grammars in the composed parser. - -} -monoid attribute grammarTerminalPrefixes :: [Pair] with [], ++; - - -abstract production parserSpec -top::ParserSpec ::= - sl::Location sg::String fn::String snt::String grams::[String] - customStartLayout::Maybe<[String]> - terminalPrefixes::[Pair] grammarTerminalPrefixes::[Pair] - addedDcls::[SyntaxDcl] -{ - top.sourceLocation = sl; - top.sourceGrammar = sg; - top.fullName = fn; - top.startNT = snt; - top.moduleNames := grams; - - -- We've decided we're using only the grammars in this parser to compute dependencies, as opposed - -- to all grammars imported in the env. - -- This could affect which conditional imports get triggered, and thus what gets included in the parser - production deps :: [String] = computeDependencies(grams, top.compiledGrammars); - production med :: ModuleExportedDefs = moduleExportedDefs(sl, top.compiledGrammars, deps, grams, []); - - -- Compute the list of terminal prefixes for marking terminals here, because the set of marking - -- terminals exported by a grammar depends on conditional imports that may be triggered by other - -- grammars included in the parser. - -- Also works around a build bug (https://github.com/melt-umn/silver/issues/36) - the grammar - -- containing the parser spec (and thus the ParserSpec itself) does not get rebuilt when only its - -- dependencies are modified, thus the set of marking terminals exported by a grammar when the - -- ParserSpec is built may be out of date. - -- componentGrammarMarkingTerminals is also needed to help determine the prefix seperator. - production componentGrammarMarkingTerminals::[Pair] = - map( - \ g::String -> - pair(g, - foldr( - consSyntax, nilSyntax(), - moduleExportedDefs(sl, top.compiledGrammars, deps, [g], []).syntaxAst).allMarkingTerminals), - grams); - production markingTerminalPrefixes::[Pair] = - flatMap( - \ gp::Pair -> - map(pair(_, gp.snd), lookupBy(stringEq, gp.fst, componentGrammarMarkingTerminals).fromJust), - grammarTerminalPrefixes); - - top.cstAst = - cstRoot( - fn, snt, foldr(consSyntax, nilSyntax(), addedDcls ++ med.syntaxAst), - customStartLayout, terminalPrefixes ++ markingTerminalPrefixes, componentGrammarMarkingTerminals); -} - diff --git a/grammars/silver/definition/concrete_syntax/ProductionDcl.sv b/grammars/silver/definition/concrete_syntax/ProductionDcl.sv deleted file mode 100644 index 2b7825c43..000000000 --- a/grammars/silver/definition/concrete_syntax/ProductionDcl.sv +++ /dev/null @@ -1,149 +0,0 @@ -grammar silver:definition:concrete_syntax; - -autocopy attribute productionName :: String; - -concrete production concreteProductionDcl -top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody -{ - top.unparse = "concrete production " ++ id.unparse ++ "\n" ++ ns.unparse ++ " " ++ pm.unparse ++ "\n" ++ body.unparse; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - production namedSig :: NamedSignature = ns.namedSignature; - - pm.productionName = fName; - ns.signatureName = fName; - ns.env = newScopeEnv(ns.defs, top.env); - - top.errors <- pm.errors; - top.errors <- ns.concreteSyntaxTypeErrors; - - -- TODO: we should CHANGE syntaxProduction so it just plain takes a NamedSignature! - top.syntaxAst := [ - syntaxProduction(namedSig, - foldr(consProductionMod, nilProductionMod(), pm.productionModifiers))]; - - forwards to productionDcl('abstract', $2, id, ns, body, location=top.location); -} - -nonterminal ProductionModifiers with config, location, unparse, productionModifiers, errors, env, productionName; -- 0 or some -nonterminal ProductionModifierList with config, location, unparse, productionModifiers, errors, env, productionName; -- 1 or more -closed nonterminal ProductionModifier with config, location, unparse, productionModifiers, errors, env, productionName; -- 1 - -monoid attribute productionModifiers :: [SyntaxProductionModifier] with [], ++; - -propagate productionModifiers on ProductionModifiers, ProductionModifierList; -propagate errors on ProductionModifiers, ProductionModifierList, ProductionModifier; - -concrete production productionModifiersNone -top::ProductionModifiers ::= -{ - top.unparse = ""; -} -concrete production productionModifierSome -top::ProductionModifiers ::= pm::ProductionModifierList -{ - top.unparse = pm.unparse; -} - -concrete production productionModifierSingle -top::ProductionModifierList ::= pm::ProductionModifier -{ - top.unparse = pm.unparse; -} -concrete production productionModifiersCons -top::ProductionModifierList ::= h::ProductionModifier ',' t::ProductionModifierList -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; -} - - -concrete production productionModifierPrecedence -top::ProductionModifier ::= 'precedence' '=' i::Int_t -{ - top.unparse = "precedence = " ++ i.lexeme; - - top.productionModifiers := [prodPrecedence(toInteger(i.lexeme))]; -} - -terminal Operator_kwd /operator/ lexer classes {KEYWORD,RESERVED}; - -concrete production productionModifierOperator -top::ProductionModifier ::= 'operator' '=' n::QName -{ - top.unparse = "operator = " ++ n.unparse; - - top.productionModifiers := [prodOperator(n.lookupType.fullName)]; - - top.errors <- n.lookupType.errors ++ - if !n.lookupType.typerep.isTerminal - then [err(n.location, n.unparse ++ " is not a terminal.")] - else []; -} - --------------------------------------------------------------------------------- --- Type sanity checking on concrete productions - -monoid attribute concreteSyntaxTypeErrors :: [Message] with [], ++; -attribute concreteSyntaxTypeErrors occurs on ProductionSignature, ProductionRHS, ProductionRHSElem; -propagate concreteSyntaxTypeErrors on ProductionSignature, ProductionRHS, ProductionRHSElem; - -aspect production productionSignature -top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS -{ - local fstType :: Type = head(top.namedSignature.inputElements).typerep; - local lstType :: Type = last(top.namedSignature.inputElements).typerep; - - local checkFirst :: Boolean = - fstType.isTerminal || !null(getOccursDcl("core:location", fstType.typeName, top.env)); - local checkSecond :: Boolean = - lstType.isTerminal || !null(getOccursDcl("core:location", lstType.typeName, top.env)); - local errFirst :: [Message] = - if checkFirst then [] else [err(top.location, "Production has location annotation, but first element of signature does not have a location.")]; - local errSecond :: [Message] = - if checkSecond then [] else [err(top.location, "Production has location annotation, but last element of signature does not have a location.")]; - - top.concreteSyntaxTypeErrors <- - if null(top.namedSignature.namedInputElements) then - [] - else if length(top.namedSignature.namedInputElements) == 1 then - if head(top.namedSignature.namedInputElements).elementName == "core:location" then - if length(top.namedSignature.inputElements) > 1 then - errFirst ++ errSecond - else if null(top.namedSignature.inputElements) then - [] -- yay, done! - else - errFirst - else - [err(top.location, "Annotation on this production is not handlable by the parser generator.")] - else - [err(top.location, "Annotations on this production are not handlable by the parser generator.")]; -} - -aspect production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - top.concreteSyntaxTypeErrors <- - if t.typerep.permittedInConcreteSyntax then [] - else [err(t.location, t.unparse ++ " is not permitted on concrete productions. Only terminals and nonterminals (without type variables) can appear here")]; -} - -synthesized attribute permittedInConcreteSyntax :: Boolean occurs on Type; - -aspect default production -top::Type ::= -{ - top.permittedInConcreteSyntax = false; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.permittedInConcreteSyntax = null(params); -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.permittedInConcreteSyntax = true; -} - diff --git a/grammars/silver/definition/concrete_syntax/Project.sv b/grammars/silver/definition/concrete_syntax/Project.sv deleted file mode 100644 index 8cad6bc9d..000000000 --- a/grammars/silver/definition/concrete_syntax/Project.sv +++ /dev/null @@ -1,11 +0,0 @@ -grammar silver:definition:concrete_syntax; - -imports silver:definition:core; -imports silver:definition:type:syntax; - -imports silver:definition:env; -imports silver:definition:type; - -imports silver:definition:concrete_syntax:ast; - -option silver:modification:copper; diff --git a/grammars/silver/definition/concrete_syntax/Root.sv b/grammars/silver/definition/concrete_syntax/Root.sv deleted file mode 100644 index 03bbdf5ec..000000000 --- a/grammars/silver/definition/concrete_syntax/Root.sv +++ /dev/null @@ -1,19 +0,0 @@ -grammar silver:definition:concrete_syntax; - -monoid attribute syntaxAst :: [SyntaxDcl] with [], ++; -monoid attribute parserSpecs :: [ParserSpec] with [], ++; - -attribute syntaxAst, parserSpecs occurs on Root, AGDcls, AGDcl; -propagate syntaxAst, parserSpecs on Root, AGDcls; - -aspect default production -top::AGDcl ::= -{ - propagate syntaxAst, parserSpecs; -} - -aspect production appendAGDcl -top::AGDcl ::= ag1::AGDcl ag2::AGDcl -{ - propagate syntaxAst, parserSpecs; -} diff --git a/grammars/silver/definition/concrete_syntax/RootSpec.sv b/grammars/silver/definition/concrete_syntax/RootSpec.sv deleted file mode 100644 index 8ef5b68f9..000000000 --- a/grammars/silver/definition/concrete_syntax/RootSpec.sv +++ /dev/null @@ -1,71 +0,0 @@ -grammar silver:definition:concrete_syntax; - -import silver:driver:util; - -attribute syntaxAst, parserSpecs occurs on RootSpec, ModuleExportedDefs, Grammar; -propagate syntaxAst, parserSpecs on Grammar; - -monoid attribute maybeSyntaxAst::Maybe<[SyntaxDcl]> with nothing(), orElse; -monoid attribute maybeParserSpecs::Maybe<[ParserSpec]> with nothing(), orElse; -attribute maybeSyntaxAst, maybeParserSpecs occurs on InterfaceItems, InterfaceItem; -propagate maybeSyntaxAst, maybeParserSpecs on InterfaceItems; - -aspect production consInterfaceItem -top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems -{ - top.interfaceErrors <- if !top.maybeSyntaxAst.isJust then ["Missing item syntaxAst"] else []; - top.interfaceErrors <- if !top.maybeParserSpecs.isJust then ["Missing item parserSpecs"] else []; -} - -aspect default production -top::InterfaceItem ::= -{ - top.maybeSyntaxAst := nothing(); - top.maybeParserSpecs := nothing(); -} - -abstract production syntaxAstInterfaceItem -top::InterfaceItem ::= val::[SyntaxDcl] -{ - top.maybeSyntaxAst := just(val); -} - -abstract production parserSpecsInterfaceItem -top::InterfaceItem ::= val::[ParserSpec] -{ - top.maybeParserSpecs := just(val); -} - -aspect function unparseRootSpec -String ::= r::Decorated RootSpec -{ - interfaceItems <- [syntaxAstInterfaceItem(r.syntaxAst)]; - interfaceItems <- [parserSpecsInterfaceItem(r.parserSpecs)]; -} - -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - propagate syntaxAst, parserSpecs; -} - -aspect production grammarRootSpec -top::RootSpec ::= c1::Grammar _ _ _ _ -{ - propagate syntaxAst, parserSpecs; -} - -aspect production interfaceRootSpec -top::RootSpec ::= i::InterfaceItems interfaceTime::Integer _ -{ - top.syntaxAst := i.maybeSyntaxAst.fromJust; - top.parserSpecs := i.maybeParserSpecs.fromJust; -} - -aspect production moduleExportedDefs -top::ModuleExportedDefs ::= l::Location compiled::EnvTree grammarDependencies::[String] need::[String] seen::[String] -{ - top.syntaxAst := if null(need) || null(rs) then [] else (head(rs).syntaxAst ++ recurse.syntaxAst); - top.parserSpecs := if null(need) || null(rs) then [] else (head(rs).parserSpecs ++ recurse.parserSpecs); -} - diff --git a/grammars/silver/definition/concrete_syntax/TerminalDcl.sv b/grammars/silver/definition/concrete_syntax/TerminalDcl.sv deleted file mode 100644 index 65f546885..000000000 --- a/grammars/silver/definition/concrete_syntax/TerminalDcl.sv +++ /dev/null @@ -1,160 +0,0 @@ -grammar silver:definition:concrete_syntax; - -import silver:definition:regex; - -terminal Ignore_kwd 'ignore' lexer classes {KEYWORD}; -terminal Marking_kwd 'marking' lexer classes {KEYWORD}; -terminal Named_kwd 'named' lexer classes {KEYWORD}; -terminal Left_kwd 'left' lexer classes {KEYWORD}; -terminal Association_kwd 'association' lexer classes {KEYWORD}; -terminal Right_kwd 'right' lexer classes {KEYWORD}; - --- We actually need to reserved this due to its appearance in PRODUCTION modifiers. -terminal Precedence_kwd 'precedence' lexer classes {KEYWORD,RESERVED}; - -abstract production terminalDclDefault -top::AGDcl ::= t::TerminalKeywordModifier id::Name r::RegExpr tm::TerminalModifiers -{ - top.unparse = t.unparse ++ "terminal " ++ id.unparse ++ " " ++ r.unparse ++ " " ++ tm.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ id.name; - - top.defs := [termDef(top.grammarName, id.location, fName, r.terminalRegExprSpec)]; - - top.errors <- - if length(getTypeDclAll(fName, top.env)) > 1 - then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- - if isLower(substring(0,1,id.name)) - then [err(id.location, "Types must be capitalized. Invalid terminal name " ++ id.name)] - else []; - - -- This is a crude check, but effective. - top.errors <- - if indexOf("\\n", r.terminalRegExprSpec.regString) != -1 && indexOf("\\r", r.terminalRegExprSpec.regString) == -1 - then [wrn(r.location, "Regex contains '\\n' but not '\\r'. This is your reminder about '\\r\\n' newlines.")] - else []; - - propagate errors; - - top.syntaxAst := [ - syntaxTerminal(fName, r.terminalRegExprSpec, - foldr(consTerminalMod, nilTerminalMod(), t.terminalModifiers ++ tm.terminalModifiers))]; -} - -concrete production terminalDclKwdModifiers -top::AGDcl ::= t::TerminalKeywordModifier 'terminal' id::Name r::RegExpr ';' -{ - forwards to terminalDclDefault(t, id, r, terminalModifiersNone(location=$5.location), location=top.location); -} - -concrete production terminalDclAllModifiers -top::AGDcl ::= t::TerminalKeywordModifier 'terminal' id::Name r::RegExpr tm::TerminalModifiers ';' -{ - forwards to terminalDclDefault(t, id, r, tm, location=top.location); -} - -{-- - - This exists as a catch-all for representing regular expressions for terminals. - - There's only one option here, but it's an extension point. - -} -nonterminal RegExpr with config, location, grammarName, unparse, terminalRegExprSpec; - -synthesized attribute terminalRegExprSpec :: Regex; - -concrete production regExpr -top::RegExpr ::= '/' r::Regex '/' -layout {} -{ - top.unparse = "/" ++ r.regString ++ "/"; - top.terminalRegExprSpec = r; -} - - -closed nonterminal TerminalKeywordModifier with unparse, location, terminalModifiers; - -concrete production terminalKeywordModifierIgnore -top::TerminalKeywordModifier ::= 'ignore' -{ - top.unparse = "ignore "; - - top.terminalModifiers := [termIgnore()]; -} - -concrete production terminalKeywordModifierMarking -top::TerminalKeywordModifier ::= 'marking' -{ - top.unparse = "marking "; - - top.terminalModifiers := [termMarking()]; -} - -concrete production terminalKeywordModifierNone -top::TerminalKeywordModifier ::= -{ - top.unparse = ""; - - top.terminalModifiers := []; -} - - -nonterminal TerminalModifiers with config, location, unparse, terminalModifiers, errors, env, grammarName, compiledGrammars, flowEnv; -closed nonterminal TerminalModifier with config, location, unparse, terminalModifiers, errors, env, grammarName, compiledGrammars, flowEnv; - -monoid attribute terminalModifiers :: [SyntaxTerminalModifier] with [], ++; - -propagate terminalModifiers, errors on TerminalModifiers; - -abstract production terminalModifiersNone -top::TerminalModifiers ::= -{ - top.unparse = ""; -} -concrete production terminalModifierSingle -top::TerminalModifiers ::= tm::TerminalModifier -{ - top.unparse = tm.unparse; -} -concrete production terminalModifiersCons -top::TerminalModifiers ::= h::TerminalModifier ',' t::TerminalModifiers -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; -} - -concrete production terminalModifierLeft -top::TerminalModifier ::= 'association' '=' 'left' -{ - top.unparse = "association = left"; - - top.terminalModifiers := [termAssociation("left")]; - top.errors := []; -} -concrete production terminalModifierRight -top::TerminalModifier ::= 'association' '=' 'right' -{ - top.unparse = "association = right"; - - top.terminalModifiers := [termAssociation("right")]; - top.errors := []; -} - -concrete production terminalModifierPrecedence -top::TerminalModifier ::= 'precedence' '=' i::Int_t -{ - top.unparse = "precedence = " ++ i.lexeme; - - top.terminalModifiers := [termPrecedence(toInteger(i.lexeme))]; - top.errors := []; -} - -concrete production terminalModifierNamed -top::TerminalModifier ::= 'named' name::String_t -{ - top.unparse = "named " ++ name.lexeme; - - top.terminalModifiers := [termPrettyName(substring(1, length(name.lexeme) - 1, name.lexeme))]; - top.errors := []; -} diff --git a/grammars/silver/definition/concrete_syntax/ast/CstAst.sv b/grammars/silver/definition/concrete_syntax/ast/CstAst.sv deleted file mode 100644 index d8370fbd0..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/CstAst.sv +++ /dev/null @@ -1,200 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -imports silver:definition:regex; -imports silver:definition:type; -imports silver:definition:env; - -imports silver:translation:java:core only makeIdName, makeClassName, makeNTClassName; -imports silver:translation:java:type only transType; - -import silver:util:raw:graph as g; -import silver:util:raw:treeset as s; - -{-- - - Encapsulates transformations and analysis of Syntax - -} -closed nonterminal SyntaxRoot with cstErrors, xmlCopper; - -{-- - - Translation of a CST AST to Copper XML. - -} -synthesized attribute xmlCopper :: String; - -abstract production cstRoot -top::SyntaxRoot ::= - parsername::String startnt::String s::Syntax - customStartLayout::Maybe<[String]> terminalPrefixes::[Pair] componentGrammarMarkingTerminals::[Pair] -{ - s.cstEnv = directBuildTree(s.cstDcls); - s.cstNTProds = directBuildTree(s.cstProds); - s.classTerminals = directBuildTree(s.classTerminalContribs); - s.containingGrammar = "host"; - s.superClasses = - directBuildTree( - g:toList( - g:transitiveClosure( - g:add( - s.superClassContribs, - g:empty(compareString))))); - s.subClasses = - directBuildTree( - g:toList( - g:transitiveClosure( - g:add( - map(\ p::Pair -> pair(p.snd, p.fst), s.superClassContribs), - g:empty(compareString))))); - s.parserAttributeAspects = directBuildTree(s.parserAttributeAspectContribs); - s.layoutTerms = - buildLayoutEnv( - map((.fullName), s.allTerminals), - map((.fullName), s.allProductions ++ s.allNonterminals), - s.layoutContribs); - s.prefixesForTerminals = directBuildTree(terminalPrefixes); - s.componentGrammarMarkingTerminals = directBuildTree(componentGrammarMarkingTerminals); - - -- Move productions under their nonterminal, and sort the declarations - production s2 :: Syntax = - foldr(consSyntax, nilSyntax(), sortBy(syntaxDclLte, s.cstNormalize)); - s2.cstEnv = s.cstEnv; - s2.containingGrammar = "host"; - s2.cstNTProds = error("TODO: make this environment not be decorated?"); -- TODO - s2.classTerminals = s.classTerminals; - s2.superClasses = s.superClasses; - s2.subClasses = s.subClasses; - s2.parserAttributeAspects = s.parserAttributeAspects; - s2.layoutTerms = s.layoutTerms; - s2.prefixesForTerminals = s.prefixesForTerminals; - s2.componentGrammarMarkingTerminals = s.componentGrammarMarkingTerminals; - - -- This should be on s1, because the s2 transform assumes everything is well formed. - -- In particular, it drops productions it can't find an NT for. - top.cstErrors := s.cstErrors; - - production startFound :: [Decorated SyntaxDcl] = searchEnvTree(startnt, s.cstEnv); - - top.cstErrors <- if !null(startFound) then [] - else ["Nonterminal " ++ startnt ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced as parser's starting nonterminal)"]; - - -- The layout before and after the root nonterminal. By default, the layout of the root nonterminal. - production startLayout :: String = - implode("", - map(xmlCopperRef, - map(head, - lookupStrings( - fromMaybe(searchEnvTree(startnt, s.layoutTerms), customStartLayout), - s.cstEnv)))); - - top.xmlCopper = -s""" - - - - ${parsername} - - ${xmlCopperRef(head(startFound))} - ${startLayout} -""" ++ --- TODO fix: ? ---" parsers\n" ++ ---" SingleParser\n" ++ --- This stuff gets dumped onto the outer class: ---" \n" ++ - -s""" tokenList = null; - - public void reset() { - tokenList = new ArrayList(); - } - - public List getTokens() { - return tokenList; // The way we reset this iterator when parsing again is to create a new list, so this is defacto immutable - } - -${s2.lexerClassRefDcls} - ]]> -""" ++ --- If not otherwise specified. We always specify. ---" \n" ++ --- If not otherwise specified. We should do this, maybe... ---" \n" ++ --- Call just before a parse: ---" \n" ++ --- Ditto, after: ---" \n" ++ --- Imports and whatnot: ---" \n" ++ --- This stuff gets dumped onto the semantic action container class: ---" \n" ++ - -s""" - - - - - -""" ++ - -s""" - - - - ${s2.containingGrammar} - - - - - - - ${s2.xmlCopper} -""" ++ --- Disambiguation classes -implode("\n", map((.xmlCopper), s2.disambiguationClasses)) ++ -s""" - - -"""; -} - - -{- -Assumptions we make about initial Syntax: - -1. All type parameter lists are the appropriate length. (Silver type checking) --} - -function makeCopperName -String ::= str::String -{ - return makeIdName(str); -} - --- Compute an environment containg the layout for a given list of items -function buildLayoutEnv -EnvTree ::= allTerms::[String] layoutItems::[String] layoutContribs::[Pair] -{ - -- Build a set of all terminals, for faster lookup - local terms::s:Set = s:add(allTerms, s:empty(compareString)); - -- Build a graph of nonterminals, productions and layout terminals where there is an edge a -> b iff a inherits layout from b - local transitiveLayout::g:Graph = - g:transitiveClosure(g:add(layoutContribs, g:empty(compareString))); - -- For every item that we wish to compute layout (productions and nonterminals), find all inherited layout terminals - local layoutTerms::[Pair] = - map( - \ item::String -> - pair(item, s:toList(s:intersect(terms, g:edgesFrom(item, transitiveLayout)))), - layoutItems); - -- Build the layout EnvTree - return - directBuildTree( - flatMap( - \ item::Pair -> map(pair(item.fst, _), item.snd), - layoutTerms)); -} - diff --git a/grammars/silver/definition/concrete_syntax/ast/LexerClassModifiers.sv b/grammars/silver/definition/concrete_syntax/ast/LexerClassModifiers.sv deleted file mode 100644 index c6a221ee5..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/LexerClassModifiers.sv +++ /dev/null @@ -1,131 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - --- From TerminalModifiers ---synthesized attribute dominatesXML :: String; ---synthesized attribute submitsXML :: String; ---synthesized attribute prefixSeperator :: Maybe; - -autocopy attribute className :: String; - -{-- - - Modifiers for lexer classes. - -} -nonterminal SyntaxLexerClassModifiers with cstEnv, cstErrors, className, classTerminals, superClasses, subClasses, superClassContribs, disambiguationClasses, dominatesXML, submitsXML, prefixSeperator, containingGrammar; - -propagate cstErrors, superClassContribs, disambiguationClasses, dominatesXML, submitsXML, prefixSeperator - on SyntaxLexerClassModifiers; - -abstract production consLexerClassMod -top::SyntaxLexerClassModifiers ::= h::SyntaxLexerClassModifier t::SyntaxLexerClassModifiers -{ - top.cstErrors <- - if h.prefixSeperator.isJust && t.prefixSeperator.isJust - then ["Multiple prefix separators for class " ++ top.className] - else []; -} - -abstract production nilLexerClassMod -top::SyntaxLexerClassModifiers ::= -{} - - - -{-- - - Modifiers for lexer classes. - -} -closed nonterminal SyntaxLexerClassModifier with cstEnv, cstErrors, className, classTerminals, superClasses, subClasses, superClassContribs, disambiguationClasses, dominatesXML, submitsXML, prefixSeperator, containingGrammar; - -{- We default ALL attributes, so we can focus only on those that are interesting in each case... -} -aspect default production -top::SyntaxLexerClassModifier ::= -{ - -- Empty values as defaults - propagate cstErrors, superClassContribs, disambiguationClasses, dominatesXML, submitsXML, prefixSeperator; -} - -{-- - - Other lexer classes of which this is considered a sub-class. - -} -abstract production lexerClassExtends -top::SyntaxLexerClassModifier ::= super::[String] -{ - local superRefsL :: [[Decorated SyntaxDcl]] = lookupStrings(super, top.cstEnv); - production superRefs :: [Decorated SyntaxDcl] = map(head, lookupStrings(super, top.cstEnv)); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from extends clause for lexer class)"], - zipWith(pair, super, superRefsL)); - top.superClassContribs := map(pair(top.className, _), super); -} - -{-- - - The submits list for the lexer class. Either lexer classes or terminals. - -} -abstract production lexerClassSubmits -top::SyntaxLexerClassModifier ::= sub::[String] -{ - production allSubs :: [String] = unionsBy(stringEq, sub :: lookupStrings(sub, top.subClasses)); - production subRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allSubs, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from submit clause for lexer class)"], --TODO: come up with a way to reference a given lexer class (line numbers would be great) - zipWith(pair, sub, subRefs)); - top.submitsXML := implode("", map(xmlCopperRef, map(head, subRefs))); -} -{-- - - The dominates list for the lexer class. Either lexer classes or terminals. - -} -abstract production lexerClassDominates -top::SyntaxLexerClassModifier ::= dom::[String] -{ - production allDoms :: [String] = unionsBy(stringEq, dom :: lookupStrings(dom, top.subClasses)); - production domRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allDoms, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from dominates clause for lexer class)"], - zipWith(pair, dom, domRefs)); - top.dominatesXML := implode("", map(xmlCopperRef, map(head, domRefs))); -} - -{-- - - A disambiguation function that should be created for the members of a lexer class. - -} -abstract production lexerClassDisambiguate -top::SyntaxLexerClassModifier ::= acode::String -{ - production terms :: [String] = searchEnvTree(top.className, top.classTerminals); - production funName::String = s"disambiguate_${makeCopperName(top.className)}"; - - production syntaxDcl::SyntaxDcl = - syntaxDisambiguationGroup(funName, terms, true, s""" -common.ConsCell tempShiftableList = common.ConsCell.nil; -for (int i = nextMember(0, shiftable); i >= 0; i = nextMember(i+1, shiftable)) { - tempShiftableList = new common.ConsCell(i, tempShiftableList); -} -final common.ConsCell shiftableList = tempShiftableList; -${acode} -"""); - -- TODO: Figure out the actual flowtype here - syntaxDcl.cstEnv = top.cstEnv; - syntaxDcl.containingGrammar = top.containingGrammar; - - -- TODO: Check for duplicate disambiguation for a lexer class - - top.disambiguationClasses := [syntaxDcl]; -} - -{-- - - The default prefix separator for the members of a lexer class. - -} -abstract production lexerClassPrefixSeperator -top::SyntaxLexerClassModifier ::= sep::String -{ - top.cstErrors := []; - top.prefixSeperator := just(sep); -} diff --git a/grammars/silver/definition/concrete_syntax/ast/NonterminalModifiers.sv b/grammars/silver/definition/concrete_syntax/ast/NonterminalModifiers.sv deleted file mode 100644 index a509d74e1..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/NonterminalModifiers.sv +++ /dev/null @@ -1,48 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -imports silver:definition:core only nonterminalName; - -{-- - - Modifiers for nonterminals. - -} -nonterminal SyntaxNonterminalModifiers with cstEnv, cstErrors, customLayout, nonterminalName; - -propagate cstErrors, customLayout on SyntaxNonterminalModifiers; - -abstract production consNonterminalMod -top::SyntaxNonterminalModifiers ::= h::SyntaxNonterminalModifier t::SyntaxNonterminalModifiers -{} - -abstract production nilNonterminalMod -top::SyntaxNonterminalModifiers ::= -{} - - -{-- - - Modifiers for nonterminals. - -} -nonterminal SyntaxNonterminalModifier with cstEnv, cstErrors, customLayout, nonterminalName; - -aspect default production -top::SyntaxNonterminalModifier ::= -{ - -- Empty values as defaults - propagate cstErrors, customLayout; -} - -{-- - - The layout for this nonterminal. - -} -abstract production ntLayout -top::SyntaxNonterminalModifier ::= terms::[String] -{ - local termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from layout clause on nonterminal " ++ top.nonterminalName ++ ")"], - zipWith(pair, terms, termRefs)); - - top.customLayout := just(terms); -} diff --git a/grammars/silver/definition/concrete_syntax/ast/PrettyNames.sv b/grammars/silver/definition/concrete_syntax/ast/PrettyNames.sv deleted file mode 100644 index b29c8b07c..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/PrettyNames.sv +++ /dev/null @@ -1,151 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -function asPrettyName -Maybe ::= r::Regex -{ - return mapMaybe(\x::String -> "'" ++ xmlEscapeString(x) ++ "'", r.asLiteral); -} - --- |If the regex matches only a single literal string, the attribute contains --- it. -synthesized attribute asLiteral::Maybe occurs on Regex, RegexSeq, - RegexRepetition, RegexItem, RegexCharSet, RegexCharSetItem, RegexChar; - -aspect production regexEpsilon -top::Regex ::= -{ - top.asLiteral = just(""); -} - -aspect production regexSeq -top::Regex ::= h::RegexSeq -{ - top.asLiteral = h.asLiteral; -} - -aspect production regexChoice -top::Regex ::= _ _ _ -{ - -- TODO: In theory, we could check if one side is empty, and then go to the - -- other. - top.asLiteral = nothing(); -} - - -aspect production regexSeqSnoc -top::RegexSeq ::= h::RegexSeq t::RegexRepetition -{ - top.asLiteral = case pair(h.asLiteral, t.asLiteral) of - | pair(just(h), just(t)) -> just(h ++ t) - | _ -> nothing() - end; -} - -aspect production regexSeqOne -top::RegexSeq ::= t::RegexRepetition -{ - top.asLiteral = t.asLiteral; -} - - -aspect production regexKleene -top::RegexRepetition ::= _ _ -{ - -- TODO: We could check if the repeated construct is zero-length and give out - -- just("") if so. - top.asLiteral = nothing(); -} - -aspect production regexPlus -top::RegexRepetition ::= _ _ -{ - -- TODO: We could check if the repeated construct is zero-length and give out - -- just("") if so. - top.asLiteral = nothing(); -} - -aspect production regexOptional -top::RegexRepetition ::= _ _ -{ - -- TODO: We could check if the repeated construct is zero-length and give out - -- just("") if so. - top.asLiteral = nothing(); -} - -aspect production regexOnce -top::RegexRepetition ::= i::RegexItem -{ - top.asLiteral = i.asLiteral; -} - - -aspect production regexCharItem -top::RegexItem ::= char::RegexChar -{ - top.asLiteral = char.asLiteral; -} - -aspect production regexWildcard -top::RegexItem ::= _ -{ - top.asLiteral = nothing(); -} - -aspect production regexSet -top::RegexItem ::= _ g::RegexCharSet _ -{ - top.asLiteral = g.asLiteral; -} - -aspect production regexSetInverted -top::RegexItem ::= _ _ g::RegexCharSet _ -{ - top.asLiteral = nothing(); -} - -aspect production regexGroup -top::RegexItem ::= _ r::Regex _ -{ - top.asLiteral = r.asLiteral; -} - -aspect production regexCharSetSnoc -top::RegexCharSet ::= h::RegexCharSet t::RegexCharSetItem -{ - top.asLiteral = case pair(h.asLiteral, t.asLiteral) of - | pair(just(h), just(t)) -> just(h ++ t) - | _ -> nothing() - end; -} - -aspect production regexCharSetOne -top::RegexCharSet ::= t::RegexCharSetItem -{ - top.asLiteral = t.asLiteral; -} - -aspect production regexSetChar -top::RegexCharSetItem ::= char::RegexChar -{ - top.asLiteral = char.asLiteral; -} - -aspect production regexSetRange -top::RegexCharSetItem ::= l::RegexChar _ u::RegexChar -{ - -- TODO: In theory, we could actually take this apart. - top.asLiteral = nothing(); -} - -aspect production regexChar -top::RegexChar ::= char::RegexChar_t -{ - top.asLiteral = just(char.lexeme); -} - -aspect production regexEscapedChar -top::RegexChar ::= esc::EscapedChar_t -{ - -- TODO: Support this better. - top.asLiteral = just(substring(1, 2, esc.lexeme)); -} diff --git a/grammars/silver/definition/concrete_syntax/ast/ProductionModifiers.sv b/grammars/silver/definition/concrete_syntax/ast/ProductionModifiers.sv deleted file mode 100644 index 9ecfdebc2..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/ProductionModifiers.sv +++ /dev/null @@ -1,84 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -imports silver:definition:concrete_syntax only productionName; - -monoid attribute productionPrecedence :: Maybe with nothing(), orElse; --- acode from terminal modifiers -monoid attribute customLayout :: Maybe<[String]> with nothing(), orElse; -monoid attribute productionOperator :: Maybe with nothing(), orElse; - -{-- - - Modifiers for productions. - -} -nonterminal SyntaxProductionModifiers with cstEnv, cstErrors, acode, productionPrecedence, customLayout, productionOperator, productionName; - -propagate cstErrors, acode, productionPrecedence, customLayout, productionOperator - on SyntaxProductionModifiers; - -abstract production consProductionMod -top::SyntaxProductionModifiers ::= h::SyntaxProductionModifier t::SyntaxProductionModifiers -{} - -abstract production nilProductionMod -top::SyntaxProductionModifiers ::= -{} - - -{-- - - Modifiers for productions. - -} -nonterminal SyntaxProductionModifier with cstEnv, cstErrors, acode, productionPrecedence, customLayout, productionOperator, productionName; - -aspect default production -top::SyntaxProductionModifier ::= -{ - -- Empty values as defaults - propagate cstErrors, acode, productionPrecedence, customLayout, productionOperator; -} - -{-- - - The precedence for the production. (Resolves reduce/reduce conflicts.) - -} -abstract production prodPrecedence -top::SyntaxProductionModifier ::= lvl::Integer -{ - top.productionPrecedence := just(lvl); -} -{-- - - The terminal this production uses for shift/reduce conflict resolution. - - By default, the last terminal in the production? TODO - -} -abstract production prodOperator -top::SyntaxProductionModifier ::= term::String -{ - local termRef :: [Decorated SyntaxDcl] = searchEnvTree(term, top.cstEnv); - - top.cstErrors := if !null(termRef) then [] - else ["Terminal " ++ term ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from operator clause on production " ++ top.productionName ++ ")"]; - top.productionOperator := just(xmlCopperRef(head(termRef))); -} -{-- - - The action to perform when this production is REDUCEd. - -} -abstract production prodAction -top::SyntaxProductionModifier ::= acode::String -{ - top.acode := acode; -} -{-- - - The layout for this production. - -} -abstract production prodLayout -top::SyntaxProductionModifier ::= terms::[String] -{ - local termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from layout clause on production " ++ top.productionName ++ ")"], - zipWith(pair, terms, termRefs)); - - top.customLayout := just(terms); -} diff --git a/grammars/silver/definition/concrete_syntax/ast/Regex.sv b/grammars/silver/definition/concrete_syntax/ast/Regex.sv deleted file mode 100644 index 73b1324c6..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/Regex.sv +++ /dev/null @@ -1,166 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - --- Translation of regex to Copper XML format. -attribute xmlCopper occurs on Regex, RegexSeq, RegexRepetition, RegexItem, RegexCharSet, RegexCharSetItem, RegexChar; - -{-- - - Used to prevent unneeded nesting of the same operator. (choices, sequences) - -} -synthesized attribute unwrappedXML :: String occurs on Regex, RegexSeq; - - ---------------------------------------------------------------------------------- -aspect production regexEpsilon -top::Regex ::= -{ - top.xmlCopper = ""; - top.unwrappedXML = top.xmlCopper; -} -aspect production regexSeq -top::Regex ::= h::RegexSeq -{ - top.xmlCopper = h.xmlCopper; - top.unwrappedXML = top.xmlCopper; -} -aspect production regexChoice -top::Regex ::= h::RegexSeq _ t::Regex -{ - top.xmlCopper = "" ++ h.xmlCopper ++ t.unwrappedXML ++ ""; - top.unwrappedXML = h.xmlCopper ++ t.unwrappedXML; -} - - -aspect production regexSeqSnoc -top::RegexSeq ::= h::RegexSeq t::RegexRepetition -{ - top.xmlCopper = "" ++ h.unwrappedXML ++ t.xmlCopper ++ ""; - top.unwrappedXML = h.unwrappedXML ++ t.xmlCopper; -} -aspect production regexSeqOne -top::RegexSeq ::= t::RegexRepetition -{ - top.xmlCopper = t.xmlCopper; - top.unwrappedXML = top.xmlCopper; -} - - -aspect production regexKleene -top::RegexRepetition ::= i::RegexItem _ -{ - top.xmlCopper = "" ++ i.xmlCopper ++ ""; -} -aspect production regexPlus -top::RegexRepetition ::= i::RegexItem _ -{ - -- Copper has no direct translation of + - top.xmlCopper = "" ++ i.xmlCopper ++ "" ++ i.xmlCopper ++ ""; -} -aspect production regexOptional -top::RegexRepetition ::= i::RegexItem _ -{ - -- Copper has no direct translation of ? - top.xmlCopper = "" ++ i.xmlCopper ++ ""; -} -aspect production regexOnce -top::RegexRepetition ::= i::RegexItem -{ - top.xmlCopper = i.xmlCopper; -} - - -aspect production regexCharItem -top::RegexItem ::= char::RegexChar -{ - top.xmlCopper = ""; -} -aspect production regexWildcard -top::RegexItem ::= _ -{ - -- Copper has no direct representation of dot. - -- Dot represents everything EXCEPT \n - top.xmlCopper = ""; -} -aspect production regexSet -top::RegexItem ::= _ g::RegexCharSet _ -{ - top.xmlCopper = "" ++ g.xmlCopper ++ ""; -} -aspect production regexSetInverted -top::RegexItem ::= _ _ g::RegexCharSet _ -{ - top.xmlCopper = "" ++ g.xmlCopper ++ ""; -} -aspect production regexGroup -top::RegexItem ::= _ r::Regex _ -{ - -- As an AST, Copper has no need to represent groups () - top.xmlCopper = r.xmlCopper; -} - - -aspect production regexCharSetSnoc -top::RegexCharSet ::= h::RegexCharSet t::RegexCharSetItem -{ - top.xmlCopper = h.xmlCopper ++ t.xmlCopper; -} -aspect production regexCharSetOne -top::RegexCharSet ::= t::RegexCharSetItem -{ - top.xmlCopper = t.xmlCopper; -} - - -aspect production regexSetChar -top::RegexCharSetItem ::= char::RegexChar -{ - top.xmlCopper = ""; -} -aspect production regexSetRange -top::RegexCharSetItem ::= l::RegexChar _ u::RegexChar -{ - top.xmlCopper = ""; -} - - -aspect production regexChar -top::RegexChar ::= char::RegexChar_t -{ - -- Can be special characters like & - top.xmlCopper = xmlEscapeChar(char.lexeme); -} -aspect production regexEscapedChar -top::RegexChar ::= esc::EscapedChar_t -{ - -- These are ESCAPED character (e.g. \n) we need to represent as XML. - -- We only animate \r\n\t. Otherwise, literal translation. - top.xmlCopper = - if esc.lexeme == "\\r" then " " - else if esc.lexeme == "\\n" then " " - else if esc.lexeme == "\\t" then " " - else xmlEscapeChar(substring(1, 2, esc.lexeme)); -} - ------------------------------------------------------------------------------------ - - -{-- XML String to emit to represent the character 'ch' -} -function xmlEscapeChar -String ::= ch::String -{ - return - if ch == ">" then ">" - else if ch == "<" then "<" - else if ch == "&" then "&" - else if ch == "\"" then """ - -- For completeness, there is "'" --> ' - -- but this should only be necessary if we used single quotes for xml-attribute values - -- e.g. - -- We used double quotes, so we should be okay... - else ch; -} - -function xmlEscapeString -String ::= s::String -{ - return implode("", map(xmlEscapeChar, explode("", s))); -} diff --git a/grammars/silver/definition/concrete_syntax/ast/Syntax.sv b/grammars/silver/definition/concrete_syntax/ast/Syntax.sv deleted file mode 100644 index 5e4b2eed0..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/Syntax.sv +++ /dev/null @@ -1,453 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -imports silver:translation:java:core only makeTerminalName; - --- For looking syntax elements up by name. -monoid attribute cstDcls :: [Pair] with [], ++; -autocopy attribute cstEnv :: EnvTree; -monoid attribute cstErrors :: [String] with [], ++; - --- Transformation that moves productions underneath their respective nonterminals. -monoid attribute cstProds :: [Pair] with [], ++; -autocopy attribute cstNTProds :: EnvTree; -monoid attribute cstNormalize :: [SyntaxDcl] with [], ++; - --- Compute and allow lookup of all terminals in a lexer class -monoid attribute classTerminalContribs::[Pair] with [], ++; -autocopy attribute classTerminals::EnvTree; -monoid attribute superClassContribs::[Pair] with [], ++; -autocopy attribute superClasses::EnvTree; -autocopy attribute subClasses::EnvTree; - --- Parser attribute action code aspects -monoid attribute parserAttributeAspectContribs::[Pair] with [], ++; -autocopy attribute parserAttributeAspects::EnvTree; - -monoid attribute allTerminals :: [Decorated SyntaxDcl] with [], ++; -monoid attribute allIgnoreTerminals :: [String] with [], ++; -monoid attribute allMarkingTerminals :: [String] with [], ++; -monoid attribute allProductions :: [Decorated SyntaxDcl] with [], ++; -monoid attribute allNonterminals :: [Decorated SyntaxDcl] with [], ++; -monoid attribute disambiguationClasses :: [Decorated SyntaxDcl] with [], ++; -synthesized attribute classDomContribs :: String; -synthesized attribute classSubContribs :: String; -autocopy attribute containingGrammar :: String; -monoid attribute lexerClassRefDcls :: String with "", ++; -synthesized attribute exportedProds :: [String]; -synthesized attribute hasCustomLayout :: Boolean; -monoid attribute layoutContribs :: [Pair] with [], ++; -- prod/nt name, prod/nt/term name -autocopy attribute layoutTerms::EnvTree; - -autocopy attribute prefixesForTerminals :: EnvTree; -autocopy attribute componentGrammarMarkingTerminals :: EnvTree<[String]>; - - -{-- - - An abstract syntax tree for representing concrete syntax. - -} -nonterminal Syntax with cstDcls, cstEnv, cstErrors, cstProds, cstNTProds, cstNormalize, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allNonterminals, disambiguationClasses, classTerminalContribs, classTerminals, superClassContribs, superClasses, subClasses, parserAttributeAspectContribs, parserAttributeAspects, lexerClassRefDcls, layoutContribs, layoutTerms, xmlCopper, containingGrammar, prefixesForTerminals, componentGrammarMarkingTerminals; - -propagate cstDcls, cstErrors, cstProds, cstNormalize, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allNonterminals, disambiguationClasses, classTerminalContribs, superClassContribs, parserAttributeAspectContribs, lexerClassRefDcls, layoutContribs - on Syntax; - -abstract production nilSyntax -top::Syntax ::= -{ - top.xmlCopper = ""; -} - -abstract production consSyntax -top::Syntax ::= s1::SyntaxDcl s2::Syntax -{ - top.xmlCopper = s1.xmlCopper ++ s2.xmlCopper; -} - -{-- - - An individual declaration of a concrete syntax element. - -} -nonterminal SyntaxDcl with cstDcls, cstEnv, cstErrors, cstProds, cstNTProds, cstNormalize, fullName, sortKey, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allNonterminals, disambiguationClasses, classTerminalContribs, classTerminals, superClassContribs, superClasses, subClasses, parserAttributeAspectContribs, parserAttributeAspects, lexerClassRefDcls, exportedProds, hasCustomLayout, layoutContribs, layoutTerms, xmlCopper, classDomContribs, classSubContribs, prefixSeperator, containingGrammar, prefixesForTerminals, componentGrammarMarkingTerminals; - -synthesized attribute sortKey :: String; - -propagate cstErrors, prefixSeperator on SyntaxDcl; - -aspect default production -top::SyntaxDcl ::= -{ - -- Empty values as defaults - propagate cstProds, allTerminals, allIgnoreTerminals, allMarkingTerminals, allProductions, allNonterminals, disambiguationClasses, classTerminalContribs, superClassContribs, parserAttributeAspectContribs, lexerClassRefDcls, layoutContribs; - top.classDomContribs = error("Internal compiler error: should only ever be demanded of lexer classes"); - top.classSubContribs = error("Internal compiler error: should only ever be demanded of lexer classes"); - top.exportedProds = error("Internal compiler error: should only ever be demanded of nonterminals"); - top.hasCustomLayout = false; -} - - -{-- - - A nonterminal. Using Type instead of String, because we'll be doing parameterization later. - - subdcls is empty to start. A transformed version of the tree will move all - - productions for this nonterminal under subdcls. - -} -abstract production syntaxNonterminal -top::SyntaxDcl ::= t::Type subdcls::Syntax exportedProds::[String] exportedLayoutTerms::[String] modifiers::SyntaxNonterminalModifiers -{ - top.fullName = t.typeName; - top.sortKey = "EEE" ++ t.typeName; - top.cstDcls := [pair(t.typeName, top)] ++ subdcls.cstDcls; - top.allNonterminals := [top]; - - top.cstErrors <- if length(searchEnvTree(t.typeName, top.cstEnv)) == 1 then [] - else ["Name conflict with nonterminal " ++ t.typeName]; - top.cstProds := subdcls.cstProds; - top.cstNormalize := - let myProds :: [SyntaxDcl] = searchEnvTree(t.typeName, top.cstNTProds) - in if null(myProds) then [] -- Eliminate "Useless nonterminals" as these are expected in Silver code (non-syntax) - else [syntaxNonterminal(t, foldr(consSyntax, nilSyntax(), myProds), exportedProds, exportedLayoutTerms, modifiers)] - end; - - top.exportedProds = exportedProds; - top.hasCustomLayout = modifiers.customLayout.isJust; - top.layoutContribs := map(pair(t.typeName, _), fromMaybe(exportedLayoutTerms, modifiers.customLayout)); - - top.xmlCopper = - "\n \n" ++ - " " ++ t.typeName ++ "\n" ++ - " \n" ++ - " \n" ++ - subdcls.xmlCopper; - - t.boundVariables = t.freeVariables; -} - -{-- - - A terminal, and regular expression. - -} -abstract production syntaxTerminal -top::SyntaxDcl ::= n::String regex::Regex modifiers::SyntaxTerminalModifiers -{ - top.fullName = n; - top.sortKey = "CCC" ++ n; - top.cstDcls := [pair(n, top)]; - top.cstErrors <- - if length(searchEnvTree(n, top.cstEnv)) == 1 then [] - else ["Name conflict with terminal " ++ n]; - - modifiers.terminalName = n; - - top.allTerminals := [top]; - top.allIgnoreTerminals := if modifiers.ignored then [top.fullName] else []; - top.allMarkingTerminals := if modifiers.marking then [top.fullName] else []; - top.classTerminalContribs := modifiers.classTerminalContribs; - - -- left(terminal name) or right(string prefix) - production pfx::[String] = searchEnvTree(n, top.prefixesForTerminals); - top.cstErrors <- - if length(pfx) <= 1 then [] - else ["Multiple prefixes for terminal " ++ n]; - - top.cstNormalize := - case modifiers.prefixSeperatorToApply of - | just(sep) -> [syntaxTerminal(n, regexConcatenate(regex, regexLiteral(sep)), modifiers)] - | nothing() -> [top] - end; - - local prettyName :: String = fromMaybe(fromMaybe(n, asPrettyName(regex)), modifiers.prettyName); - - top.xmlCopper = - " \n" ++ - " " ++ prettyName ++ "\n" ++ - " " ++ regex.xmlCopper ++ "\n" ++ - (if modifiers.opPrecedence.isJust || modifiers.opAssociation.isJust then - " \n" ++ - " " ++ toString(fromMaybe(0, modifiers.opPrecedence)) ++ "\n" ++ - " " ++ convertAssocNXML(modifiers.opAssociation) ++ "\n" ++ -- TODO - " \n" - else "") ++ - " " ++ makeTerminalName(n) ++ "\n" ++ - " \n" ++ - " " ++ modifiers.lexerclassesXML ++ "\n" ++ - (if null(pfx) then "" - else " \n") ++ - " " ++ modifiers.submitsXML ++ "\n" ++ - " " ++ modifiers.dominatesXML ++ "\n" ++ - " \n"; -} - --- New XML Skin START -function convertAssocNXML -- TODO remove, make attribute -String ::= opassoc::Maybe -{ - local attribute assoc::String; - assoc = fromMaybe("", opassoc); - return if assoc=="left" then "" - else if assoc=="right" then "" - else ""; -} --- New XML Skin END - -{-- - - A (named) production. Using types for later parameterization. - -} -abstract production syntaxProduction -top::SyntaxDcl ::= ns::NamedSignature modifiers::SyntaxProductionModifiers -{ - top.fullName = ns.fullName; - top.sortKey = "FFF" ++ ns.fullName; - top.cstDcls := [pair(ns.fullName, top)]; - top.allProductions := [top]; - - modifiers.productionName = ns.fullName; - - production lhsRef :: [Decorated SyntaxDcl] = - searchEnvTree(ns.outputElement.typerep.typeName, top.cstEnv); - production rhsRefs :: [[Decorated SyntaxDcl]] = - lookupStrings(map((.typeName), map((.typerep), ns.inputElements)), top.cstEnv); - - top.cstErrors <- if length(searchEnvTree(ns.fullName, top.cstEnv)) == 1 then [] - else ["Name conflict with production " ++ ns.fullName]; - - top.cstErrors <- if length(lhsRef) == 1 then - case head(lhsRef) of - | syntaxNonterminal(_,_,_,_,_) -> [] - | _ -> ["LHS of production " ++ ns.fullName ++ " is not a nonterminal"] end - else ["Nonterminal " ++ ns.outputElement.typerep.typeName ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from LHS of production " ++ ns.fullName ++ ")"]; - - top.cstErrors <- checkRHS(ns.fullName, map((.typerep), ns.inputElements), rhsRefs); - - top.cstProds := [pair(ns.outputElement.typerep.typeName, top)]; - top.cstNormalize := []; - - top.hasCustomLayout = modifiers.customLayout.isJust; - top.layoutContribs := - map(pair(ns.fullName, _), fromMaybe([], modifiers.customLayout)) ++ - -- The production inherits its LHS nonterminal's layout, unless overridden. - (if top.hasCustomLayout then [] else [pair(ns.fullName, head(lhsRef).fullName)]) ++ - -- All nonterminals on the RHS that export this production inherit this - -- production's layout, unless overriden on the nonterminal. - flatMap( - \ rhsRef::[Decorated SyntaxDcl] -> - case head(rhsRef) of - | syntaxNonterminal(_,_,_,_,_) - when !head(rhsRef).hasCustomLayout && - containsBy(stringEq, top.fullName, head(rhsRef).exportedProds) -> - [pair(head(rhsRef).fullName, ns.fullName)] - | _ -> [] - end, - rhsRefs); - - -- Copper doesn't support default layout on nonterminals, so we specify layout on every production. - production prodLayout::String = - implode("", - map(xmlCopperRef, - map(head, - lookupStrings(searchEnvTree(ns.fullName, top.layoutTerms), top.cstEnv)))); - - top.xmlCopper = - " \n" ++ - (if modifiers.productionPrecedence.isJust then --- " \n" ++ - " " ++ toString(modifiers.productionPrecedence.fromJust) ++ "\n" - else "") ++ - " \n" ++ - " " ++ xmlCopperRef(head(lhsRef)) ++ "\n" ++ - " " ++ implode("", map(xmlCopperRef, map(head, rhsRefs))) ++ "\n" ++ - " " ++ prodLayout ++ "\n" ++ - (if modifiers.productionOperator.isJust then - " " ++ modifiers.productionOperator.fromJust ++ "\n" - else "") ++ - " \n"; -} - -function fetchChildren -String ::= i::Integer ns::[NamedSignatureElement] -{ - return if null(ns) then "" - else if null(tail(ns)) then "_children[" ++ toString(i) ++ "]" - else "_children[" ++ toString(i) ++ "], " ++ fetchChildren(i + 1, tail(ns)); -} - -function insertLocationAnnotation -String ::= ns::Decorated NamedSignature -{ - local pfx :: String = if null(ns.inputElements) then "" else ", "; - - return if null(ns.namedInputElements) then "" - else if length(ns.namedInputElements) > 1 then pfx ++ "multiple_annotation_problem" -- TODO - else if head(ns.namedInputElements).elementName != "core:location" then pfx ++ "unknown_annotation_type_problem" - else pfx ++ "common.Terminal.createSpan(_children, virtualLocation, (int)_pos.getPos())"; -} - - -function lookupStrings -[[a]] ::= t::[String] e::EnvTree -{ - return map(searchEnvTree(_, e), t); -} -function checkRHS -[String] ::= pn::String rhs::[Type] refs::[[Decorated SyntaxDcl]] -{ - return if null(rhs) then [] - else (if length(head(refs)) == 1 then - case head(head(refs)) of - | syntaxNonterminal(_,_,_,_,_) -> [] - | syntaxTerminal(_,_,_) -> [] - | _ -> ["parameter " ++ head(rhs).typeName ++ " of production " ++ pn ++ " is not syntax."] - end - else ["Terminal " ++ head(rhs).typeName ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from RHS of " ++ pn ++ ")"]) - ++ checkRHS(pn, tail(rhs), tail(refs)); -} - -{-- - - A lexer class. Copper doesn't take these, so we'll have to translate away - - the domlist/sublist that appear here. - -} -abstract production syntaxLexerClass -top::SyntaxDcl ::= n::String modifiers::SyntaxLexerClassModifiers -{ - top.fullName = n; - top.sortKey = "AAA" ++ n; - top.cstDcls := [pair(n, top)]; - top.cstErrors <- - if length(searchEnvTree(n, top.cstEnv)) == 1 then [] - else ["Name conflict with lexer class " ++ n]; - modifiers.className = n; - - -- TODO: these attributes are on all SyntaxDcls, but only have meaning for this production - -- that's UUUUGLY. - top.classDomContribs = modifiers.dominatesXML; - top.classSubContribs = modifiers.submitsXML; - - top.cstNormalize := [top]; - top.superClassContribs := modifiers.superClassContribs; - top.disambiguationClasses := modifiers.disambiguationClasses; - - production terms :: [String] = searchEnvTree(n, top.classTerminals); - local termsInit::String = - foldr( - \ term::String rest::String -> s"new common.ConsCell(Terminals.${makeCopperName(term)}.num(), ${rest})", - "common.ConsCell.nil", - terms); - top.lexerClassRefDcls := - s" protected common.ConsCell ${makeCopperName(n)} = ${termsInit};\n"; - - top.xmlCopper = - " \n"; -} - -{-- - - A parser attribute. The acode initializes it. - -} -abstract production syntaxParserAttribute -top::SyntaxDcl ::= n::String ty::Type acode::String -{ - top.fullName = n; - top.sortKey = "BBB" ++ n; - top.cstDcls := [pair(n, top)]; - top.cstErrors <- if length(searchEnvTree(n, top.cstEnv)) == 1 then [] - else ["Name conflict with parser attribute " ++ n]; - - top.cstNormalize := [top]; - - top.xmlCopper = - " \n" ++ - " \n" ++ - " \n" ++ - " \n"; - - -- TODO: technically, there should be no free variables in ty. - ty.boundVariables = []; -} - -{-- - - Additonal action code that should be added to the initialization of - - a parser attribute. - -} -abstract production syntaxParserAttributeAspect -top::SyntaxDcl ::= n::String acode::String -{ - top.fullName = n; - top.sortKey = "BBB" ++ n; - top.cstDcls := []; - top.cstErrors <- - if !null(searchEnvTree(n, top.cstEnv)) then [] - else ["Parser attribute " ++ n ++ " was referenced but this grammar was not included in this parser."]; - - top.cstNormalize := [top]; - - top.parserAttributeAspectContribs := [pair(n, acode)]; - top.xmlCopper = ""; -} - -{-- - - A disambiguation group. - - The acode distinguished between the listed terminals. - -} -abstract production syntaxDisambiguationGroup -top::SyntaxDcl ::= n::String terms::[String] applicableToSubsets::Boolean acode::String -{ - top.fullName = n; - top.sortKey = "DDD" ++ n; - top.cstDcls := []; - - local trefs::[[Decorated SyntaxDcl]] = lookupStrings(terms, top.cstEnv); - - -- this 'n' here appears to actually hold the line number of the - -- disambiguation, and the grammar. But we arent supposed to know this? - top.cstErrors <- flatMap(\p ::Pair -> - if !null(p.snd) then [] - else ["Terminal " ++ p.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from disambiguation group " ++ n ++ ")"], - zipWith(pair, terms, trefs)); - - top.cstNormalize := [top]; - - top.xmlCopper = - " \n" ++ - " " ++ implode("", map(xmlCopperRef, map(head, trefs))) ++ "\n" ++ - " \n" ++ - " \n"; -} - -function syntaxDclLte -Boolean ::= l::SyntaxDcl r::SyntaxDcl -{ - return l.sortKey <= r.sortKey; -{-- Sort key PREFIXES are as follows: - | syntaxLexerClass(_,_,_) -> AAA - | syntaxParserAttribute(_,_,_) -> BBB - | syntaxTerminal(_,_,_) -> CCC - | syntaxDisambiguationGroup(_,_,_) -> DDD - | syntaxNonterminal(_,_) -> EEE - | syntaxProduction(_,_,_,_) -> FFF --} -} - -function xmlCopperRef -String ::= d::Decorated SyntaxDcl -{ - return case d of - | syntaxLexerClass(n, _) -> "" - | syntaxTerminal(n, _, _) -> "" - | syntaxNonterminal(n, _, _, _, _) -> "" - | syntaxProduction(ns, _) -> "" - | syntaxDisambiguationGroup(n, _, _, _) -> "" - end; -} - diff --git a/grammars/silver/definition/concrete_syntax/ast/TerminalModifiers.sv b/grammars/silver/definition/concrete_syntax/ast/TerminalModifiers.sv deleted file mode 100644 index 9c3cf0335..000000000 --- a/grammars/silver/definition/concrete_syntax/ast/TerminalModifiers.sv +++ /dev/null @@ -1,231 +0,0 @@ -grammar silver:definition:concrete_syntax:ast; - -monoid attribute dominatesXML :: String with "", ++; -monoid attribute submitsXML :: String with "", ++; -monoid attribute lexerclassesXML :: String with "", ++; -monoid attribute ignored :: Boolean with false, ||; -monoid attribute marking :: Boolean with false, ||; -monoid attribute acode :: String with "", ++; -monoid attribute opPrecedence :: Maybe with nothing(), orElse; -monoid attribute opAssociation :: Maybe with nothing(), orElse; -- TODO type? -monoid attribute prefixSeperator :: Maybe with nothing(), orElse; -monoid attribute prefixSeperatorToApply :: Maybe with nothing(), orElse; -monoid attribute prettyName :: Maybe with nothing(), orElse; -autocopy attribute terminalName :: String; - -{-- - - Modifiers for terminals. - -} -nonterminal SyntaxTerminalModifiers with cstEnv, cstErrors, classTerminalContribs, superClasses, subClasses, dominatesXML, - submitsXML, ignored, acode, lexerclassesXML, opPrecedence, opAssociation, prefixSeperator, prefixSeperatorToApply, componentGrammarMarkingTerminals, - marking, terminalName, prettyName; - -propagate cstErrors, classTerminalContribs, dominatesXML, - submitsXML, ignored, acode, lexerclassesXML, opPrecedence, opAssociation, prefixSeperator, prefixSeperatorToApply, - marking, prettyName - on SyntaxTerminalModifiers; - -abstract production consTerminalMod -top::SyntaxTerminalModifiers ::= h::SyntaxTerminalModifier t::SyntaxTerminalModifiers -{ - top.cstErrors <- - if h.prefixSeperator.isJust && t.prefixSeperator.isJust - then ["Multiple prefix separators for terminal " ++ top.terminalName] - else []; -} - -abstract production nilTerminalMod -top::SyntaxTerminalModifiers ::= -{} - - - -{-- - - Modifiers for terminals. - -} -closed nonterminal SyntaxTerminalModifier with cstEnv, cstErrors, classTerminalContribs, superClasses, subClasses, dominatesXML, - submitsXML, ignored, acode, lexerclassesXML, opPrecedence, opAssociation, prefixSeperator, prefixSeperatorToApply, componentGrammarMarkingTerminals, - marking, terminalName, prettyName; - -{- We default ALL attributes, so we can focus only on those that are interesting in each case... -} -aspect default production -top::SyntaxTerminalModifier ::= -{ - -- Empty values as defaults - propagate cstErrors, classTerminalContribs, dominatesXML, - submitsXML, ignored, acode, lexerclassesXML, opPrecedence, opAssociation, prefixSeperator, prefixSeperatorToApply, - marking, prettyName; -} - -{-- - - If present, it's an ignore terminal, otherwise ordinary terminal. - - Copper has no notion of an ignore terminal, this is translated away. - -} -abstract production termIgnore -top::SyntaxTerminalModifier ::= -{ - top.ignored := true; -} -{-- - - If present, this is a Marking terminal. In the default translation, - - this does nothing. - -} -abstract production termMarking -top::SyntaxTerminalModifier ::= -{ - top.marking := true; -} -{-- - - The terminal's precedence. (Resolves shift/reduce conflicts) - -} -abstract production termPrecedence -top::SyntaxTerminalModifier ::= lvl::Integer -{ - top.opPrecedence := just(lvl); -} -{-- - - The terminal's association. Either left, right, or nonassoc. TODO: a type? - -} -abstract production termAssociation -top::SyntaxTerminalModifier ::= direction::String -{ - top.opAssociation := just(direction); -} -{-- - - The terminal's "pretty name". Used for error messages. - -} -abstract production termPrettyName -top::SyntaxTerminalModifier ::= prettyName::String -{ - top.prettyName := just(prettyName); -} -{-- - - The terminal's lexer classes. - -} -abstract production termClasses -top::SyntaxTerminalModifier ::= cls::[String] -{ - production allCls :: [String] = unionsBy(stringEq, cls :: lookupStrings(cls, top.superClasses)); - local allClsRefsL :: [[Decorated SyntaxDcl]] = lookupStrings(allCls, top.cstEnv); - production allClsRefs :: [Decorated SyntaxDcl] = - flatMap(\ sds::[Decorated SyntaxDcl] -> if null(sds) then [] else [head(sds)], allClsRefsL); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from lexer class on terminal " ++ top.terminalName ++ ")"], - zipWith(pair, allCls, allClsRefsL)); - top.classTerminalContribs := map(pair(_, top.terminalName), allCls); - -- We "translate away" lexer classes dom/sub, by moving that info to the terminals (here) - top.dominatesXML := implode("", map((.classDomContribs), allClsRefs)); - top.submitsXML := implode("", map((.classSubContribs), allClsRefs)); - top.lexerclassesXML := implode("", map(xmlCopperRef, allClsRefs)); - - local termSeps :: [Maybe] = map((.prefixSeperator), allClsRefs); - top.prefixSeperator := foldr(orElse, nothing(), termSeps); - top.cstErrors <- - if length(catMaybes(termSeps)) > 1 - then ["Multiple prefix separators for terminal " ++ top.terminalName] - else []; -} -{-- - - The submits list for the terminal. Either lexer classes or terminals. - -} -abstract production termSubmits -top::SyntaxTerminalModifier ::= sub::[String] -{ - production allSubs :: [String] = unionsBy(stringEq, sub :: lookupStrings(sub, top.subClasses)); - production subRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allSubs, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from submit clause on terminal " ++ top.terminalName ++ ")"], - zipWith(pair, sub, subRefs)); - top.submitsXML := implode("", map(xmlCopperRef, map(head, subRefs))); -} -{-- - - The dominates list for the terminal. Either lexer classes or terminals. - -} -abstract production termDominates -top::SyntaxTerminalModifier ::= dom::[String] -{ - production allDoms :: [String] = unionsBy(stringEq, dom :: lookupStrings(dom, top.subClasses)); - production domRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allDoms, top.cstEnv); - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal / Lexer Class " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from dominates clause on terminal " ++ top.terminalName ++ ")"], - zipWith(pair, dom, domRefs)); - top.dominatesXML := implode("", map(xmlCopperRef, map(head, domRefs))); -} -{-- - - The action to take whenever this terminal is SHIFTed. - -} -abstract production termAction -top::SyntaxTerminalModifier ::= acode::String -{ - top.acode := acode; -} -{-- - - The prefix separator to use for the terminal. - - Doesn't seem super useful, but support this on terminals too for consistency - -} -abstract production termPrefixSeperator -top::SyntaxTerminalModifier ::= sep::String -{ - top.prefixSeperator := just(sep); -} -{-- - - The terminals/grammars prefixed by this terminal, for which to use their separator. - -} -abstract production termUsePrefixSeperatorFor -top::SyntaxTerminalModifier ::= terms::[String] grams::[String] -{ - production allTerms :: [String] = terms ++ concat(concat(lookupStrings(grams, top.componentGrammarMarkingTerminals))); - - production termRefs :: [[Decorated SyntaxDcl]] = lookupStrings(allTerms, top.cstEnv); - top.prefixSeperatorToApply := - case termRefs of - | [] -> nothing() - | [ref] :: _ -> ref.prefixSeperator - | _ -> error("Lookup failure not caught during error checking") - end; - - top.cstErrors := flatMap(\ a::Pair -> - if !null(a.snd) then [] - else ["Terminal " ++ a.fst ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced from use prefix seperator for clause for terminal)"], - zipWith(pair, terms, termRefs)); - - top.cstErrors <- - flatMap( - \ s::Decorated SyntaxDcl -> - if !s.prefixSeperator.isJust - then ["Terminal " ++ s.fullName ++ " does not define a prefix separator, and must use an explicit terminal to define a prefix."] - else [], - map(head, termRefs)); - - {- TODO: This really should be some sort of warning, not an error, I think. - top.cstErrors <- - if null(allTerms) - then [top.terminalName ++ " does not prefix any terminals"] - else []; - -} - - local distinctSepTermRefs :: [Decorated SyntaxDcl] = - nubBy( - \ s1::Decorated SyntaxDcl s2::Decorated SyntaxDcl -> - case s1.prefixSeperator, s2.prefixSeperator of - | just(ps1), just(ps2) -> ps1 == ps2 - | _, _ -> false - end, - map(head, termRefs)); - top.cstErrors <- - if length(distinctSepTermRefs) > 1 - then ["Terminals " ++ implode(", ", map((.fullName), distinctSepTermRefs)) ++ - " have different prefix separators, so their prefixes must be specified seperately"] - else []; -} - diff --git a/grammars/silver/definition/core/AGDcl.sv b/grammars/silver/definition/core/AGDcl.sv deleted file mode 100644 index 2f971a0f3..000000000 --- a/grammars/silver/definition/core/AGDcl.sv +++ /dev/null @@ -1,99 +0,0 @@ -grammar silver:definition:core; - -{-- - - Top-level declarations of a Silver grammar. The "meat" of a file. - -} -nonterminal AGDcls with config, grammarName, env, location, unparse, errors, defs, occursDefs, moduleNames, compiledGrammars, grammarDependencies, jarName; -nonterminal AGDcl with config, grammarName, env, location, unparse, errors, defs, occursDefs, moduleNames, compiledGrammars, grammarDependencies, jarName; - -flowtype forward {grammarName, env} on AGDcls, AGDcl; - -propagate errors, moduleNames, jarName on AGDcls, AGDcl; -propagate defs, occursDefs on AGDcls; - -concrete production nilAGDcls -top::AGDcls ::= -{ - top.unparse = ""; -} - -concrete production consAGDcls -top::AGDcls ::= h::AGDcl t::AGDcls -{ - top.unparse = h.unparse ++ "\n" ++ t.unparse; - - top.errors <- warnIfMultJarName(h.jarName, t.jarName, top.location); -} - --------- --- AGDcl - -{-- - - A semantically meaningless declaration. Does nothing. - - Used for: (1) 'nil' counterpart to appendAgDcl - -} -abstract production emptyAGDcl -top::AGDcl ::= -{ - top.unparse = ""; -} - -abstract production errorAGDcl -top::AGDcl ::= e::[Message] -{ - top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; - top.errors <- e; -} - -abstract production defsAGDcl -top::AGDcl ::= d::[Def] -{ - top.unparse = s"{- Defs:\n${hackUnparse(d)} -}"; - top.defs := d; -} - -{-- - - Permits extensions to expand an AGDcl into a series of AGDcl's. - -} -abstract production appendAGDcl -top::AGDcl ::= h::AGDcl t::AGDcl -{ - top.unparse = h.unparse ++ "\n" ++ t.unparse; - propagate defs, occursDefs; - - top.errors <- warnIfMultJarName(h.jarName, t.jarName, top.location); -} - -function makeAppendAGDclOfAGDcls -AGDcl ::= dcls::AGDcls -{ - return case dcls of - | nilAGDcls(location=l) -> emptyAGDcl(location=l) - | consAGDcls(dcl, rest, location=l) -> appendAGDcl(dcl, makeAppendAGDclOfAGDcls(rest), location=l) - end; -} - -abstract production jarNameDcl -top::AGDcl ::= n::Name -{ - top.unparse = "jarName " ++ n.unparse; - top.jarName <- just(n.name); -} - -aspect default production -top::AGDcl ::= -{ - propagate moduleNames, defs, occursDefs, jarName; -} - -function warnIfMultJarName -[Message] ::= n1::Maybe n2::Maybe loc::Location -{ - return if n1.isJust && n2.isJust - then [wrn(loc, "Duplicate specification of jar name: " ++ - n1.fromJust ++ " and " ++ n2.fromJust)] - else []; -} - --- All AGDcls have their own file, or modification. None here. - diff --git a/grammars/silver/definition/core/Annotation.sv b/grammars/silver/definition/core/Annotation.sv deleted file mode 100644 index 1a45b4545..000000000 --- a/grammars/silver/definition/core/Annotation.sv +++ /dev/null @@ -1,29 +0,0 @@ -grammar silver:definition:core; - -terminal Annotation_kwd 'annotation' lexer classes {KEYWORD}; - -concrete production annotationDcl -top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.unparse = "annotation " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; - - production fName :: String = top.grammarName ++ ":" ++ a.name; - - top.defs := [annoDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "The name '" ++ fName ++ "' is already bound.")] - else []; - top.errors <- - if indexOf(":", a.name) == -1 then [] - else [err(a.location, "The name '" ++ a.name ++ "' must not be qualified.")]; - - top.errors <- tl.errorsTyVars; -} - - diff --git a/grammars/silver/definition/core/AspectDcl.sv b/grammars/silver/definition/core/AspectDcl.sv deleted file mode 100644 index 6d2d15106..000000000 --- a/grammars/silver/definition/core/AspectDcl.sv +++ /dev/null @@ -1,261 +0,0 @@ -grammar silver:definition:core; - -nonterminal AspectProductionSignature with config, grammarName, env, location, unparse, errors, defs, realSignature, namedSignature, signatureName; -nonterminal AspectProductionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement, realSignature; - -nonterminal AspectFunctionSignature with config, grammarName, env, location, unparse, errors, defs, realSignature, namedSignature, signatureName; -nonterminal AspectFunctionLHS with config, grammarName, env, location, unparse, errors, defs, realSignature, outputElement; - -nonterminal AspectRHS with config, grammarName, env, location, unparse, errors, defs, inputElements, realSignature; -nonterminal AspectRHSElem with config, grammarName, env, location, unparse, errors, defs, realSignature, inputElements, deterministicCount; - -flowtype forward {realSignature, env} on AspectProductionSignature, AspectProductionLHS, AspectFunctionSignature, AspectFunctionLHS, AspectRHS; -flowtype forward {deterministicCount, realSignature, env} on AspectRHSElem; - -{-- - - The signature elements from the fun/produciton being aspected. - -} -autocopy attribute realSignature :: [NamedSignatureElement]; - -propagate errors on AspectProductionSignature, AspectProductionLHS, AspectFunctionSignature, AspectFunctionLHS, AspectRHS, AspectRHSElem; - -concrete production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.unparse = "aspect production " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; - - top.defs := - if null(body.productionAttributes) then [] - else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; - - production namedSig :: NamedSignature = ns.namedSignature; - - production attribute realSig :: NamedSignature; - realSig = if id.lookupValue.found - then freshenNamedSignature(id.lookupValue.dcl.namedSignature) - else bogusNamedSignature(); - - -- Making sure we're aspecting a production is taken care of by type checking. - - top.errors <- id.lookupValue.errors; - - production attribute sigDefs :: [Def] with ++; - sigDefs := ns.defs; - - ns.signatureName = id.lookupValue.fullName; - ns.env = newScopeEnv(sigDefs, top.env); - ns.realSignature = if null(id.lookupValue.dcls) then [] else [realSig.outputElement] ++ realSig.inputElements; - - local attribute prodAtts :: [Def]; - prodAtts = if id.lookupValue.found - then defsFromPADcls(getProdAttrs(id.lookupValue.fullName, top.env), namedSig) - else []; - - body.env = newScopeEnv(body.defs ++ sigDefs, newScopeEnv(prodAtts, top.env)); - body.frame = aspectProductionContext(namedSig, myFlowGraph); -- graph from flow:env -} - -concrete production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.unparse = "aspect function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; - - top.defs := - if null(body.productionAttributes) then [] - else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; - - production namedSig :: NamedSignature = ns.namedSignature; - - production attribute realSig :: NamedSignature; - realSig = if id.lookupValue.found - then freshenNamedSignature(id.lookupValue.dcl.namedSignature) - else bogusNamedSignature(); - - -- Making sure we're aspecting a function is taken care of by type checking. - - top.errors <- id.lookupValue.errors; - - production attribute sigDefs :: [Def] with ++; - sigDefs := ns.defs; - - ns.signatureName = id.lookupValue.fullName; - ns.env = newScopeEnv(sigDefs, top.env); - ns.realSignature = if null(id.lookupValue.dcls) then [] else [realSig.outputElement] ++ realSig.inputElements; - - local attribute prodAtts :: [Def]; - prodAtts = if id.lookupValue.found - then defsFromPADcls(getProdAttrs(id.lookupValue.fullName, top.env), namedSig) - else []; - - body.env = newScopeEnv(body.defs ++ sigDefs, newScopeEnv(prodAtts, top.env)); - body.frame = aspectFunctionContext(namedSig, myFlowGraph); -- graph from flow:env -} - -concrete production aspectProductionSignature -top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS -{ - top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; - - propagate defs; - - top.namedSignature = namedSignature(top.signatureName, rhs.inputElements, lhs.outputElement, annotationsForNonterminal(lhs.outputElement.typerep, top.env)); - - lhs.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; - rhs.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); -} - -concrete production aspectProductionLHSNone -top::AspectProductionLHS ::= '_' -{ - top.unparse = "_"; - forwards to aspectProductionLHSId(name("p_top", top.location), location=top.location); -} - -concrete production aspectProductionLHSId -top::AspectProductionLHS ::= id::Name -{ - top.unparse = id.unparse; - - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - forwards to aspectProductionLHSFull(id, rType, location=top.location); -} - -concrete production aspectProductionLHSTyped -top::AspectProductionLHS ::= id::Name '::' t::TypeExpr -{ - top.unparse = id.unparse; - - top.errors <- t.errors; - - forwards to aspectProductionLHSFull(id, t.typerep, location=top.location); -} - -abstract production aspectProductionLHSFull -top::AspectProductionLHS ::= id::Name t::Type -{ - top.unparse = id.unparse ++ "::" ++ prettyType(t); - - production attribute fName :: String; - fName = if null(top.realSignature) then id.name else head(top.realSignature).elementName; - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - top.outputElement = namedSignatureElement(id.name, t); - - top.defs := [aliasedLhsDef(top.grammarName, id.location, fName, t, id.name)]; - - top.errors <- if length(getValueDclInScope(id.name, top.env)) > 1 - then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] - else []; -} - -concrete production aspectRHSElemNil -top::AspectRHS ::= -{ - top.unparse = ""; - - propagate defs; - top.inputElements = []; -} - -concrete production aspectRHSElemCons -top::AspectRHS ::= h::AspectRHSElem t::AspectRHS -{ - top.unparse = h.unparse ++ " " ++ t.unparse; - - propagate defs; - - top.inputElements = h.inputElements ++ t.inputElements; - - h.deterministicCount = length(t.inputElements); - h.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; - t.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); -} - -concrete production aspectRHSElemNone -top::AspectRHSElem ::= '_' -{ - top.unparse = "_"; - - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - forwards to aspectRHSElemFull( - name("p_" ++ toString(top.deterministicCount), $1.location), - rType, - location=top.location); -} - -concrete production aspectRHSElemId -top::AspectRHSElem ::= id::Name -{ - top.unparse = id.unparse; - - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - top.errors <- [wrn(top.location, "Giving just a name '" ++ id.name ++ "' is deprecated in aspect signature. Please explicitly use a name and type.")]; - - forwards to aspectRHSElemFull(id, rType, location=top.location); -} - -concrete production aspectRHSElemTyped -top::AspectRHSElem ::= id::Name '::' t::TypeExpr -{ - top.unparse = id.unparse ++ "::" ++ t.unparse; - - top.errors <- t.errors; - - forwards to aspectRHSElemFull(id, t.typerep, location=top.location); -} - -abstract production aspectRHSElemFull -top::AspectRHSElem ::= id::Name t::Type -{ - top.unparse = id.unparse ++ "::" ++ prettyType(t); - - production attribute fName :: String; - fName = if null(top.realSignature) then id.name else head(top.realSignature).elementName; - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - top.inputElements = [namedSignatureElement(id.name, t)]; - - top.defs := [aliasedChildDef(top.grammarName, id.location, fName, t, id.name)]; - - top.errors <- if length(getValueDclInScope(id.name, top.env)) > 1 - then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] - else []; -} - -concrete production aspectFunctionSignature -top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS -{ - top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; - - propagate defs; - - -- For the moment, functions do not have named parameters (hence, []) - top.namedSignature = namedSignature(top.signatureName, rhs.inputElements, lhs.outputElement, []); - - lhs.realSignature = if null(top.realSignature) then [] else [head(top.realSignature)]; - rhs.realSignature = if null(top.realSignature) then [] else tail(top.realSignature); -} - -concrete production functionLHSType -top::AspectFunctionLHS ::= t::TypeExpr -{ - top.unparse = t.unparse; - - production attribute fName :: String; - fName = if null(top.realSignature) then "_NULL_" else head(top.realSignature).elementName; - production attribute rType :: Type; - rType = if null(top.realSignature) then errorType() else head(top.realSignature).typerep; - - top.outputElement = namedSignatureElement(fName, t.typerep); - - -- TODO: this needs thinking. is it broken? maybe __return? or wait, it's doing that automatically isnt it... - top.defs := [aliasedLhsDef(top.grammarName, t.location, fName, t.typerep, fName)]; -} diff --git a/grammars/silver/definition/core/AttributeDcl.sv b/grammars/silver/definition/core/AttributeDcl.sv deleted file mode 100644 index 19bcf1346..000000000 --- a/grammars/silver/definition/core/AttributeDcl.sv +++ /dev/null @@ -1,46 +0,0 @@ -grammar silver:definition:core; - -concrete production attributeDclInh -top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.unparse = "inherited attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - top.defs := [inhDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- tl.errorsTyVars; -} - -concrete production attributeDclSyn -top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.unparse = "synthesized attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - top.defs := [synDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep)]; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- tl.errorsTyVars; -} - diff --git a/grammars/silver/definition/core/Attributes.sv b/grammars/silver/definition/core/Attributes.sv deleted file mode 100644 index 906c7931c..000000000 --- a/grammars/silver/definition/core/Attributes.sv +++ /dev/null @@ -1,12 +0,0 @@ -grammar silver:definition:core; - -{-- - - The grammar containing this tree. - -} -autocopy attribute grammarName :: String; - -{-- - - The name to use for the generated .jar if not overridden by -o command-line option. - -} -monoid attribute jarName :: Maybe with nothing(), orElse; - diff --git a/grammars/silver/definition/core/BlockContext.sv b/grammars/silver/definition/core/BlockContext.sv deleted file mode 100644 index 12127ef53..000000000 --- a/grammars/silver/definition/core/BlockContext.sv +++ /dev/null @@ -1,141 +0,0 @@ -grammar silver:definition:core; - -import silver:definition:flow:driver only ProductionGraph; - -{-- - - Permissions management information for certain features that can appear in production - - statements, etc. i.e. "can forward/return/pluck?" - -} -nonterminal BlockContext with permitReturn, permitForward, permitProductionAttributes, permitLocalAttributes, lazyApplication, hasFullSignature, hasPartialSignature, fullName, lhsNtName, signature, sourceGrammar, flowGraph; - - -{-- Are 'return' equations allowed in this context? -} -synthesized attribute permitReturn :: Boolean; -{-- Are 'forwards to' equations allowed in this context? -} -synthesized attribute permitForward :: Boolean; -{-- Are 'local' equations allowed in this context? -} -synthesized attribute permitLocalAttributes :: Boolean; -{-- Are 'production attribute' equations allowed in this context? - DISTINCT from locals, due to action blocks. -} -synthesized attribute permitProductionAttributes :: Boolean; - -{-- - - Whether the signature includes the name of a LHS. - - Not strictly necessary to set partial to true, but to be expected... - - REFACTORING NOTES: Used to: - - 1. Figure out how to get a production graph (REPLACE THIS) (i.e. function vs production) - - 2. Ignore checking LHS Inhs in functions, which don't have LHS inhs... - -} -synthesized attribute hasFullSignature :: Boolean; -{-- - - Whether the signature includes the type of a LHS & name/type pairs for RHS. - - And the name. e.g. top.frame.fullName - - REFACTORING NOTES: Used to: - - 1. Decide if syn eq should be exported by NT alone (default eq) or OCC/NT (normal syn eq) - - 2. Decide if we need to look at deps of syn eqs (i.e. default eqs don't get checked locally) - - 3. Decide to emit a default equation or synthesized equation - -} -synthesized attribute hasPartialSignature :: Boolean; -{-- - - Whether expressions should be evaluated lazily in this context. - - (False for action blocks, for example.) - -} -synthesized attribute lazyApplication :: Boolean; -{-- - - The full name of the LHS nonterminal. - - ONLY ACCESSIBLE IF `top.hasFullSignature` is true! - -} -synthesized attribute lhsNtName :: String; -{-- - - The signature of the current context. - - Not always sensible, depending on context. Needs care in use. - - TODO: figure out a way to guard accesses maybe? - -} -synthesized attribute signature :: NamedSignature; -{-- - - The flow graph for the current context. - -} -synthesized attribute flowGraph :: ProductionGraph; - -{- fullName on BlockContext: - - Used to: - - 1. Name locals. - - 2. Equations to emit flowDefs - - 3. Exprs to emit anon flowDefs. - - - - sourceGrammar on BlockContext: - - Used to: - - 1. Do isExportedBy checks, finding prod origin grammar. - -} - - -aspect default production -top::BlockContext ::= -{ - top.lhsNtName = error("LHS NT accessed for non-production"); - top.sourceGrammar = error("sourceGrammar accessed for non-production/function"); - -- most restrictive possible - top.permitReturn = false; - top.permitForward = false; - top.permitProductionAttributes = false; - top.permitLocalAttributes = false; - top.lazyApplication = true; - top.hasPartialSignature = false; - top.hasFullSignature = false; - - -- always required: fullName, signature, flowGraph -} - -abstract production functionContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.fullName = sig.fullName; - top.lhsNtName = "::nolhs"; -- unfortunately this is sometimes accessed, and a dummy value works okay - top.signature = sig; - top.sourceGrammar = substring(0, lastIndexOf(":", top.fullName), top.fullName); -- hack - top.flowGraph = g; - - top.permitReturn = true; - top.hasPartialSignature = true; - top.permitProductionAttributes = true; - top.permitLocalAttributes = true; -} - -abstract production productionContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.fullName = sig.fullName; - top.lhsNtName = sig.outputElement.typerep.typeName; - top.signature = sig; - top.sourceGrammar = substring(0, lastIndexOf(":", top.fullName), top.fullName); -- hack - top.flowGraph = g; - - top.permitForward = true; - top.hasPartialSignature = true; - top.hasFullSignature = true; - top.permitProductionAttributes = true; - top.permitLocalAttributes = true; -} - -abstract production aspectFunctionContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.permitReturn = false; - forwards to functionContext(sig, g); -} - -abstract production aspectProductionContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.permitForward = false; - forwards to productionContext(sig, g); -} - -abstract production globalExprContext -top::BlockContext ::= g::ProductionGraph -{ - top.fullName = "_NULL_"; -- maybe we should actually error? - top.signature = bogusNamedSignature(); -- TODO: do something about this? - top.flowGraph = g; -} - diff --git a/grammars/silver/definition/core/BuiltInFunctions.sv b/grammars/silver/definition/core/BuiltInFunctions.sv deleted file mode 100644 index 73244864e..000000000 --- a/grammars/silver/definition/core/BuiltInFunctions.sv +++ /dev/null @@ -1,186 +0,0 @@ -grammar silver:definition:core; - -concrete production lengthFunction -top::Expr ::= 'length' '(' e::Expr ')' -{ - top.unparse = "length(" ++ e.unparse ++ ")"; - - top.typerep = intType(); -- is this necessary? for flowtype reasons? - - forwards to performSubstitution(e.typerep, e.upSubst).lengthDispatcher(e, top.location); -} - -abstract production errorLength -top::Expr ::= e::Decorated Expr -{ - top.unparse = "length(" ++ e.unparse ++ ")"; - - top.typerep = intType(); - - local resolved :: Type = - performSubstitution(e.typerep, top.finalSubst); - - top.errors <- e.errors; - top.errors <- - if resolved.isError then [] -- suppress additional error message - else [err(e.location, "Operand to length is not compatible. It is of type " ++ prettyType(resolved))]; -} - -abstract production stringLength -top::Expr ::= e::Decorated Expr -{ - top.unparse = "length(" ++ e.unparse ++ ")"; - - top.typerep = intType(); - - top.errors <- e.errors; -} - -concrete production toIntegerFunction -top::Expr ::= 'toInteger' '(' e::Expr ')' -{ - top.unparse = "toInteger(" ++ e.unparse ++ ")"; - - top.typerep = intType(); -} - -concrete production toBooleanFunction -top::Expr ::= 'toBoolean' '(' e::Expr ')' -{ - top.unparse = "toBoolean(" ++ e.unparse ++ ")"; - - top.typerep = boolType(); -} - -concrete production toFloatFunction -top::Expr ::= 'toFloat' '(' e::Expr ')' -{ - top.unparse = "toFloat(" ++ e.unparse ++ ")"; - - top.typerep = floatType(); -} - -concrete production toStringFunction -top::Expr ::= 'toString' '(' e::Expr ')' -{ - top.unparse = "toString(" ++ e.unparse ++ ")"; - - top.typerep = stringType(); -} - -concrete production reifyFunctionLiteral -top::Expr ::= 'reify' -{ - top.unparse = "reify"; - - top.typerep = - functionType(nonterminalType("core:Either", [stringType(), varType(freshTyVar())]), [nonterminalType("core:reflect:AST", [])], []); -} - -concrete production newFunction -top::Expr ::= 'new' '(' e::Expr ')' -{ - top.unparse = "new(" ++ e.unparse ++ ")"; - - top.typerep = performSubstitution(e.typerep, top.upSubst).decoratedType; -} - -{-- - - The standard terminal constructor. This is *abstract* because 3-arg - - syntax is ambiguous without type-based disambiguation with a legacy syntax. - - See below in this file. - -} -abstract production terminalConstructor -top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' -{ - top.unparse = "terminal(" ++ t.unparse ++ ", " ++ es.unparse ++ ", " ++ el.unparse ++ ")"; - - top.typerep = t.typerep; -} - - --------------------------------------------------------------------------------- --- Deprecated variants of built-in functions - - -concrete production toIntFunction -top::Expr ::= 'toInt' '(' e::Expr ')' -{ - top.unparse = "toInt(" ++ e.unparse ++ ")"; - - -- TODO: Please uncomment this soon. I'm only leaving it because - -- Jenkins builds things with `--warn-error` as part of MWDA. - -- We really need to add a `--mwda` flag or something, so new warnings - -- can be introduced safely. - --top.errors <- [wrn($1.location, "'toInt' is deprecated syntax, please use 'toInteger' instead.")]; - - forwards to toIntegerFunction('toInteger', '(', e, ')', location=top.location); -} - -{-- - - Three-argument `terminal` is either: - - `terminal(type,lexeme,location)` or a legacy syntax of - - `terminal(type,lexeme,inherited_terminal)`. - - - - We should remove the "inherited terminal" variant, eventually. - -} -concrete production terminalConstructorTemporaryDispatcher -top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' -{ - top.unparse = "terminal(" ++ t.unparse ++ ", " ++ es.unparse ++ ", " ++ el.unparse ++ ")"; - - -- A hack, so we can get `typerep`. Should be always fine, since explicitly stated - -- types won't actually use `downSubst`. - el.downSubst = emptySubst(); - - forwards to - if el.typerep.isTerminal - then terminalFunctionInherited($1, $2, t, $4, es, $6, el, $8, location=top.location) - else terminalConstructor($1, $2, t, $4, es, $6, el, $8, location=top.location); -} - -concrete production terminalFunction -top::Expr ::= 'terminal' '(' t::TypeExpr ',' e::Expr ')' -{ - -- So, *maybe* this is deprecated? But let's not complain for now, because - -- it's too widely used. - - --top.errors <- [wrn(t.location, "terminal(type,lexeme) is deprecated. Use terminal(type,lexeme,location) instead.")]; - - local bogus :: Expr = - mkStrFunctionInvocation($6.location, "core:bogusLoc", []); - - forwards to terminalConstructor($1, $2, t, $4, e, ',', bogus, $6, location=top.location); -} - -concrete production terminalFunctionLineCol -top::Expr ::= 'terminal' '(' t::TypeExpr ',' e1::Expr ',' e2::Expr ',' e3::Expr ')' -{ - top.errors <- - [wrn(t.location, "terminal(type,lexeme,line,column) is deprecated. Use terminal(type,lexeme,location) instead.")]; - - local bogus :: Expr = - intConst(terminal(Int_t, "-1"), location=$10.location); - - forwards to terminalConstructor($1, $2, t, $4, e1, $6, - mkStrFunctionInvocation($10.location, "core:loc", [ - stringConst(terminal(String_t, "\"??\""), location=$10.location), - e2, e3, bogus, bogus, bogus, bogus - ]), $10, location=top.location); -} - -{-- - - Legacy syntax should be removed, eventually. - -} -abstract production terminalFunctionInherited -top::Expr ::= 'terminal' '(' t::TypeExpr ',' e1::Expr ',' e2::Expr ')' -{ - top.errors <- - [wrn(t.location, "terminal(type,lexeme,terminal) is deprecated. Please just add '.location' on the terminal to use terminal(type,lexeme,location)")]; - - local loc_access :: Expr = - access(e2, '.', qNameAttrOccur(qName($8.location, "location"), location=top.location), location=top.location); - - forwards to terminalConstructor($1, $2, t, $4, e1, $6, loc_access, $8, location=top.location); -} - diff --git a/grammars/silver/definition/core/DclInfo.sv b/grammars/silver/definition/core/DclInfo.sv deleted file mode 100644 index 5de08513c..000000000 --- a/grammars/silver/definition/core/DclInfo.sv +++ /dev/null @@ -1,145 +0,0 @@ -grammar silver:definition:core; - -import silver:definition:regex; -- soley for Terms. TODO : fix? -import silver:modification:copper only terminalIdReference; - -{-- - - The production a variable reference should forward to for this type of value - -} -synthesized attribute refDispatcher :: (Expr ::= Decorated QName Location) occurs on DclInfo; -{-- - - The production an "assignment" should forward to for this type of value - -} -synthesized attribute defDispatcher :: (ProductionStmt ::= Decorated QName Expr Location) occurs on DclInfo; -{-- - - The production an "equation" left hand side should forward to for this type of value (i.e. the 'x' in 'x.a = e') - -} -synthesized attribute defLHSDispatcher :: (DefLHS ::= Decorated QName Location) occurs on DclInfo; - -{-- - - The handler for 'x.a' for 'a', given that 'x' is DECORATED. - - @see accessDispather in TypeExp.sv, for the first step in that process... - - @see decoratedAccessHandler production for where this is used - -} -synthesized attribute decoratedAccessHandler :: (Expr ::= Decorated Expr Decorated QNameAttrOccur Location) occurs on DclInfo; -{-- - - The handler for 'x.a' for 'a', given that 'x' is UNdecorated. - - @see accessDispather in TypeExp.sv, for the first step in that process... - - @see undecoratedAccessHandler production for where this is used - -} -synthesized attribute undecoratedAccessHandler :: (Expr ::= Decorated Expr Decorated QNameAttrOccur Location) occurs on DclInfo; -{-- - - The production an "equation" should forward to for this type of attribute (i.e. the 'a' in 'x.a = e') - -} -synthesized attribute attrDefDispatcher :: (ProductionStmt ::= Decorated DefLHS Decorated QNameAttrOccur Expr Location) occurs on DclInfo; -{-- - - The production an "occurs on" decl should forward to for this type of attribute (for extension use, defaultAttributionDcl for all syn/inh/autocopy attrs.) - -} -synthesized attribute attributionDispatcher :: (AGDcl ::= Decorated QName BracketedOptTypeExprs QName BracketedOptTypeExprs Location) occurs on DclInfo; - -aspect default production -top::DclInfo ::= -{ - -- again, blank. - - -- all values must provide refDispatcher, defDispatcher, dehLHSDispatcher. - top.refDispatcher = error("Internal compiler error: must be defined for all value declarations"); - top.defDispatcher = error("Internal compiler error: must be defined for all value declarations"); - top.defLHSDispatcher = error("Internal compiler error: must be defined for all value declarations"); - -- all attributes must provide decoratedAccessHandler, attrDefDispatcher. - top.decoratedAccessHandler = error("Internal compiler error: must be defined for all attribute declarations"); - top.undecoratedAccessHandler = error("Internal compiler error: must be defined for all attribute declarations"); - top.attrDefDispatcher = error("Internal compiler error: must be defined for all attribute declarations"); - top.attributionDispatcher = error("Internal compiler error: must be defined for all attribute declarations"); -} - --- -- non-interface values -aspect production childDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.refDispatcher = childReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: we should be smarted about error messages, and mention its a child - top.defLHSDispatcher = childDefLHS(_, location=_); -} -aspect production lhsDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.refDispatcher = lhsReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: be smarter about the error message - top.defLHSDispatcher = lhsDefLHS(_, location=_); -} -aspect production localDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.refDispatcher = localReference(_, location=_); - top.defDispatcher = localValueDef(_, _, location=_); - top.defLHSDispatcher = localDefLHS(_, location=_); -} - - --- -- interface values -aspect production prodDcl -top::DclInfo ::= sg::String sl::Location ns::NamedSignature hasForward::Boolean -{ - top.refDispatcher = productionReference(_, location=_); - -- Note that we still need production references, even though bug #16 removes the production type. - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} -aspect production funDcl -top::DclInfo ::= sg::String sl::Location ns::NamedSignature -{ - top.refDispatcher = functionReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} -aspect production globalValueDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.refDispatcher = globalValueReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} - --- -- interface Attributes -aspect production synDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); - top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); -} -aspect production inhDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: above should probably be an error handler! access inh from undecorated? - top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); -} -aspect production annoDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.decoratedAccessHandler = accessBounceUndecorate(annoAccessHandler(_, _, location=_), _, _, _); - top.undecoratedAccessHandler = annoAccessHandler(_, _, location=_); - top.attrDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, "Annotations are not defined as equations within productions")], dl, attr, e, location=l); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); -} - --- -- interface Production attr (values) -aspect production forwardDcl -top::DclInfo ::= sg::String sl::Location ty::Type -{ - top.refDispatcher = forwardReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: better error message - top.defLHSDispatcher = forwardDefLHS(_, location=_); -} - -aspect production termDcl -top::DclInfo ::= sg::String sl::Location fn::String regex::Regex -{ - top.refDispatcher = terminalIdReference(_, location=_); -} diff --git a/grammars/silver/definition/core/Expr.sv b/grammars/silver/definition/core/Expr.sv deleted file mode 100644 index 5d8805879..000000000 --- a/grammars/silver/definition/core/Expr.sv +++ /dev/null @@ -1,1080 +0,0 @@ -grammar silver:definition:core; - ---import silver:analysis:typechecking:core; - -nonterminal Expr with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, typerep; -nonterminal Exprs with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, exprs, rawExprs; - -nonterminal ExprInhs with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, decoratingnt, suppliedInhs; -nonterminal ExprInh with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, decoratingnt, suppliedInhs; -nonterminal ExprLHSExpr with - config, grammarName, env, location, unparse, errors, name, typerep, decoratingnt, suppliedInhs; - -propagate errors on Expr, Exprs, ExprInhs, ExprInh, ExprLHSExpr; - -{-- - - The nonterminal being decorated. (Used for 'decorate with {}') - -} -autocopy attribute decoratingnt :: Type; -{-- - - The inherited attributes being supplied in a decorate expression - -} -synthesized attribute suppliedInhs :: [String]; -{-- - - A list of decorated expressions from an Exprs. - -} -monoid attribute exprs :: [Decorated Expr] with [], ++; -{-- - - Get each individual Expr, without decorating them. - -} -monoid attribute rawExprs :: [Expr] with [], ++; - - -abstract production errorExpr -top::Expr ::= e::[Message] -{ - top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; - top.errors <- e; - top.typerep = errorType(); -} - -concrete production nestedExpr -top::Expr ::= '(' e::Expr ')' -{ - top.unparse = "(" ++ e.unparse ++ ")"; - - forwards to e; -} - -concrete production baseExpr -top::Expr ::= q::QName -{ - top.unparse = q.unparse; - - forwards to if null(q.lookupValue.dcls) - then errorReference(q.lookupValue.errors, q, location=top.location) - else q.lookupValue.dcl.refDispatcher(q, top.location); -} - -abstract production errorReference -top::Expr ::= msg::[Message] q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors <- msg; - top.typerep = errorType(); -} - --- TODO: We should separate this out, even, to be "nonterminal/decorable" and "as-is" -abstract production childReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.typerep = if q.lookupValue.typerep.isDecorable - then ntOrDecType(q.lookupValue.typerep, freshType()) - else q.lookupValue.typerep; -} - -abstract production lhsReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - -- An LHS is *always* a decorable (nonterminal) type. - top.typerep = ntOrDecType(q.lookupValue.typerep, freshType()); -} - -abstract production localReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.typerep = if q.lookupValue.typerep.isDecorable - then ntOrDecType(q.lookupValue.typerep, freshType()) - else q.lookupValue.typerep; -} - -abstract production forwardReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - -- An LHS (and thus, forward) is *always* a decorable (nonterminal) type. - top.typerep = ntOrDecType(q.lookupValue.typerep, freshType()); -} - --- Note here that production and function *references* are distinguished. --- Later on, we do *not* distinguish for application. - -abstract production productionReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - -- TODO: the freshening should probably be the responsibility of the thing in the environment, not here? - top.typerep = freshenCompletely(q.lookupValue.typerep); -} - -abstract production functionReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.typerep = freshenCompletely(q.lookupValue.typerep); -- TODO see above -} - -abstract production globalValueReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.typerep = freshenCompletely(q.lookupValue.typerep); -- TODO see above -} - -concrete production concreteForwardExpr -top::Expr ::= q::'forward' -{ - top.unparse = "forward"; - - -- TODO: we're forwarding to baseExpr just to decorate the tree we create. - -- That's a bit weird. - forwards to baseExpr(qName(q.location, "forward"), location=top.location); -} - -concrete production application -top::Expr ::= e::Expr '(' es::AppExprs ',' anns::AnnoAppExprs ')' -{ - -- TODO: fix comma when one or the other is empty - top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; - - -- TODO: You know, since the rule is we can't access .typerep without "first" supplying - -- .downSubst, perhaps we should just... report .typerep after substitution in the first place! - forwards to performSubstitution(e.typerep, e.upSubst).applicationDispatcher(e, es, anns, top.location); -} - -concrete production applicationAnno -top::Expr ::= e::Expr '(' anns::AnnoAppExprs ')' -{ - forwards to application(e, $2, emptyAppExprs(location=$2.location), ',', anns, $4, location=top.location); -} -concrete production applicationExpr -top::Expr ::= e::Expr '(' es::AppExprs ')' -{ - forwards to application(e, $2, es, ',', emptyAnnoAppExprs(location=$4.location), $4, location=top.location); -} -concrete production applicationEmpty -top::Expr ::= e::Expr '(' ')' -{ - forwards to application(e, $2, emptyAppExprs(location=$2.location), ',', emptyAnnoAppExprs(location=$3.location), $3, location=top.location); -} - -abstract production errorApplication -top::Expr ::= e::Decorated Expr es::AppExprs anns::AnnoAppExprs -{ - top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; - - top.errors <- e.errors; - top.errors <- - if e.typerep.isError then [] else - [err(top.location, e.unparse ++ " has type " ++ prettyType(performSubstitution(e.typerep, e.upSubst)) ++ - " and cannot be invoked as a function.")]; - -- TODO This error message is cumbersomely generated... - - top.typerep = errorType(); - - es.appExprTypereps = []; - es.appExprApplied = e.unparse; - anns.appExprApplied = e.unparse; - anns.remainingFuncAnnotations = []; - anns.funcAnnotations = []; -} - --- Note that this applies to both function and productions. --- We don't distinguish anymore at this point. A production reference --- becomes a function, effectively. -abstract production functionApplication -top::Expr ::= e::Decorated Expr es::AppExprs anns::AnnoAppExprs -{ - top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; - - -- NOTE: REVERSED ORDER - -- We may need to resolve e's type to get at the actual 'function type' - local t :: Type = performSubstitution(e.typerep, e.upSubst); - es.appExprTypereps = reverse(t.inputTypes); - es.appExprApplied = e.unparse; - anns.appExprApplied = e.unparse; - anns.remainingFuncAnnotations = t.namedTypes; - anns.funcAnnotations = anns.remainingFuncAnnotations; - - -- TODO: we have an ambiguity here in the longer term. - -- How to distinguish between - -- foo(x) where there is an annotation 'a'? - -- Is this partial application, give (Foo ::= ;a::Something) or (Foo) + error. - -- Possibly this can be solved by having somehting like "foo(x,a=?)" - forwards to if es.isPartial || anns.isPartial - then partialApplication(e, es, anns, location=top.location) - else functionInvocation(e, es, anns, location=top.location); -} - -abstract production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; - - top.errors <- e.errors ++ es.errors ++ anns.errors; - - local ety :: Type = performSubstitution(e.typerep, e.upSubst); - - top.typerep = ety.outputType; -} - -abstract production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.unparse = e.unparse ++ "(" ++ es.unparse ++ "," ++ anns.unparse ++ ")"; - - top.errors <- e.errors ++ es.errors ++ anns.errors; - - local ety :: Type = performSubstitution(e.typerep, e.upSubst); - - top.typerep = functionType(ety.outputType, es.missingTypereps ++ anns.partialAnnoTypereps, anns.missingAnnotations); -} - -concrete production attributeSection -top::Expr ::= '(' '.' q::QName ')' -{ - top.unparse = "(." ++ q.unparse ++ ")"; - - -- Fresh variable for the input type, and we'll come back later and check that it occurs on that type. - - -- Also, freshen the attribute type, because even though there currently should NOT be any type variables - -- there, there could be if the code will raise an error. - local rawInputType :: Type = freshType(); - top.typerep = functionType(freshenCompletely(q.lookupAttribute.typerep), [rawInputType], []); - - top.errors <- q.lookupAttribute.errors; - - top.errors <- if null(q.lookupAttribute.dclBoundVars) then [] - else [err(q.location, "Attribute " ++ q.name ++ " is parameterized, and attribute sections currently do not work with parameterized attributes, yet.")]; -- TODO The type inference system is too weak, currently. - - top.errors <- if !q.lookupAttribute.found || q.lookupAttribute.dcl.isSynthesized then [] - else [err(q.location, "Only synthesized attributes are currently supported in attribute sections.")]; - - -- Only known after the inference pass (uses final subst) - production attribute inputType :: Type; - inputType = performSubstitution(rawInputType, top.finalSubst); - - -- We're not using QNameAttrOccur right now because that assumes we know the nonterminal - -- so we can filter down to just attributes occurring on that. We could probably fix this - -- to make it work either way. - production attribute occursCheck :: OccursCheck; - occursCheck = occursCheckQName(q, if inputType.isDecorated then inputType.decoratedType else inputType); - - top.errors <- occursCheck.errors; -} - --- NOTE: this is not intended to be used normally. --- Its purpose is for test cases. Essentially all other situations should never care what the forward tree is. -concrete production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - top.unparse = e.unparse ++ ".forward"; - top.typerep = e.typerep; -} - -concrete production access -top::Expr ::= e::Expr '.' q::QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - -- We don't include 'q' here because this might be a terminal, where - -- 'q' shouldn't actually resolve to a name! - top.errors := e.errors ++ forward.errors; - - local eTy::Type = performSubstitution(e.typerep, e.upSubst); - q.attrFor = if eTy.isDecorated then eTy.decoratedType else eTy; - - -- Note: we're first consulting the TYPE of the LHS. - forwards to eTy.accessHandler(e, q, top.location); - -- This jumps to: - -- errorAccessHandler (e.g. 1.unparse) - -- undecoratedAccessHandler - -- decoratedAccessHandler (see that production, for how normal attribute access proceeds!) - -- terminalAccessHandler -} - -abstract production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.typerep = errorType(); - top.errors <- q.errors; - - top.errors <- - if e.typerep.isError then [] else - let ref :: String = - if length(e.unparse) < 12 then "'" ++ e.unparse ++ "' has" else "LHS of '.' is" - in [err(top.location, ref ++ " type " ++ prettyType(q.attrFor) ++ " and cannot have attributes.")] - end; -} - -abstract production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - production index :: Integer = - findNamedSigElem(q.name, annotationsForNonterminal(q.attrFor, top.env), 0); - - top.typerep = q.typerep; - - top.errors <- q.errors; -} - -abstract production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - -- NO use of q.errors, as that become nonsensical here. - - top.errors <- - if q.name == "lexeme" || q.name == "location" || - q.name == "filename" || q.name == "line" || q.name == "column" - then [] - else [err(q.location, q.name ++ " is not a terminal attribute")]; - - top.typerep = - if q.name == "lexeme" || q.name == "filename" - then stringType() - else if q.name == "line" || q.name == "column" - then intType() - else if q.name == "location" - then nonterminalType("core:Location", []) - else errorType(); -} - -abstract production undecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.errors := q.errors ++ forward.errors; -- so that these errors appear first. - - -- TODO: We should consider disambiguating based on what dcls *actually* - -- occur on the LHS here. - - -- Note: LHS is UNdecorated, here we dispatch based on the kind of attribute. - forwards to if !q.found then errorDecoratedAccessHandler(e, q, location=top.location) - else q.attrDcl.undecoratedAccessHandler(e, q, top.location); - -- annoAccessHandler - -- accessBouncer -} - -{-- - - Accessing an attribute occasionally demands manipulating the left-hand side. - - This production is intended to permit that. - -} -abstract production accessBouncer -top::Expr ::= target::(Expr ::= Decorated Expr Decorated QNameAttrOccur Location) e::Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - -- Basically the only purpose here is to decorate 'e'. - forwards to target(e, q, top.location); -} -function accessBounceDecorate -Expr ::= target::(Expr ::= Decorated Expr Decorated QNameAttrOccur Location) e::Decorated Expr q::Decorated QNameAttrOccur l::Location -{ - return accessBouncer(target, decorateExprWithEmpty('decorate', exprRef(e, location=l), 'with', '{', '}', location=l), q, location=l); -} -function accessBounceUndecorate -Expr ::= target::(Expr ::= Decorated Expr Decorated QNameAttrOccur Location) e::Decorated Expr q::Decorated QNameAttrOccur l::Location -{ - return accessBouncer(target, newFunction('new', '(', exprRef(e, location=l), ')', location=l), q, location=l); -} - -abstract production decoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.errors := q.errors ++ forward.errors; -- so that these errors appear first. - - -- TODO: We should consider disambiguating based on what dcls *actually* - -- occur on the LHS here. - - -- Note: LHS is decorated, here we dispatch based on the kind of attribute. - forwards to if !q.found then errorDecoratedAccessHandler(e, q, location=top.location) - else q.attrDcl.decoratedAccessHandler(e, q, top.location); - -- From here we go to: - -- synDecoratedAccessHandler - -- inhDecoratedAccessHandler - -- errorDecoratedAccessHandler -- unknown attribute error raised already. -} - -abstract production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.typerep = q.typerep; -} - -abstract production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.typerep = q.typerep; -} - --- TODO: change name. really "unknownDclAccessHandler" -abstract production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.unparse = e.unparse ++ "." ++ q.unparse; - - top.typerep = errorType(); -} - - -concrete production decorateExprWithEmpty -top::Expr ::= 'decorate' e::Expr 'with' '{' '}' -{ - top.unparse = "decorate " ++ e.unparse ++ " with {}"; - - forwards to decorateExprWith($1, e, $3, $4, exprInhsEmpty(location=top.location), $5, location=top.location); -} - -concrete production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - top.unparse = "decorate " ++ e.unparse ++ " with {" ++ inh.unparse ++ "}"; - - top.typerep = decoratedType(performSubstitution(e.typerep, e.upSubst)); -- .decoratedForm? - - inh.decoratingnt = performSubstitution(e.typerep, e.upSubst); -} - -abstract production exprInhsEmpty -top::ExprInhs ::= -{ - top.unparse = ""; - - top.suppliedInhs = []; -} - -concrete production exprInhsOne -top::ExprInhs ::= lhs::ExprInh -{ - top.unparse = lhs.unparse; - - top.suppliedInhs = lhs.suppliedInhs; -} - -concrete production exprInhsCons -top::ExprInhs ::= lhs::ExprInh inh::ExprInhs -{ - top.unparse = lhs.unparse ++ " " ++ inh.unparse; - - top.suppliedInhs = lhs.suppliedInhs ++ inh.suppliedInhs; -} - -concrete production exprInh -top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' -{ - top.unparse = lhs.unparse ++ " = " ++ e.unparse ++ ";"; - - top.suppliedInhs = lhs.suppliedInhs; -} - -concrete production exprLhsExpr -top::ExprLHSExpr ::= q::QNameAttrOccur -{ - top.name = q.name; - top.unparse = q.unparse; - - top.typerep = q.typerep; - top.suppliedInhs = [q.dcl.attrOccurring]; - - q.attrFor = top.decoratingnt; -} - -concrete production trueConst -top::Expr ::= 'true' -{ - top.unparse = "true"; - - top.typerep = boolType(); -} - -concrete production falseConst -top::Expr ::= 'false' -{ - top.unparse = "false"; - - top.typerep = boolType(); -} - -concrete production and -top::Expr ::= e1::Expr '&&' e2::Expr -{ - top.unparse = e1.unparse ++ " && " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production or -top::Expr ::= e1::Expr '||' e2::Expr -{ - top.unparse = e1.unparse ++ " || " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production not -top::Expr ::= '!' e::Expr -{ - top.unparse = "! " ++ e.unparse; - - top.typerep = boolType(); -} - -concrete production gt -top::Expr ::= e1::Expr '>' e2::Expr -{ - top.unparse = e1.unparse ++ " > " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production lt -top::Expr ::= e1::Expr '<' e2::Expr -{ - top.unparse = e1.unparse ++ " < " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production gteq -top::Expr ::= e1::Expr '>=' e2::Expr -{ - top.unparse = e1.unparse ++ " >= " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production lteq -top::Expr ::= e1::Expr '<=' e2::Expr -{ - top.unparse = e1.unparse ++ " <= " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production eqeq -top::Expr ::= e1::Expr '==' e2::Expr -{ - top.unparse = e1.unparse ++ " == " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production neq -top::Expr ::= e1::Expr '!=' e2::Expr -{ - top.unparse = e1.unparse ++ " != " ++ e2.unparse; - - top.typerep = boolType(); -} - -concrete production ifThenElse -top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr -precedence = 0 -{ - top.unparse = "if " ++ e1.unparse ++ " then " ++ e2.unparse ++ " else " ++ e3.unparse; - - top.typerep = e2.typerep; -} - -concrete production intConst -top::Expr ::= i::Int_t -{ - top.unparse = i.lexeme; - - top.typerep = intType(); -} - -concrete production floatConst -top::Expr ::= f::Float_t -{ - top.unparse = f.lexeme; - - top.typerep = floatType(); -} - -concrete production plus -top::Expr ::= e1::Expr '+' e2::Expr -{ - top.unparse = e1.unparse ++ " + " ++ e2.unparse; - - top.typerep = e1.typerep; -} - -concrete production minus -top::Expr ::= e1::Expr '-' e2::Expr -{ - top.unparse = e1.unparse ++ " - " ++ e2.unparse; - - top.typerep = e1.typerep; -} - -concrete production multiply -top::Expr ::= e1::Expr '*' e2::Expr -{ - top.unparse = e1.unparse ++ " * " ++ e2.unparse; - - top.typerep = e1.typerep; -} - -concrete production divide -top::Expr ::= e1::Expr '/' e2::Expr -{ - top.unparse = e1.unparse ++ " / " ++ e2.unparse; - - top.typerep = e1.typerep; -} - -concrete production modulus -top::Expr ::= e1::Expr '%' e2::Expr -{ - top.unparse = e1.unparse ++ " % " ++ e2.unparse; - - top.typerep = e1.typerep; -} - -concrete production neg -top::Expr ::= '-' e::Expr -precedence = 13 -{ - top.unparse = "- " ++ e.unparse; - - top.typerep = e.typerep; -} - -concrete production stringConst -top::Expr ::= s::String_t -{ - top.unparse = s.lexeme; - - top.typerep = stringType(); -} - -concrete production plusPlus -top::Expr ::= e1::Expr '++' e2::Expr -{ - top.unparse = e1.unparse ++ " ++ " ++ e2.unparse; - - propagate errors; - top.errors <- forward.errors; - top.typerep = if errCheck1.typeerror then errorType() else result_type; - - local result_type :: Type = performSubstitution(e1.typerep, errCheck1.upSubst); - - -- Moved from 'analysis:typechecking' because we want to use this stuff here now - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e1.downSubst = top.downSubst; - e2.downSubst = e1.upSubst; - errCheck1.downSubst = e2.upSubst; - forward.downSubst = errCheck1.upSubst; - -- upSubst defined via forward :D - - errCheck1 = check(e1.typerep, e2.typerep); - - forwards to - -- if the types disagree, forward to an error production instead. - if errCheck1.typeerror - then errorExpr([err(top.location, "Operands to ++ must be the same concatenable type. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)], location=top.location) - else top.typerep.appendDispatcher(e1, e2, top.location); -} - -abstract production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.unparse = e1.unparse ++ " ++ " ++ e2.unparse; - - top.typerep = stringType(); -} - -abstract production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.unparse = e1.unparse ++ " ++ " ++ e2.unparse; - - local result_type :: Type = performSubstitution(e1.typerep, top.downSubst); - - top.errors <- - if result_type.isError then [] - else [err(e1.location, prettyType(result_type) ++ " is not a concatenable type.")]; - top.typerep = errorType(); -} - --- These sorta seem obsolete, but there are some important differences from AppExprs. --- For one, AppExprs expects a fixed, imposed list of types. Here we're flexible! --- This is used by both pattern matching and list literals. -abstract production exprsEmpty -top::Exprs ::= -{ - top.unparse = ""; - - top.exprs := []; - top.rawExprs := []; -} -concrete production exprsSingle -top::Exprs ::= e::Expr -{ - top.unparse = e.unparse; - - top.exprs := [e]; - top.rawExprs := [e]; -} -concrete production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ - top.unparse = e1.unparse ++ ", " ++ e2.unparse; - - top.exprs := [e1] ++ e2.exprs; - top.rawExprs := [e1] ++ e2.rawExprs; -} - - -{-- - - Exprs with optional underscores omitting parameters. Used exclusively for - - (partial) function application. - -} -nonterminal AppExprs with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, exprs, rawExprs, - isPartial, missingTypereps, appExprIndicies, appExprSize, appExprTypereps, appExprApplied; - -nonterminal AppExpr with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, exprs, rawExprs, - isPartial, missingTypereps, appExprIndicies, appExprIndex, appExprTyperep, appExprApplied; - -propagate errors on AppExprs, AppExpr; -propagate exprs, rawExprs on AppExprs; - -synthesized attribute isPartial :: Boolean; -synthesized attribute missingTypereps :: [Type]; -synthesized attribute appExprIndicies :: [Integer]; -synthesized attribute appExprSize :: Integer; -inherited attribute appExprIndex :: Integer; -inherited attribute appExprTypereps :: [Type]; -inherited attribute appExprTyperep :: Type; -autocopy attribute appExprApplied :: String; - --- These are the "new" Exprs syntax. This allows missing (_) arguments, to indicate partial application. -concrete production missingAppExpr -top::AppExpr ::= '_' -{ - top.unparse = "_"; - - top.isPartial = true; - top.missingTypereps = [top.appExprTyperep]; - - top.rawExprs := []; - top.exprs := []; - top.appExprIndicies = []; -} -concrete production presentAppExpr -top::AppExpr ::= e::Expr -{ - top.unparse = e.unparse; - - top.isPartial = false; - top.missingTypereps = []; - - top.rawExprs := [e]; - top.exprs := [e]; - top.appExprIndicies = [top.appExprIndex]; -} - -concrete production snocAppExprs -top::AppExprs ::= es::AppExprs ',' e::AppExpr -{ - top.unparse = es.unparse ++ ", " ++ e.unparse; - - top.isPartial = es.isPartial || e.isPartial; - top.missingTypereps = es.missingTypereps ++ e.missingTypereps; - - top.appExprIndicies = es.appExprIndicies ++ e.appExprIndicies; - - top.appExprSize = es.appExprSize + 1; - - e.appExprIndex = es.appExprSize; - e.appExprTyperep = if null(top.appExprTypereps) - then errorType() - else head(top.appExprTypereps); - - es.appExprTypereps = if null(top.appExprTypereps) then [] else tail(top.appExprTypereps); -} -concrete production oneAppExprs -top::AppExprs ::= e::AppExpr -{ - top.unparse = e.unparse; - - top.isPartial = e.isPartial; - top.missingTypereps = e.missingTypereps; - - top.appExprIndicies = e.appExprIndicies; - - top.errors <- if null(top.appExprTypereps) - then [err(top.location, "Too many arguments provided to function '" ++ top.appExprApplied ++ "'")] - else if length(top.appExprTypereps) > 1 - then [err(top.location, "Too few arguments provided to function '" ++ top.appExprApplied ++ "'")] - else []; - top.appExprSize = 1; - - e.appExprIndex = 0; - e.appExprTyperep = if null(top.appExprTypereps) - then errorType() - else head(top.appExprTypereps); -} -abstract production emptyAppExprs -top::AppExprs ::= -{ - top.unparse = ""; - - top.isPartial = false; - top.missingTypereps = []; - - top.appExprIndicies = []; - top.appExprSize = 0; - - -- Assumption: We only get here when we're looking at () - -- i.e. we can't ever have 'too many' provided error - top.errors <- if null(top.appExprTypereps) then [] - else [err(top.location, "Too few arguments provided to function '" ++ top.appExprApplied ++ "'")]; -} - - -nonterminal AnnoAppExprs with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, - isPartial, appExprApplied, exprs, - remainingFuncAnnotations, funcAnnotations, - missingAnnotations, partialAnnoTypereps, annoIndexConverted, annoIndexSupplied; -nonterminal AnnoExpr with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, - isPartial, appExprApplied, exprs, - remainingFuncAnnotations, funcAnnotations, - missingAnnotations, partialAnnoTypereps, annoIndexConverted, annoIndexSupplied; - -propagate errors, exprs on AnnoAppExprs, AnnoExpr; - -{-- - - Annotations that have not yet been supplied - -} -inherited attribute remainingFuncAnnotations :: [NamedArgType]; -{-- - - All annotations of this function - -} -autocopy attribute funcAnnotations :: [NamedArgType]; -{-- - - Annotations that have not been supplied (by subtracting from remainingFuncAnnotations) - -} -synthesized attribute missingAnnotations :: [NamedArgType]; -{-- - - Typereps of those annotations that are partial (_) - -} -synthesized attribute partialAnnoTypereps :: [Type]; - -synthesized attribute annoIndexConverted :: [Integer]; -synthesized attribute annoIndexSupplied :: [Integer]; - -concrete production annoExpr -top::AnnoExpr ::= qn::QName '=' e::AppExpr -{ - top.unparse = qn.unparse ++ "=" ++ e.unparse; - - local fq :: Pair [NamedArgType]> = - extractNamedArg(qn.name, top.remainingFuncAnnotations); - - e.appExprIndex = - findNamedArgType(qn.name, top.funcAnnotations, 0); - e.appExprTyperep = - if fq.fst.isJust then fq.fst.fromJust.argType else errorType(); - - top.missingAnnotations = fq.snd; -- minus qn, if it was there - top.partialAnnoTypereps = e.missingTypereps; - - top.errors <- - if fq.fst.isJust then [] - else [err(qn.location, "Named parameter '" ++ qn.name ++ "' is not appropriate for '" ++ top.appExprApplied ++ "'")]; - top.isPartial = e.isPartial; - top.annoIndexConverted = - if e.isPartial then [e.appExprIndex] else []; - top.annoIndexSupplied = - if e.isPartial then [] else [e.appExprIndex]; -} - -concrete production snocAnnoAppExprs -top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr -{ - top.unparse = es.unparse ++ ", " ++ e.unparse; - - top.isPartial = es.isPartial || e.isPartial; - - e.remainingFuncAnnotations = top.remainingFuncAnnotations; - es.remainingFuncAnnotations = e.missingAnnotations; - top.missingAnnotations = es.missingAnnotations; - - top.partialAnnoTypereps = es.partialAnnoTypereps ++ e.partialAnnoTypereps; - top.annoIndexConverted = es.annoIndexConverted ++ e.annoIndexConverted; - top.annoIndexSupplied = es.annoIndexSupplied ++ e.annoIndexSupplied; -} - -concrete production oneAnnoAppExprs -top::AnnoAppExprs ::= e::AnnoExpr -{ - top.unparse = e.unparse; - - top.isPartial = e.isPartial; - top.errors <- - if null(top.missingAnnotations) then [] - else [err(top.location, "Missing named parameters for function '" ++ top.appExprApplied ++ "': " - ++ implode(", ", map((.argName), top.missingAnnotations)))]; - - top.errors <- e.errors; - - e.remainingFuncAnnotations = top.remainingFuncAnnotations; - top.missingAnnotations = e.missingAnnotations; - - top.partialAnnoTypereps = e.partialAnnoTypereps; - top.annoIndexConverted = e.annoIndexConverted; - top.annoIndexSupplied = e.annoIndexSupplied; -} - -abstract production emptyAnnoAppExprs -top::AnnoAppExprs ::= -{ - top.unparse = ""; - - top.isPartial = false; - top.errors <- - if null(top.missingAnnotations) then [] - else [err(top.location, "Missing named parameters for function '" ++ top.appExprApplied ++ "': " - ++ implode(", ", map((.argName), top.missingAnnotations)))]; - - top.missingAnnotations = top.remainingFuncAnnotations; - - top.partialAnnoTypereps = []; - top.annoIndexConverted = []; - top.annoIndexSupplied = []; -} - -function reorderedAnnoAppExprs -[Decorated Expr] ::= d::Decorated AnnoAppExprs -{ - -- This is an annoyingly poor quality implementation - return map(reorderedGetSnd, sortBy(reorderedLte, zipWith(pair, d.annoIndexSupplied, d.exprs))); -} -function reorderedGetSnd -b ::= p::Pair { return p.snd; } -function reorderedLte -Boolean ::= l::Pair r::Pair { return l.fst <= r.fst; } - - - - - - -{-- - - Utility for other modules to create function invocations. - - This makes no assumptions, use it any way you wish! - -} -function mkStrFunctionInvocation -Expr ::= l::Location e::String es::[Expr] -{ - return mkFullFunctionInvocation(l, baseExpr(qName(l, e), location=l), es, []); -} -function mkFunctionInvocation -Expr ::= l::Location e::Expr es::[Expr] -{ - return mkFullFunctionInvocation(l, e, es, []); -} -function mkFullFunctionInvocation -Expr ::= l::Location e::Expr es::[Expr] ans::[Pair] -{ - return application(e, '(', - foldl(snocAppExprs(_, ',', _, location=l), emptyAppExprs(location=l), - map(presentAppExpr(_, location=l), es)), - ',', - foldl(snocAnnoAppExprs(_, ',', _, location=l), emptyAnnoAppExprs(location=l), - map(mkAnnoExpr, ans)), - ')', location=l); -} --- Internal helper function -function mkAnnoExpr -AnnoExpr ::= p::Pair -{ - return annoExpr(qName(p.snd.location, p.fst), '=', presentAppExpr(p.snd, location=p.snd.location), location=p.snd.location); -} - -{-- - - Utility for other modules to create function invocations. - - - - Major assumption: The expressions are already decorated, and the - - typing substitution threaded through them will then be fed through - - the expr created by this function. - - - - The purpose of this vs just mkFunctionInvocationDecorated - - is to avoid exponential growth from forwarding. Type checking - - an expr, then forwarding to a function call that again type - - checks that expr well... just nest those and boom. - -} -function mkFunctionInvocationDecorated -Expr ::= l::Location e::Expr es::[Decorated Expr] -{ - return mkFunctionInvocation(l, e, map(exprRef(_, location=l), es)); -} -function mkStrFunctionInvocationDecorated -Expr ::= l::Location e::String es::[Decorated Expr] -{ - return mkFunctionInvocation(l, baseExpr(qName(l, e), location=l), map(exprRef(_, location=l), es)); -} - -{-- - - We allow references to existing subexpressions to appear arbitrarily in trees. - - - - There is one MAJOR restriction on the use of this production: - - The referenced expression (e) MUST APPEAR in the same expression tree - - as it is referenced in. - - - - This is for type information reasons: the subtree referenced must have been - - typechecked in the same 'typing context' as wherever this tree appears. - - - - This is trivially satisfied for the typical use case for this production, - - where you're typechecking your children, then forwarding to some tree with - - references to those children. - -} -abstract production exprRef -top::Expr ::= e::Decorated Expr -{ - top.unparse = e.unparse; - - -- See the major restriction. This should have been checked for error already! - top.typerep = e.typerep; - - -- TODO: one of the little things we might want is to make this transparent to - -- forwarding. e.g. e might be a 'childReference' and pattern matching would - -- need to separately account for this! - -- To accomplish this, we might want some notion of a decorated forward. -} - diff --git a/grammars/silver/definition/core/FunctionDcl.sv b/grammars/silver/definition/core/FunctionDcl.sv deleted file mode 100644 index e628adc87..000000000 --- a/grammars/silver/definition/core/FunctionDcl.sv +++ /dev/null @@ -1,69 +0,0 @@ -grammar silver:definition:core; - -nonterminal FunctionSignature with config, grammarName, env, location, unparse, errors, defs, namedSignature, signatureName; -nonterminal FunctionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement; - -propagate errors on FunctionSignature, FunctionLHS; - -concrete production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.unparse = "function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - production namedSig :: NamedSignature = ns.namedSignature; - - top.defs := funDef(top.grammarName, id.location, namedSig) :: - if null(body.productionAttributes) then [] - else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; - - top.errors <- - if length(getValueDclAll(fName, top.env)) > 1 - then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- - if null(body.uniqueSignificantExpression) - then [err(top.location, "Function '" ++ id.name ++ "' does not have a return value.")] - else if length(body.uniqueSignificantExpression) > 1 - then [err(top.location, "Function '" ++ id.name ++ "' has more than one declared return value.")] - else []; - - production attribute sigDefs :: [Def] with ++; - sigDefs := ns.defs; - - ns.signatureName = fName; - ns.env = newScopeEnv(sigDefs, top.env); - - local attribute prodAtts :: [Def]; - prodAtts = defsFromPADcls(getProdAttrs(fName, top.env), namedSig); - - body.env = newScopeEnv(body.defs ++ sigDefs, newScopeEnv(prodAtts, top.env)); - body.frame = functionContext(namedSig, myFlowGraph); -- graph from flow:env -} - -concrete production functionSignature -top::FunctionSignature ::= lhs::FunctionLHS '::=' rhs::ProductionRHS -{ - top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; - - propagate defs; - - -- For the moment, functions do not have named parameters (hence, []) - top.namedSignature = namedSignature(top.signatureName, rhs.inputElements, lhs.outputElement, []); -} - -concrete production functionLHS -top::FunctionLHS ::= t::TypeExpr -{ - top.unparse = t.unparse; - - production attribute fName :: String; - fName = "__func__lhs"; - - top.outputElement = namedSignatureElement(fName, t.typerep); - - -- TODO: think about this. lhs doesn't really have an fName. - top.defs := [lhsDef(top.grammarName, t.location, fName, t.typerep)]; -} - diff --git a/grammars/silver/definition/core/GlobalDcl.sv b/grammars/silver/definition/core/GlobalDcl.sv deleted file mode 100644 index fc1758b64..000000000 --- a/grammars/silver/definition/core/GlobalDcl.sv +++ /dev/null @@ -1,28 +0,0 @@ -grammar silver:definition:core; - -import silver:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; - -concrete production globalValueDclConcrete -top::AGDcl ::= 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - top.unparse = "global " ++ id.unparse ++ " :: " ++ t.unparse ++ " = " ++ e.unparse ++ "\n"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ id.name; - - top.defs := [globalDef(top.grammarName, id.location, fName, t.typerep)]; - - top.errors <- - if length(getValueDclAll(fName, top.env)) > 1 - then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] - else []; - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local myFlowGraph :: ProductionGraph = - constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); - - e.frame = globalExprContext(myFlowGraph); -} diff --git a/grammars/silver/definition/core/Name.sv b/grammars/silver/definition/core/Name.sv deleted file mode 100644 index 1700daee4..000000000 --- a/grammars/silver/definition/core/Name.sv +++ /dev/null @@ -1,28 +0,0 @@ -grammar silver:definition:core; - -nonterminal Name with config, grammarName, location, unparse, name; - -{-- - - An identifier's (possibly qualified) name. - -} -synthesized attribute name :: String; - -concrete production nameIdLower -top::Name ::= id::IdLower_t -{ - top.name = id.lexeme; - top.unparse = id.lexeme; -} -concrete production nameIdUpper -top::Name ::= id::IdUpper_t -{ - top.name = id.lexeme; - top.unparse = id.lexeme; -} - -function name -Name ::= n::String l::Location -{ - return nameIdLower(terminal(IdLower_t, n, l), location=l); -} - diff --git a/grammars/silver/definition/core/NonTerminalDcl.sv b/grammars/silver/definition/core/NonTerminalDcl.sv deleted file mode 100644 index 82169300c..000000000 --- a/grammars/silver/definition/core/NonTerminalDcl.sv +++ /dev/null @@ -1,83 +0,0 @@ -grammar silver:definition:core; - -autocopy attribute nonterminalName :: String; - -concrete production nonterminalDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - top.unparse = "nonterminal " ++ id.unparse ++ tl.unparse ++ " " ++ nm.unparse ++ ";"; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - nm.nonterminalName = fName; - - -- tl.freeVariables is our order list of the bound types for this nonterminal. - top.defs := [cl.whichDcl(top.grammarName, id.location, fName, tl.freeVariables, nonterminalType(fName, tl.types))]; - -- TODO: It's probably reasonable to skip listing - -- tl.freeVariables, and the Type. Assuming we have a proper ntDcl. - -- And we should consider recording the exact concrete names used... might be nice documentation to use - - - -- Here we ensure that the type list contains only type *variables* - top.errors <- tl.errorsTyVars; - - -- Here we bind those type variables. - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - - -- Redefinition check of the name - top.errors <- - if length(getTypeDclAll(fName, top.env)) > 1 - then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- - if isLower(substring(0,1,id.name)) - then [err(id.location, "Types must be capitalized. Invalid nonterminal name " ++ id.name)] - else []; -} - --- This feels a bit hackish. -nonterminal ClosedOrNot with location, whichDcl; - -synthesized attribute whichDcl :: (Def ::= String Location String [TyVar] Type); - -concrete production openNt -top::ClosedOrNot ::= -{ - top.whichDcl = ntDef; -} - -concrete production closedNt -top::ClosedOrNot ::= 'closed' -{ - top.whichDcl = closedNtDef; -} - -nonterminal NonterminalModifiers with config, location, unparse, errors, env, nonterminalName; -- 0 or some -nonterminal NonterminalModifierList with config, location, unparse, errors, env, nonterminalName; -- 1 or more -closed nonterminal NonterminalModifier with config, location, unparse, errors, env, nonterminalName; -- 1 - -propagate errors on NonterminalModifiers, NonterminalModifierList, NonterminalModifier; - -concrete production nonterminalModifiersNone -top::NonterminalModifiers ::= -{ - top.unparse = ""; -} -concrete production nonterminalModifierSome -top::NonterminalModifiers ::= nm::NonterminalModifierList -{ - top.unparse = nm.unparse; -} - -concrete production nonterminalModifierSingle -top::NonterminalModifierList ::= nm::NonterminalModifier -{ - top.unparse = nm.unparse; -} -concrete production nonterminalModifiersCons -top::NonterminalModifierList ::= h::NonterminalModifier ',' t::NonterminalModifierList -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; -} - diff --git a/grammars/silver/definition/core/OccursDcl.sv b/grammars/silver/definition/core/OccursDcl.sv deleted file mode 100644 index 63674813a..000000000 --- a/grammars/silver/definition/core/OccursDcl.sv +++ /dev/null @@ -1,154 +0,0 @@ -grammar silver:definition:core; - -abstract production defaultAttributionDcl -top::AGDcl ::= at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; - - -- TODO: this location is highly unreliable. - - -- We must unconditionally emit the occurs def in order to signal to the - -- environment mechanism that we're in a different namespace than - -- the types/attributes. - top.occursDefs := [ - (if !at.lookupAttribute.dcl.isAnnotation then occursDcl else annoInstanceDcl)( - top.grammarName, at.location, - nt.lookupType.fullName, at.lookupAttribute.fullName, - protontty, protoatty)]; - - -- binding errors in looking up these names. - top.errors <- nt.lookupType.errors ++ - -- The nonterminal type list is strictly type VARIABLES only - nttl.errorsTyVars; - - nttl.initialEnv = top.env; - attl.env = nttl.envBindingTyVars; - nttl.env = nttl.envBindingTyVars; - - -- Make sure we get the number of tyvars correct for the NT - top.errors <- - if length(nt.lookupType.dclBoundVars) != length(nttl.types) - then [err(nt.location, nt.name ++ " expects " ++ toString(length(nt.lookupType.dclBoundVars)) ++ - " type variables, but " ++ toString(length(nttl.types)) ++ " were provided.")] - else []; - - -- Make sure we get the number of tyvars correct for the ATTR - top.errors <- - if length(at.lookupAttribute.dclBoundVars) != length(attl.types) - then [err(at.location, at.name ++ " expects " ++ toString(length(at.lookupAttribute.dclBoundVars)) ++ - " type variables, but " ++ toString(length(attl.types)) ++ " were provided.")] - else []; - - -- We have 4 types. - -- 1: A type, from env, for the nonterminal - -- 2: A type, from syntax, for the nonterminal - -- 3: A type, from env, for the attribute - -- 4: A type, from syntax, for the attribute. - - -- Our goal is to be able to take a (unknown) nonterminal type - -- and yield the appropriate attribute type it corresponds to. - - -- To that end, we want two things: - -- 1: A type that we can unify with some nonterminal type. - -- 2: A type that, under than unification, will be the resulting attribute type. - - -- So we generate three substitutions: - -- 1: Rewrite the tyvars of type #1 to the types of type #2. - -- 2: Rewrite the tyvars of type #3 to the types of type #4. - -- 3: Rewrite our local tyvars to fresh variables. - - -- Thus, we apply this to type #2 and #4, and get our goal. - - -- This is perfectly correct, but it can probably be simplified with some invariants - -- on what appears in the environment. - - -- This renames the vars from the environment - local rewrite_from :: Substitution = - composeSubst( - -- nt's env types -> local skolem types (vars -> vars) - zipVarsIntoSubstitution(nt.lookupType.dclBoundVars, nttl.freeVariables), - -- at's env types -> local skolem types (vars -> types) - zipVarsAndTypesIntoSubstitution(at.lookupAttribute.dclBoundVars, attl.types)); - - local rewrite_to :: Substitution = - zipVarsIntoSubstitution(nttl.freeVariables, freshTyVars(length(nttl.freeVariables))); - - -- These have to be two separate renamings, because the second renaming replaces names getting substituted in by the first renaming. - production attribute protontty :: Type; - production attribute protoatty :: Type; - protontty = performRenaming(performRenaming(nt.lookupType.typerep, rewrite_from), rewrite_to); - protoatty = performRenaming(performRenaming(at.lookupAttribute.typerep, rewrite_from), rewrite_to); - - -- Now, finally, make sure we're not "redefining" the occurs. - production attribute occursCheck :: [DclInfo]; - occursCheck = getOccursDcl(at.lookupAttribute.fullName, nt.lookupType.fullName, top.env); - - top.errors <- - if length(occursCheck) > 1 - then [err(at.location, "Attribute '" ++ at.name ++ "' already occurs on '" ++ nt.name ++ "'.")] - else []; - - top.errors <- - if !nt.lookupType.typerep.isDecorable - then [err(nt.location, nt.name ++ " is not a nonterminal. Attributes can only occur on nonterminals.")] - else []; - - top.errors <- - if !nt.lookupType.found || !at.lookupAttribute.found || !at.lookupAttribute.dcl.isAnnotation || - isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars) then [] - else [err(top.location, "Annotations for a nonterminal must be in a module exported by the nonterminal's declaring grammar.")]; -} - -abstract production errorAttributionDcl -top::AGDcl ::= msg::[Message] at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; - top.occursDefs := []; - top.errors <- msg; - - nttl.initialEnv = top.env; - attl.env = nttl.envBindingTyVars; - nttl.env = nttl.envBindingTyVars; - - -- Decorate everything else to still check for errors - top.errors <- - -- binding errors in looking up these names. - nt.lookupType.errors ++ - -- The nonterminal type list is strictly type VARIABLES only - nttl.errorsTyVars; - - -- Make sure we get the number of tyvars correct for the NT - top.errors <- - if length(nt.lookupType.dclBoundVars) != length(nttl.types) - then [err(nt.location, nt.name ++ " expects " ++ toString(length(nt.lookupType.dclBoundVars)) ++ - " type variables, but " ++ toString(length(nttl.types)) ++ " were provided.")] - else []; -} - -concrete production attributionDcl -top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; - - -- Workaround for circular dependency due to dispatching on env: - -- Nothing used to build the env namespaces on which we dispatch can depend on - -- the forward here. - -- Attribution (occurs) defs, which obviously must depend on this forward, are - -- specified by a seperate occursDefs attribute. - -- Attribution dispatch productions should only define occursDefs (i.e. no new - -- nonterminals, productions, attributes, etc.) - top.defs := []; - top.moduleNames := []; - - forwards to - (if !at.lookupAttribute.found - then errorAttributionDcl(at.lookupAttribute.errors, _, _, _, _, location=_) - else at.lookupAttribute.dcl.attributionDispatcher)(at, attl, nt, nttl, top.location); -} - -concrete production annotateDcl -top::AGDcl ::= 'annotation' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - forwards to attributionDcl('attribute', at, attl, $4, $5, nt, nttl, $8, location=top.location); -} - diff --git a/grammars/silver/definition/core/ProductionBody.sv b/grammars/silver/definition/core/ProductionBody.sv deleted file mode 100644 index 0426593a7..000000000 --- a/grammars/silver/definition/core/ProductionBody.sv +++ /dev/null @@ -1,395 +0,0 @@ -grammar silver:definition:core; - -nonterminal ProductionBody with - config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, - productionAttributes, uniqueSignificantExpression; -nonterminal ProductionStmts with - config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, - productionAttributes, uniqueSignificantExpression; -nonterminal ProductionStmt with - config, grammarName, env, location, unparse, errors, defs, frame, compiledGrammars, - productionAttributes, uniqueSignificantExpression; - -flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, downSubst} - on ProductionBody, ProductionStmts; -flowtype decorate {frame, grammarName, compiledGrammars, config, env, flowEnv, downSubst, finalSubst} - on ProductionStmt; - -nonterminal DefLHS with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, name, typerep, defLHSattr, found; - -nonterminal ForwardInhs with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars; -nonterminal ForwardInh with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars; -nonterminal ForwardLHSExpr with - config, grammarName, env, location, unparse, errors, frame, name, typerep; - -{-- - - Context for ProductionStmt blocks. (Indicates function, production, aspect, etc) - - Includes singature for those contexts with a signature. - -} -autocopy attribute frame :: BlockContext; - -{-- - - Defs of attributes that should be wrapped up as production attributes. - -} -monoid attribute productionAttributes :: [Def] with [], ++; -{-- - - Either the 'forward' expression, or the 'return' expression. - - I gave it an obtuse name so it could easily be renamed in the future. - -} -monoid attribute uniqueSignificantExpression :: [Decorated Expr] with [], ++; - -{-- - - The attribute we're defining on a DefLHS. - -} -inherited attribute defLHSattr :: Decorated QNameAttrOccur; - -propagate errors on ProductionBody, ProductionStmts, ProductionStmt, DefLHS, ForwardInhs, ForwardInh, ForwardLHSExpr; -propagate defs, productionAttributes, uniqueSignificantExpression on ProductionBody, ProductionStmts; - -concrete production productionBody -top::ProductionBody ::= '{' stmts::ProductionStmts '}' -{ - top.unparse = stmts.unparse; -} - -concrete production productionStmtsNil -top::ProductionStmts ::= -{ - top.unparse = ""; -} - -concrete production productionStmtsSnoc -top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt -{ - top.unparse = h.unparse ++ "\n" ++ t.unparse; -} - ----------- - -abstract production productionStmtAppend -top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt -{ - top.unparse = h.unparse ++ "\n" ++ t.unparse; - propagate defs, productionAttributes, uniqueSignificantExpression; -} - -abstract production errorProductionStmt -top::ProductionStmt ::= e::[Message] -{ - top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; - top.errors <- e; -} - --------------------------------------------------------------------------------- - -aspect default production -top::ProductionStmt ::= -{ - -- as is usual for defaults ("base classes") - -- can't provide unparse or location, errors should NOT be defined! - top.productionAttributes := []; - top.uniqueSignificantExpression := []; - - top.defs := []; -} - -concrete production returnDef -top::ProductionStmt ::= 'return' e::Expr ';' -{ - top.unparse = "\treturn " ++ e.unparse ++ ";"; - - top.uniqueSignificantExpression := [e]; - - top.errors <- if !top.frame.permitReturn - then [err(top.location, "Return is not valid in this context. (They are only permitted in function declarations.)")] - else []; -} - -concrete production localAttributeDcl -top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' -{ - top.unparse = "\tlocal attribute " ++ a.unparse ++ "::" ++ te.unparse ++ ";"; - - production attribute fName :: String; - fName = top.frame.fullName ++ ":local:" ++ a.name; - - top.defs := [localDef(top.grammarName, a.location, fName, te.typerep)]; - - top.errors <- - if length(getValueDclAll(fName, top.env)) > 1 - then [err(a.location, "Value '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- if !top.frame.permitLocalAttributes - then [err(top.location, "Local attributes are not valid in this context.")] - else []; -} - -concrete production productionAttributeDcl -top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr ';' -{ - -- TODO: we should unparse the production keyword, not local here!! - --top.unparse = "\tproduction attribute " ++ a.unparse ++ "::" ++ te.unparse ++ ";"; - - top.productionAttributes := forward.defs; - top.defs := []; - - top.errors <- if !top.frame.permitProductionAttributes - then [err(top.location, "Production attributes are not valid in this context.")] - else []; - - forwards to localAttributeDcl('local', $2, a, $4, te, $6, location=top.location); -} - -concrete production forwardsTo -top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' -{ - top.unparse = "\tforwards to " ++ e.unparse; - - top.productionAttributes := [forwardDef(top.grammarName, top.location, top.frame.signature.outputElement.typerep)]; - top.uniqueSignificantExpression := [e]; - - top.errors <- if !top.frame.permitForward - then [err(top.location, "Forwarding is not permitted in this context. (Only permitted in non-aspect productions.)")] - else []; -} - -concrete production forwardsToWith -top::ProductionStmt ::= 'forwards' 'to' e::Expr 'with' '{' inh::ForwardInhs '}' ';' -{ - top.unparse = "\tforwards to " ++ e.unparse ++ " with {" ++ inh.unparse ++ "};"; - - forwards to productionStmtAppend( - forwardsTo($1, $2, $3, $8, location=top.location), - forwardingWith('forwarding', $4, $5, inh, $7, $8, location=top.location), - location=top.location); -} - -concrete production forwardingWith -top::ProductionStmt ::= 'forwarding' 'with' '{' inh::ForwardInhs '}' ';' -{ - top.unparse = "\tforwarding with {" ++ inh.unparse ++ "};"; - - production attribute fwdDcls :: [DclInfo]; - fwdDcls = getValueDcl("forward", top.env); - - top.errors <- if null(fwdDcls) - then [err(top.location, "'forwarding with' clause for a production that does not forward!")] - else []; -} - --- TODO eliminate these (/ combine with the ones for decorate expression) -concrete production forwardInh -top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' -{ - top.unparse = lhs.unparse ++ " = " ++ e.unparse ++ ";"; -} - -concrete production forwardInhsOne -top::ForwardInhs ::= lhs::ForwardInh -{ - top.unparse = lhs.unparse; -} - -concrete production forwardInhsCons -top::ForwardInhs ::= lhs::ForwardInh rhs::ForwardInhs -{ - top.unparse = lhs.unparse ++ " " ++ rhs.unparse; -} - -concrete production forwardLhsExpr -top::ForwardLHSExpr ::= q::QNameAttrOccur -{ - top.name = q.name; - top.unparse = q.unparse; - - top.typerep = q.typerep; - - q.attrFor = top.frame.signature.outputElement.typerep; -} - -concrete production attributeDef -top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur '=' e::Expr ';' -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; - - -- defs must stay here explicitly, because we dispatch on types in the forward here! - top.productionAttributes := []; - top.defs := []; - - dl.defLHSattr = attr; - attr.attrFor = dl.typerep; - - local problems :: [Message] = - if attr.found && attr.attrDcl.isAnnotation - then [err(attr.location, attr.name ++ " is an annotation, which are supplied to productions as arguments, not defined as equations.")] - else dl.errors ++ attr.errors; - - forwards to - -- oddly enough we may have no errors and need to forward to error production: - -- consider "production foo top::DoesNotExist ::= { top.errors = ...; }" - -- where top is a valid reference to a type that is an error type - -- so there is an error elsewhere - if !dl.found || !attr.found || !null(problems) - then errorAttributeDef(problems, dl, attr, e, location=top.location) - else attr.attrDcl.attrDefDispatcher(dl, attr, e, top.location); -} - -{- This is a helper that exist primarily to decorate 'e' and add its error messages to the list. - Invariant: msg should not be null! -} -abstract production errorAttributeDef -top::ProductionStmt ::= msg::[Message] dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; - - forwards to errorProductionStmt(msg ++ e.errors, location=top.location); -} - -abstract production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; -} - -abstract production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " = " ++ e.unparse ++ ";"; -} - -concrete production concreteDefLHS -top::DefLHS ::= q::QName -{ - top.name = q.name; - top.unparse = q.unparse; - - top.errors := q.lookupValue.errors ++ forward.errors; - - forwards to if null(q.lookupValue.dcls) - then errorDefLHS(q, location=top.location) - else q.lookupValue.dcl.defLHSDispatcher(q, top.location); -} - -abstract production errorDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = false; - - top.errors <- - if top.typerep.isError then [] else [err(q.location, "Cannot define attributes on " ++ q.name)]; - top.typerep = q.lookupValue.typerep; -} - -concrete production concreteDefLHSfwd -top::DefLHS ::= q::'forward' -{ - forwards to concreteDefLHS(qName(q.location, "forward"), location=top.location); -} - -abstract production childDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; - - local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; - - top.errors <- - if existingProblems || top.found then [] - else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on child '" ++ q.name ++ "'")]; - - top.typerep = q.lookupValue.typerep; -} - -abstract production lhsDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = !existingProblems && top.defLHSattr.attrDcl.isSynthesized; - - local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; - - top.errors <- - if existingProblems || top.found then [] - else [err(q.location, "Cannot define inherited attribute '" ++ top.defLHSattr.name ++ "' on the lhs '" ++ q.name ++ "'")]; - - top.typerep = q.lookupValue.typerep; -} - -abstract production localDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; - - local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; - - top.errors <- - if existingProblems || top.found then [] - else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on local '" ++ q.name ++ "'")]; - - top.typerep = q.lookupValue.typerep; -} - -abstract production forwardDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = !existingProblems && top.defLHSattr.attrDcl.isInherited; - - local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; - - top.errors <- - if existingProblems || top.found then [] - else [err(q.location, "Cannot define synthesized attribute '" ++ top.defLHSattr.name ++ "' on forward")]; - - top.typerep = q.lookupValue.typerep; -} - ------ done with DefLHS - -concrete production valueEq -top::ProductionStmt ::= val::QName '=' e::Expr ';' -{ - top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; - - top.errors <- val.lookupValue.errors; - - -- defs must stay here explicitly, because we dispatch on types in the forward here! - top.productionAttributes := []; - top.defs := []; - - forwards to if null(val.lookupValue.dcls) - then errorValueDef(val, e, location=top.location) - else val.lookupValue.dcl.defDispatcher(val, e, top.location); -} - -abstract production errorValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; - - top.errors <- - if val.lookupValue.typerep.isError then [] - else [err(val.location, val.name ++ " cannot be assigned to.")]; -} - -abstract production localValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.unparse = "\t" ++ val.unparse ++ " = " ++ e.unparse ++ ";"; - - -- val is already valid here - - -- TODO: missing redefinition check -} - diff --git a/grammars/silver/definition/core/ProductionDcl.sv b/grammars/silver/definition/core/ProductionDcl.sv deleted file mode 100644 index 93110ee7f..000000000 --- a/grammars/silver/definition/core/ProductionDcl.sv +++ /dev/null @@ -1,129 +0,0 @@ -grammar silver:definition:core; - -nonterminal ProductionSignature with config, grammarName, env, location, unparse, errors, defs, namedSignature, signatureName; -nonterminal ProductionLHS with config, grammarName, env, location, unparse, errors, defs, outputElement; -nonterminal ProductionRHS with config, grammarName, env, location, unparse, errors, defs, inputElements; -nonterminal ProductionRHSElem with config, grammarName, env, location, unparse, errors, defs, inputElements, deterministicCount; - -flowtype forward {env} on ProductionSignature, ProductionLHS, ProductionRHS; -flowtype forward {deterministicCount, env} on ProductionRHSElem; - -propagate errors on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; -propagate defs on ProductionSignature, ProductionRHS; - -{-- - - Used to help give names to children, when names are omitted. - -} -inherited attribute deterministicCount :: Integer; - -{-- - - Given to signature syntax, so as to construct a named signature representation. - -} -inherited attribute signatureName :: String; - -concrete production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - top.unparse = "abstract production " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - production namedSig :: NamedSignature = ns.namedSignature; - - top.defs := prodDef(top.grammarName, id.location, namedSig, length(body.uniqueSignificantExpression) > 0) :: - if null(body.productionAttributes) then [] - else [prodOccursDef(top.grammarName, id.location, namedSig, body.productionAttributes)]; - - top.errors <- - if length(getValueDclAll(fName, top.env)) > 1 - then [err(id.location, "Value '" ++ fName ++ "' is already bound.")] - - -- TODO: Narrow this down to just a list of productions of the same nonterminal before deciding to error. - else if length(getValueDclAll(id.name, top.env)) > 1 - then [err(top.location, "Production " ++ id.name ++ " shares a name with another production from an imported grammar. Either this production is meant to be an aspect, or you should use 'import ... with " ++ id.name ++ " as ...' to change the other production's apparent name.")] - else []; - - top.errors <- - if length(body.uniqueSignificantExpression) > 1 - then [err(top.location, "Production '" ++ id.name ++ "' has more than one forward declaration.")] - else []; - - top.errors <- - if isLower(substring(0,1,id.name)) then [] - else [wrn(id.location, s"(future) ${id.name}: productions may be required to begin with a lower-case letter.")]; - - production attribute sigDefs :: [Def] with ++; - sigDefs := ns.defs; - - ns.signatureName = fName; - ns.env = newScopeEnv(sigDefs, top.env); - - local attribute prodAtts :: [Def]; - prodAtts = defsFromPADcls(getProdAttrs(fName, top.env), namedSig); - - body.env = newScopeEnv(body.defs ++ sigDefs, newScopeEnv(prodAtts, top.env)); - body.frame = productionContext(namedSig, myFlowGraph); -- graph from flow:env -} - -concrete production productionSignature -top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS -{ - top.unparse = lhs.unparse ++ " ::= " ++ rhs.unparse; - - top.namedSignature = namedSignature(top.signatureName, rhs.inputElements, lhs.outputElement, annotationsForNonterminal(lhs.outputElement.typerep, top.env)); -} - -concrete production productionLHS -top::ProductionLHS ::= id::Name '::' t::TypeExpr -{ - top.unparse = id.unparse ++ "::" ++ t.unparse; - - top.outputElement = namedSignatureElement(id.name, t.typerep); - - top.defs := [lhsDef(top.grammarName, t.location, id.name, t.typerep)]; - - top.errors <- - if length(getValueDclInScope(id.name, top.env)) > 1 - then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] - else []; -} - -concrete production productionRHSNil -top::ProductionRHS ::= -{ - top.unparse = ""; - - top.inputElements = []; -} - -concrete production productionRHSCons -top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS -{ - top.unparse = h.unparse ++ " " ++ t.unparse; - - top.inputElements = h.inputElements ++ t.inputElements; - h.deterministicCount = length(t.inputElements); -} - -concrete production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - top.unparse = id.unparse ++ "::" ++ t.unparse; - - top.inputElements = [namedSignatureElement(id.name, t.typerep)]; - - top.defs := [childDef(top.grammarName, t.location, id.name, t.typerep)]; - - top.errors <- - if length(getValueDclInScope(id.name, top.env)) > 1 - then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] - else []; -} - -concrete production productionRHSElemType -top::ProductionRHSElem ::= t::TypeExpr -{ - top.unparse = t.unparse; - - forwards to productionRHSElem(name("_G_" ++ toString(top.deterministicCount), t.location), '::', t, location=top.location); -} - diff --git a/grammars/silver/definition/core/Project.sv b/grammars/silver/definition/core/Project.sv deleted file mode 100644 index 25fbed201..000000000 --- a/grammars/silver/definition/core/Project.sv +++ /dev/null @@ -1,36 +0,0 @@ -grammar silver:definition:core; - -exports silver:langutil; - --- The following are grammar-wide imports for 'core' - --- The 'Type' syntax. (I made this separate to try to make s:d:core less of a "dump everything here" grammar.) -imports silver:definition:type:syntax; - --- Type Representation -imports silver:definition:type; - --- Environment Representation -imports silver:definition:env; - --- Utilities -imports silver:util; - -option silver:definition:concrete_syntax; -option silver:definition:flow:syntax; -option silver:modification:lambda_fn; -option silver:modification:let_fix; -option silver:modification:primitivepattern; -option silver:modification:ffi; -option silver:modification:typedecl; -option silver:modification:copper; -option silver:modification:defaultattr; -option silver:modification:collection; -option silver:modification:copper_mda; - -option silver:extension:testing; -- TODO this is about that buggy experiment of Eric's... - --- These are somewhat less than desirable exports, due to the modularity analysis. -exports silver:analysis:typechecking:core; -exports silver:definition:flow:env; - diff --git a/grammars/silver/definition/core/QName.sv b/grammars/silver/definition/core/QName.sv deleted file mode 100644 index f5a7252bf..000000000 --- a/grammars/silver/definition/core/QName.sv +++ /dev/null @@ -1,257 +0,0 @@ -grammar silver:definition:core; - -{-- - - Qualified names of the form 'a:b:c:d...' - -} -nonterminal QName with config, name, location, grammarName, env, unparse, qNameType; -{-- - - Qualified names where the LAST name has an upper case first letter. - -} -nonterminal QNameType with config, name, location, grammarName, env, unparse; - -{-- - - The list of declarations resulting from looking up this QName - -} -synthesized attribute dcls :: [DclInfo]; - -synthesized attribute qNameType::QNameType; - --- TODO: for consistency, the order of these args should be flipped: -function qName -QName ::= l::Location s::String -{ - return qNameId(nameIdLower(terminal(IdLower_t, s, l), location=l), location=l); -} - -concrete production qNameId -top::QName ::= id::Name -{ - top.name = id.name; - top.unparse = id.unparse; - top.qNameType = qNameTypeId(terminal(IdUpper_t, id.name, id.location), location=id.location); - - top.lookupValue = decorate customLookup("value", getValueDcl(top.name, top.env), top.name, top.location) with {}; - top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; - top.lookupAttribute = decorate customLookup("attribute", getAttrDcl(top.name, top.env), top.name, top.location) with {}; -} - -concrete production qNameCons -top::QName ::= id::Name ':' qn::QName -{ - top.name = id.name ++ ":" ++ qn.name; - top.unparse = id.unparse ++ ":" ++ qn.unparse; - top.qNameType = qNameTypeCons(id, ':', qn.qNameType, location=top.location); - - top.lookupValue = decorate customLookup("value", getValueDcl(top.name, top.env), top.name, top.location) with {}; - top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; - top.lookupAttribute = decorate customLookup("attribute", getAttrDcl(top.name, top.env), top.name, top.location) with {}; -} - -abstract production qNameError -top::QName ::= msg::[Message] -{ - top.name = "err"; - top.unparse = ""; - top.qNameType = qNameTypeId(terminal(IdUpper_t, "Err", top.location), location=top.location); - - top.lookupValue = decorate errorLookup(msg) with {}; - top.lookupType = decorate errorLookup(msg) with {}; - top.lookupAttribute = decorate errorLookup(msg) with {}; -} - -nonterminal QNameLookup with fullName, typerep, errors, dcls, dcl, dclBoundVars, found; - -synthesized attribute lookupValue :: Decorated QNameLookup occurs on QName; -synthesized attribute lookupType :: Decorated QNameLookup occurs on QName; -synthesized attribute lookupAttribute :: Decorated QNameLookup occurs on QName; - -abstract production customLookup -top::QNameLookup ::= kindOfLookup::String dcls::[DclInfo] name::String l::Location -{ - top.dcls = dcls; - top.found = !null(top.dcls); -- currently accurate - top.dcl = - if top.found then head(top.dcls) - else error("INTERNAL ERROR: Accessing dcl of " ++ kindOfLookup ++ " " ++ name ++ " at " ++ l.unparse); - - top.fullName = if top.found then top.dcl.fullName else "undeclared:value:" ++ name; - - -- TODO: We could eliminate a lot of explicit calls to 'freshenCompletely' and make this more correct - -- if we pushed into 'dcl' a different kind of Type, which recorded quantifiers. - -- e.g. QuantifiedType. Then when we asked for .typerep of that, it always freshens. - top.typerep = if top.found then top.dcl.typerep else errorType(); - top.dclBoundVars = if top.found then top.dcl.dclBoundVars else []; - - top.errors := - (if top.found then [] - else [err(l, "Undeclared " ++ kindOfLookup ++ " '" ++ name ++ "'.")]) ++ - (if length(top.dcls) <= 1 then [] - else [err(l, "Ambiguous reference to " ++ kindOfLookup ++ " '" ++ name ++ "'. Possibilities are:\n" ++ printPossibilities(top.dcls))]); -} - -abstract production errorLookup -top::QNameLookup ::= msg::[Message] -{ - top.dcls = []; - top.found = true; - top.dcl = error("dcl demanded from errorLookup"); - top.fullName = "err"; - top.typerep = errorType(); - top.dclBoundVars = []; - top.errors := msg; -} - -function printPossibilities -String ::= lst::[DclInfo] -{ - return implode("\n", map(dclinfo2possibility, lst)); -} -function dclinfo2possibility -String ::= dcl::DclInfo -{ - -- TODO: perhaps some way of including types, when they are relevant (attributes, values) - return "\t" ++ dcl.fullName ++ " (" ++ dcl.sourceLocation.filename ++ ":" ++ toString(dcl.sourceLocation.line) ++ ")"; -} - - ----- Right now, this is only used for types: -attribute lookupType occurs on QNameType; - -concrete production qNameTypeId -top::QNameType ::= id::IdUpper_t -{ - top.name = id.lexeme; - top.unparse = id.lexeme; - - top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; -} - -concrete production qNameTypeCons -top::QNameType ::= id::Name ':' qn::QNameType -{ - top.name = id.name ++ ":" ++ qn.name; - top.unparse = id.unparse ++ ":" ++ qn.unparse; - - top.lookupType = decorate customLookup("type", getTypeDcl(top.name, top.env), top.name, top.location) with {}; -} - -{-- - - Qualified name looked up CONTEXTUALLY - -} -nonterminal QNameAttrOccur with config, name, location, grammarName, env, unparse, attrFor, errors, typerep, dcl, attrDcl, found; - -{-- - - For QNameAttrOccur, the name of the LHS to look up this attribute on. - - i.e. - -} -inherited attribute attrFor :: Type; -synthesized attribute attrDcl :: DclInfo; - -{-- - - Whether lookup was successful. Better than `null(_.errors)` because errors may be suppressed - -} -synthesized attribute found :: Boolean; - -{-- - - Used like `x.`. - - @param at the name of an attribute - - @inh attrFor the type this attribute should be on - -} -concrete production qNameAttrOccur -top::QNameAttrOccur ::= at::QName -{ - top.name = at.name; - top.unparse = at.unparse; - - -- We start with all attributes we find with the name `at`: - -- Then we filter to just those that appear to have an occurrence on `top.attrFor`: - local narrowed :: [[DclInfo]] = - -- The occurs dcls on this nonterminal for - map(getOccursDcl(_, top.attrFor.typeName, top.env), - -- the full names of each candidate - map((.fullName), at.lookupAttribute.dcls)); - -- TODO: BUG: this disambiguates, but doesn't find full-named that aren't in scope with short names! - -- i.e. 'import somthing as prefixed; something.a' won't find prefixed:a. - - -- Occurs dcls for `at` on `top.attrFor` (there should be only one) - local dclsNarrowed :: [DclInfo] = concat(narrowed); - - -- Attribute dcls - local attrsNarrowed :: [DclInfo] = zipFilterDcls(at.lookupAttribute.dcls, narrowed); - - -- This basically has to mirror the logic in errors below! - top.found = - !(null(at.lookupAttribute.dcls) || - top.attrFor.isError || - null(dclsNarrowed) || - length(attrsNarrowed) != 1); - - top.errors := - -- If we fail to look up the attribute, just report that. - if null(at.lookupAttribute.dcls) then - at.lookupAttribute.errors - -- If we're looking up an attribute on `errorType`, an error is already raised, don't create noise - else if top.attrFor.isError then - [] - -- If no attribute occurs on this type, raise that error - else if null(dclsNarrowed) then - -- This is a heuristic error message for the situation where you have a type, but haven't imported - -- the grammar declaring that type. - (if top.attrFor.typeName != "" && null(getTypeDcl(top.attrFor.typeName, top.env)) then - [err(at.location, "Attribute '" ++ at.name ++ "' does not occur on '" ++ prettyType(top.attrFor) ++ "'. Perhaps import '" ++ substring(0, lastIndexOf(":", top.attrFor.typeName), top.attrFor.typeName) ++ "'?")] - else - [err(at.location, "Attribute '" ++ at.name ++ "' does not occur on '" ++ prettyType(top.attrFor) ++ "'. Looked at:\n" ++ printPossibilities(at.lookupAttribute.dcls))] - ) - -- If more than one attribute on the same _short name_ occurs, raise ambiguity - else if length(attrsNarrowed) > 1 then - [err(at.location, "Ambiguous reference to attribute occurring on '" ++ at.name ++ "'. Possibilities are:\n" ++ printPossibilities(attrsNarrowed))] - -- If this same attribute has multiple occurences (must be due to orphaned occurs) - else []; {-if length(dclsNarrowed) > 1 then - [err(at.location, "There are erroneously multiple attribute occurrences for '" ++ at.name ++ "'. Possibilities are:\n" ++ printPossibilities(dclsNarrowed))] - else [];-} - -- TODO: This last bit is disabled because we have problems with importing grammars multiple times. - -- TODO FIXME: enable this, and fix the grammar import issues! - - top.typerep = if top.found then determineAttributeType(head(dclsNarrowed), top.attrFor) else errorType(); - top.dcl = if top.found then head(dclsNarrowed) else - error("INTERNAL ERROR: Accessing dcl of occurrence " ++ at.name ++ " at " ++ top.grammarName ++ " " ++ top.location.unparse); - top.attrDcl = if top.found then head(attrsNarrowed) else - -- Workaround fix for proper error reporting - appairently there are some places where this is still demanded. - if !null(at.lookupAttribute.dcls) then head(at.lookupAttribute.dcls) else - error("INTERNAL ERROR: Accessing dcl of attribute " ++ at.name ++ " at " ++ top.grammarName ++ " " ++ top.location.unparse); -} - -{-- - - `at` is a list of attribute declarations - - `occ` is a mapped list of occurrence declarations for the corresponding attribute - - we return only those `at` which have a non-empty element in `occ` - -} -function zipFilterDcls -[DclInfo] ::= at::[DclInfo] occ::[[DclInfo]] -{ - return if null(at) then [] - else if null(head(occ)) then zipFilterDcls(tail(at), tail(occ)) - else head(at) :: zipFilterDcls(tail(at), tail(occ)); -} - - --- TODO THIS SHOULD BE OBSOLETED BY THE ABOVE -nonterminal OccursCheck with errors, typerep, dcl; - --- Doc note: be sure you've included at.errors, as well as this production's errors! -abstract production occursCheckQName -top::OccursCheck ::= at::Decorated QName ntty::Type -{ - local occursCheck :: [DclInfo] = - getOccursDcl(at.lookupAttribute.fullName, ntty.typeName, at.env); -- cheating to get env! :) Must be decorated! - - top.errors := if null(at.lookupAttribute.errors) && null(occursCheck) && !ntty.isError - then [err(at.location, "Attribute '" ++ at.name ++ "' does not occur on '" ++ prettyType(ntty) ++ "'")] - else []; - top.typerep = if null(at.lookupAttribute.errors) && null(top.errors) - then determineAttributeType(head(occursCheck), ntty) - else errorType(); - top.dcl = head(occursCheck); -} - - diff --git a/grammars/silver/definition/core/Root.sv b/grammars/silver/definition/core/Root.sv deleted file mode 100644 index f79eb51d6..000000000 --- a/grammars/silver/definition/core/Root.sv +++ /dev/null @@ -1,68 +0,0 @@ -grammar silver:definition:core; - -{-- - - Root represents one textual file of Silver source. - -} -nonterminal Root with - -- Global-level inherited attributes - config, compiledGrammars, - -- Grammar-level inherited attributes - grammarName, env, globalImports, grammarDependencies, - -- File-level inherited attributes - -- Synthesized attributes - declaredName, unparse, location, errors, defs, occursDefs, moduleNames, importedDefs, importedOccursDefs, - exportedGrammars, optionalGrammars, condBuild, jarName; - -nonterminal GrammarDcl with - declaredName, grammarName, location, unparse, errors; - -propagate errors on Root, GrammarDcl; -propagate moduleNames on Root; - -concrete production root -top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls -{ - ims.compiledGrammars = top.compiledGrammars; - ims.grammarDependencies = top.grammarDependencies; - ims.grammarName = top.grammarName; - ims.config = top.config; - - top.unparse = gdcl.unparse ++ "\n\n" ++ ms.unparse ++ "\n\n" ++ ims.unparse ++ "\n\n" ++ ags.unparse; - top.declaredName = gdcl.declaredName; - - top.defs := ags.defs; - top.occursDefs := ags.occursDefs; - - top.importedDefs := ms.defs; - top.importedOccursDefs := ms.occursDefs; - top.exportedGrammars := ms.exportedGrammars; - top.optionalGrammars := ms.optionalGrammars; - top.condBuild := ms.condBuild; - top.jarName := ags.jarName; - - -- We have an mismatch in how the environment gets put together: - -- Outermost, we have grammar-wide imports in one sope. That's top.globalImports here. - -- THEN, we have this particular file's list of local imports. That's ims.defs here. - -- THEN, we have the grammar-wide definitions, from the whole grammr. That's top.env here. - -- So we're kind of injecting local imports in between two grammar-wide things there. - ags.env = appendEnv(top.env, newScopeEnv(ims.defs, occursEnv(ims.occursDefs, top.globalImports))); -} - -concrete production noGrammarDcl -top::GrammarDcl ::= -{ - top.unparse = ""; - top.declaredName = top.grammarName; -} - -concrete production grammarDcl_c -top::GrammarDcl ::= 'grammar' qn::QName ';' -{ - top.unparse = "grammar " ++ qn.unparse ++ ";"; - - top.declaredName = qn.name; - top.errors <- - if qn.name == top.grammarName then [] - else [err(top.location, "Grammar declaration is incorrect: " ++ qn.name)]; -} - diff --git a/grammars/silver/definition/core/Terminals.sv b/grammars/silver/definition/core/Terminals.sv deleted file mode 100644 index 337ed25b3..000000000 --- a/grammars/silver/definition/core/Terminals.sv +++ /dev/null @@ -1,115 +0,0 @@ -grammar silver:definition:core; - -temp_imp_ide_font font_comments color(82, 141, 115) italic; -- Good: same as java -temp_imp_ide_font font_literal color(50, 50, 250); -- BAD -temp_imp_ide_font font_keyword color(123, 0, 82) bold; -- Good: same as java -temp_imp_ide_font font_modword color(41,95,148) bold; -- maybe good? Unusual but looks good -temp_imp_ide_font font_type color(41,95,148); -- type coloring - -lexer class Silver prefix separator ":"; - -lexer class IDENTIFIER extends Silver; -lexer class RESERVED dominates IDENTIFIER; - -lexer class COMMENT extends Silver, font = font_comments; -lexer class LITERAL extends Silver, font = font_literal; -lexer class KEYWORD extends Silver, font = font_keyword; -lexer class MODSTMT extends Silver, font = font_modword; -lexer class SPECOP extends Silver, font = font_keyword; -lexer class BUILTIN extends Silver, font = font_keyword; -lexer class TYPE extends Silver, font = font_type; - -terminal As_kwd 'as' lexer classes {MODSTMT,RESERVED}; -terminal Exports_kwd 'exports' lexer classes {MODSTMT}; -terminal Grammar_kwd 'grammar' lexer classes {MODSTMT,RESERVED}; -terminal Hiding_kwd 'hiding' lexer classes {MODSTMT,RESERVED}; -terminal Import_kwd 'import' lexer classes {MODSTMT}; -terminal Imports_kwd 'imports' lexer classes {MODSTMT}; -terminal Only_kwd 'only' lexer classes {MODSTMT,RESERVED}; -terminal Optional_kwd 'option' lexer classes {MODSTMT}; --- TODO with - --- TODO A substantial fraction of these don't need to be reserved! -terminal Abstract_kwd 'abstract' lexer classes {KEYWORD,RESERVED}; -terminal Aspect_kwd 'aspect' lexer classes {KEYWORD,RESERVED}; -terminal Attribute_kwd 'attribute' lexer classes {KEYWORD,RESERVED}; -terminal Closed_kwd 'closed' lexer classes {KEYWORD}; -terminal Concrete_kwd 'concrete' lexer classes {KEYWORD,RESERVED}; -terminal Decorate_kwd 'decorate' lexer classes {KEYWORD,RESERVED}; -terminal Else_kwd 'else' lexer classes {KEYWORD,RESERVED}, precedence = 4, association = left; -- Association needed for dangling else in action code. -terminal Forwarding_kwd 'forwarding' lexer classes {KEYWORD,RESERVED}; -terminal Forward_kwd 'forward' lexer classes {KEYWORD,RESERVED}; -terminal Forwards_kwd 'forwards' lexer classes {KEYWORD,RESERVED}; -terminal Function_kwd 'function' lexer classes {KEYWORD,RESERVED}; -terminal If_kwd 'if' lexer classes {KEYWORD,RESERVED}; -terminal Inherited_kwd 'inherited' lexer classes {KEYWORD,RESERVED}; -terminal Local_kwd 'local' lexer classes {KEYWORD,RESERVED}; -terminal New_kwd 'new' lexer classes {KEYWORD,RESERVED}; -terminal NonTerminal_kwd 'nonterminal' lexer classes {KEYWORD,RESERVED}; -terminal Occurs_kwd 'occurs' lexer classes {KEYWORD,RESERVED}; -terminal On_kwd 'on' lexer classes {KEYWORD,RESERVED}; -terminal Production_kwd 'production' lexer classes {KEYWORD,RESERVED}; -terminal Return_kwd 'return' lexer classes {KEYWORD,RESERVED}; -terminal Synthesized_kwd 'synthesized' lexer classes {KEYWORD,RESERVED}; -terminal Terminal_kwd 'terminal' lexer classes {KEYWORD,RESERVED}; -terminal Then_kwd 'then' lexer classes {KEYWORD,RESERVED}; -terminal To_kwd 'to' lexer classes {KEYWORD,RESERVED}; -terminal With_kwd 'with' lexer classes {KEYWORD,RESERVED}; -terminal Global_kwd 'global' lexer classes {KEYWORD,RESERVED}; - -terminal Length_kwd 'length' lexer classes {BUILTIN,RESERVED}; -terminal ToBoolean_kwd 'toBoolean' lexer classes {BUILTIN,RESERVED}; -terminal ToFloat_kwd 'toFloat' lexer classes {BUILTIN,RESERVED}; -terminal ToInt_kwd 'toInt' lexer classes {BUILTIN,RESERVED}; -- Legacy -terminal ToInteger_kwd 'toInteger' lexer classes {BUILTIN,RESERVED}; -terminal ToString_kwd 'toString' lexer classes {BUILTIN,RESERVED}; -terminal Reify_kwd 'reify' lexer classes {BUILTIN,RESERVED}; - -terminal Comma_t ',' precedence = 4; -terminal Or_t '||' precedence = 5, association = left; -terminal And_t '&&' precedence = 6, association = left; -terminal Not_t '!' precedence = 7; -terminal GT_t '>' precedence = 9, association = left; -terminal LT_t '<' precedence = 9, association = left; -terminal GTEQ_t '>=' precedence = 9, association = left; -terminal LTEQ_t '<=' precedence = 9, association = left; -terminal EQEQ_t '==' precedence = 9, association = left; -terminal NEQ_t '!=' precedence = 9, association = left; -terminal PlusPlus_t '++' precedence = 10, association = right; -- right because that's generally more efficient. -terminal Plus_t '+' precedence = 11, association = left; -terminal Minus_t '-' precedence = 11, association = left; -terminal Multiply_t '*' precedence = 12, association = left; -terminal Divide_t '/' precedence = 12, association = left; -terminal Modulus_t '%' precedence = 12, association = left; -terminal ColonColon_t '::' precedence = 14, association = right; -- HasType AND cons. right due to cons. -terminal LParen_t '(' precedence = 24; -terminal RParen_t ')' precedence = 1, association = left; -- Precedence and association eeded for dangling else in action code. -terminal LCurly_t '{' ; -terminal RCurly_t '}' ; -terminal Dot_t '.' precedence = 25, association = left; -terminal Semi_t ';' ; -terminal Colon_t ':' ; -terminal UnderScore_t '_' ; - -terminal CCEQ_t '::=' lexer classes {SPECOP}; -terminal Equal_t '=' lexer classes {SPECOP}; - --- Unused infix operators: ~ ` @ # % ^ & | \ --- $ is used by convenience. - - -- this is a very careful regex. beware: ---ignore terminal BlockComments /\{\-([^\-]|\-+[^\}\-])*\-+\}/ lexer classes {COMMENT}; -ignore terminal BlockComments /\{\-(\{\-([^\-]|\-+[^\}\-])*\-+\}|[^\-]|\-+[^\}\-])*\-+\}/ lexer classes {COMMENT}; -- Allows (one level of) nested comments. -ignore terminal Comments /([\-][\-].*)/ lexer classes {COMMENT}; - -ignore terminal WhiteSpace /[\r\n\t\ ]+/; - -terminal IdLower_t /[a-z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; -terminal IdUpper_t /[A-Z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; - -terminal True_kwd 'true' lexer classes {LITERAL,RESERVED}; -terminal False_kwd 'false' lexer classes {LITERAL,RESERVED}; -terminal Int_t /[\-]?[0-9]+/ lexer classes {LITERAL}; -terminal Float_t /[\-]?[0-9]+[\.][0-9]+/ lexer classes {LITERAL}; -terminal String_t /[\"]([^\r\n\"\\]|[\\][\"]|[\\][\\]|[\\]b|[\\]n|[\\]r|[\\]f|[\\]t)*[\"]/ lexer classes {LITERAL}; - diff --git a/grammars/silver/definition/core/Type.sv b/grammars/silver/definition/core/Type.sv deleted file mode 100644 index 25b394874..000000000 --- a/grammars/silver/definition/core/Type.sv +++ /dev/null @@ -1,110 +0,0 @@ -grammar silver:definition:core; - --- LHS type gives this to 'application' for "foo(...)" calls. -synthesized attribute applicationDispatcher :: (Expr ::= Decorated Expr AppExprs AnnoAppExprs Location); --- LHS type gives this to 'access' for "foo.some" accesses. --- (See DclInfo for the next step) -synthesized attribute accessHandler :: (Expr ::= Decorated Expr Decorated QNameAttrOccur Location); - -synthesized attribute lengthDispatcher :: (Expr ::= Decorated Expr Location); -synthesized attribute appendDispatcher :: (Expr ::= Decorated Expr Decorated Expr Location); - --- Used for poor man's type classes -synthesized attribute instanceEq :: Boolean; -synthesized attribute instanceOrd :: Boolean; -synthesized attribute instanceNum :: Boolean; -synthesized attribute instanceConvertible :: Boolean; - -attribute applicationDispatcher, accessHandler, lengthDispatcher, appendDispatcher, - instanceEq, instanceOrd, instanceNum, instanceConvertible occurs on Type; - -aspect default production -top::Type ::= -{ - top.applicationDispatcher = errorApplication(_, _, _, location=_); - top.accessHandler = errorAccessHandler(_, _, location=_); - top.instanceEq = false; - top.instanceOrd = false; - top.instanceNum = false; - top.instanceConvertible = false; - top.lengthDispatcher = errorLength(_, location=_); - top.appendDispatcher = errorPlusPlus(_, _, location=_); -} - -aspect production errorType -top::Type ::= -{ - -- Allow these, to suppress raising additional unnecessary errors. - top.instanceEq = true; - top.instanceOrd = true; - top.instanceNum = true; - top.instanceConvertible = true; -} - -aspect production intType -top::Type ::= -{ - top.instanceEq = true; - top.instanceOrd = true; - top.instanceNum = true; - top.instanceConvertible = true; -} - -aspect production boolType -top::Type ::= -{ - top.instanceEq = true; - top.instanceConvertible = true; -} - -aspect production floatType -top::Type ::= -{ - top.instanceEq = true; - top.instanceOrd = true; - top.instanceNum = true; - top.instanceConvertible = true; -} - -aspect production stringType -top::Type ::= -{ - top.instanceEq = true; - top.instanceOrd = true; - top.instanceConvertible = true; - top.lengthDispatcher = stringLength(_, location=_); - top.appendDispatcher = stringPlusPlus(_, _, location=_); -} - -aspect production terminalIdType -top::Type ::= -{ - top.instanceEq = true; - top.instanceOrd = true; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.accessHandler = undecoratedAccessHandler(_, _, location=_); -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.accessHandler = terminalAccessHandler(_, _, location=_); -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.accessHandler = decoratedAccessHandler(_, _, location=_); -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - -- TODO: We don't seem to use this. Perhaps we should remove it? - top.applicationDispatcher = functionApplication(_, _, _, location=_); -} - diff --git a/grammars/silver/definition/env/Attributes.sv b/grammars/silver/definition/env/Attributes.sv deleted file mode 100644 index 886421eb2..000000000 --- a/grammars/silver/definition/env/Attributes.sv +++ /dev/null @@ -1,104 +0,0 @@ -grammar silver:definition:env; - --- TODO: it'd be nice to find a way to rejigger things so these imports can go away. -import silver:util:cmdargs only CmdArgs; -import silver:definition:flow:driver only ProductionGraph, FlowType; -import silver:driver:util only RootSpec; - -{- This grammar contains common definitions of attributes that - are widely used in the Silver compiler. - - Attributes should NOT be added here lightly! - Include a justification for each one. - - These are truely cross-cutting attributes, not simple - things that occurs on a few pieces of syntax. - A good rule of thumb is: is it used by RootSpec Root and IRoot? - --------------------------------------------------------------------} - --- --- RootSpec, Root and IRoot grammar graph-related attributes. --- - -{-- - - The name of the grammar this RootSpec represents. - -} -synthesized attribute declaredName :: String; -{-- - - Grammars directly depended upon by this grammar. - - i.e. imports, exports, parser components, etc. - - NOT options, or triggers, or transitive dependencies. - -} -monoid attribute moduleNames :: [String] with [], ++; -{-- - - Grammars DIRECTLY exported by this grammar. - -} -monoid attribute exportedGrammars :: [String] with [], ++; -{-- - - Grammars this grammar specifies as optional modifications. - - (i.e. grammars that introduce more productions that do not forward) - -} -monoid attribute optionalGrammars :: [String] with [], ++; -{-- - - A list of triggered builds. Format is actually [ [build x, with gram], ... ] - -} -monoid attribute condBuild :: [[String]] with [], ++; -{-- - - A list of TRUE dependencies of this grammar. - - Closes over moduleNames using exports & triggers. - -} -monoid attribute allGrammarDependencies :: [String] with [], ++; -{-- - - A list of attribute occurences that are exported by this particular grammar. - - Seperate from defs as a workaround for circular dependency issues. - -} -monoid attribute occursDefs :: [DclInfo] with [], ++; - - --- --- Standard "little attributes," used almost universally in the compiler. --- - -{-- - - A list of definitions exported by this particular grammar. - -} -monoid attribute defs :: [Def] with [], ++; -{-- - - The environment. Dun dun dunnn. - -} -autocopy attribute env :: Decorated Env; - --- --- Top-level, compiler-wide information passed down by the build process --- TODO: these don't necessarily make sense. Can we move them pleaaase?! - -{-- -- All grammars Silver looked at. Despite the name, including interface files. --} -autocopy attribute compiledGrammars :: EnvTree; -{-- -- Compiler configuration information, made available everywhere. --} -autocopy attribute config :: Decorated CmdArgs; -{-- -- Flow information computed for this grammar --} -autocopy attribute productionFlowGraphs :: EnvTree; -autocopy attribute grammarFlowTypes :: EnvTree; - -{-- - - The path to the origin of this root spec - -} -synthesized attribute grammarSource :: String; -{-- - - The modification time of the source .sv files of this grammar. - -} -synthesized attribute grammarTime :: Integer; -{-- - - The modification time of the interface file of this grammar. - - If the grammar was read from source, == grammarTime. - -} -synthesized attribute interfaceTime :: Integer; - - - diff --git a/grammars/silver/definition/env/DclInfo.sv b/grammars/silver/definition/env/DclInfo.sv deleted file mode 100644 index b13e381ee..000000000 --- a/grammars/silver/definition/env/DclInfo.sv +++ /dev/null @@ -1,344 +0,0 @@ -grammar silver:definition:env; - -imports silver:definition:type; - -import silver:definition:regex; -- soley for Terms. TODO : fix? - -synthesized attribute sourceGrammar :: String; -synthesized attribute sourceLocation :: Location; -synthesized attribute fullName :: String; - --- types -synthesized attribute typerep :: Type; -synthesized attribute dclBoundVars :: [TyVar]; - --- values -synthesized attribute namedSignature :: NamedSignature; -synthesized attribute hasForward :: Boolean; - --- occurs -synthesized attribute attrOccurring :: String; -inherited attribute givenNonterminalType :: Type; - -synthesized attribute isAnnotation :: Boolean; -- also "attrs" - --- attrs -synthesized attribute isSynthesized :: Boolean; -synthesized attribute isInherited :: Boolean; - --- production attribute -inherited attribute givenSignatureForDefs :: NamedSignature; -synthesized attribute prodDefs :: [Def]; --- production attribute substitutions -synthesized attribute substitutedDclInfo :: DclInfo; -- really ValueDclInfo -inherited attribute givenSubstitution :: Substitution; - - -{-- - - DclInfo SHOULD be several different types: TypeDclInfo, Value, Attribute, - - Occurs, ProductionAttr, etc. - - - - The reason it's not is we lack the ability to abstract over different types - - with "the same" interface (need typeclasses tia): this is necessary for some - - things that make use of e.g. fullName. - - - - hmm, unparsing could probably be fixed... - -} -closed nonterminal DclInfo with sourceGrammar, sourceLocation, fullName, -- everyone - typerep, givenNonterminalType, -- types (gNT for occurs) - namedSignature, hasForward, -- values that are fun/prod - attrOccurring, isAnnotation, -- occurs - isInherited, isSynthesized, -- attrs - prodDefs, -- production attributes - dclBoundVars, -- Global values (where we have type schemes) - substitutedDclInfo, givenSubstitution -- type substitutions on dcls - ; - -aspect default production -top::DclInfo ::= -{ - -- All dcls must provide sourceGrammar, sourceLocation, fullName - - -- All values must provide typerep. - -- All attributes must provide typerep, bound. - -- All types must provide typerep, bound. - - -- All production attributes must provide attrDcl. - -- All values that may be production attributes must provide substitutedDclInfo - -- All occurs must provide attrOccurring. (And now, typerep, which depends on givenNonterminalType) - - -- See silver:definition:core for more "musts" - - -- TODO: DESIGN PROBLEM: - -- The following defaults are provided to account for this one type (dclinfo) - -- being used, when there really SHOULD be different types. - -- (The only reason we use one type right now is that we like to have - -- e.g. fullName on all declarations, and we currently can't write a type - -- like "anything with a fullName".) - top.attrOccurring = error("Internal compiler error: must be defined for all occurs declarations"); - top.prodDefs = error("Internal compiler error: must be defined for all production attribute declarations"); - top.dclBoundVars = error("Internal compiler error: must be defined for all value declarations"); - top.substitutedDclInfo = error("Internal compiler error: must be defined for all value declarations that are production attributes"); - - -- Values that are not fun/prod have this valid default. - top.namedSignature = bogusNamedSignature(); - top.hasForward = false; - - -- On Occurs declarations and attrs - top.isAnnotation = false; - - -- attrs - top.isSynthesized = false; - top.isInherited = false; -} - --- ValueDclInfos that can NEVER appear in interface files: -abstract production childDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; -} -abstract production lhsDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; -} - --- ValueDclInfos that CAN appear in interface files, but only via "production attributes:" -abstract production localDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.substitutedDclInfo = localDcl(sg,sl, fn, performRenaming(ty, top.givenSubstitution)); -} -abstract production forwardDcl -top::DclInfo ::= sg::String sl::Location ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = "forward"; - - top.typerep = ty; - - top.substitutedDclInfo = forwardDcl(sg,sl, performRenaming(ty, top.givenSubstitution)); -} - --- ValueDclInfos that DO appear in interface files: -abstract production prodDcl -top::DclInfo ::= sg::String sl::Location ns::NamedSignature hasForward::Boolean -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = ns.fullName; - - local boundvars :: [TyVar] = top.typerep.freeVariables; - - top.namedSignature = ns; - top.typerep = ns.typerep; - top.hasForward = hasForward; -} -abstract production funDcl -top::DclInfo ::= sg::String sl::Location ns::NamedSignature -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = ns.fullName; - - local boundvars :: [TyVar] = top.typerep.freeVariables; - - top.namedSignature = ns; - top.typerep = ns.typerep; - top.hasForward = false; -} -abstract production globalValueDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; -} - --- TypeDclInfos -abstract production ntDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type closed::Boolean -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; -} -abstract production termDcl -top::DclInfo ::= sg::String sl::Location fn::String regex::Regex -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = terminalType(fn); - top.dclBoundVars = []; -} -abstract production lexTyVarDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = []; -} - --- AttributeDclInfos -abstract production synDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isSynthesized = true; -} -abstract production inhDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isInherited = true; -} -abstract production annoDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isAnnotation = true; -} - --- ProductionAttrDclInfo -abstract production paDcl -top::DclInfo ::= sg::String sl::Location ns::NamedSignature{-fn::String outty::Type intys::[Type]-} dcls::[Def] -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = ns.fullName; - - local boundvars :: [TyVar] = top.typerep.freeVariables; - - top.prodDefs = dcls; - - -- This is used by the function that computes the substituted defs. - top.typerep = ns.typerep; - -- We do have this now... any refactoring that should use it? - --top.namedSignature = ns; -} - --- OccursDclInfo -abstract production occursDcl -top::DclInfo ::= sg::String sl::Location fnnt::String fnat::String ntty::Type atty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fnnt; - - -- There should be no type variables in atty that aren't in ntty. (Important constraint!) - -- that's why we only use ntty.FV above. - - -- ALSO IMPORTANT: ntty and atty should be tyvar'd up, not skolem'd up. You dig? - - -- Here we use givenNonterminalType to find the attribute type: - local attribute subst :: Substitution; - subst = unifyDirectional(ntty, top.givenNonterminalType); -- must rewrite FROM ntty TO gNT - - top.typerep = if subst.failure - then -- We didn't get a sensible type for givenNonterminalType. Let's do our best? (This error should already be caught!) - freshenCompletely(atty) - else performRenaming(atty, subst); - - top.attrOccurring = fnat; -} - -abstract production annoInstanceDcl -top::DclInfo ::= sg::String sl::Location fnnt::String fnat::String ntty::Type atty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fnnt; - - -- There should be no type variables in atty that aren't in ntty. (Important constraint!) - -- that's why we only use ntty.FV above. - - -- ALSO IMPORTANT: ntty and atty should be tyvar'd up, not skolem'd up. You dig? - - -- Here we use givenNonterminalType to find the attribute type: - local attribute subst :: Substitution; - subst = unifyDirectional(ntty, top.givenNonterminalType); -- must rewrite FROM ntty TO gNT - - top.typerep = if subst.failure - then -- We didn't get a sensible type for givenNonterminalType. Let's do our best? (This error should already be caught!) - freshenCompletely(atty) - else performRenaming(atty, subst); - - top.attrOccurring = fnat; - - -- UGH - bit of a short hand here... - top.isAnnotation = true; -} - --- TODO: this should probably go elsewhere? -function determineAttributeType -Type ::= occursDclInfo::DclInfo ntty::Type -{ - occursDclInfo.givenNonterminalType = ntty; - return occursDclInfo.typerep; -} - --- Dealing with substitutions for production attributes. Really ValueDclInfos -function performSubstitutionDclInfo -DclInfo ::= valueDclInfo::DclInfo s::Substitution -{ - valueDclInfo.givenSubstitution = s; - return valueDclInfo.substitutedDclInfo; -} - --- This function really takes a list of ValueDclInfos -function defsFromPADcls -[Def] ::= valueDclInfos::[DclInfo] s::NamedSignature -{ - -- We want to rewrite FROM the sig these PAs were declared with, TO the given sig - local subst :: Substitution = - unifyDirectional(head(valueDclInfos).typerep, s.typerep); - - local useSubst :: Substitution = - if !subst.failure then subst - else errorSubstitution(head(valueDclInfos).typerep); - - return if null(valueDclInfos) then [] - else map(performSubstitutionDef(_, useSubst), head(valueDclInfos).prodDefs) ++ defsFromPADcls(tail(valueDclInfos), s); -} - diff --git a/grammars/silver/definition/env/Defs.sv b/grammars/silver/definition/env/Defs.sv deleted file mode 100644 index 3a5427971..000000000 --- a/grammars/silver/definition/env/Defs.sv +++ /dev/null @@ -1,239 +0,0 @@ -grammar silver:definition:env; - -import silver:definition:regex; -- soley for Terminals. TODO : perhaps this shouldn't be here! - -nonterminal Defs with typeList, valueList, attrList, prodOccursList, prodDclList; - --- The standard namespaces -synthesized attribute typeList :: [EnvItem]; -synthesized attribute valueList :: [EnvItem]; -synthesized attribute attrList :: [EnvItem]; - --- Production attributes. -synthesized attribute prodOccursList :: [DclInfo]; - --- Extra space for production list -synthesized attribute prodDclList :: [DclInfo]; - - -abstract production nilDefs -top::Defs ::= -{ - top.typeList = []; - top.valueList = []; - top.attrList = []; - - top.prodOccursList = []; - - top.prodDclList = []; -} - -abstract production consDefs -top::Defs ::= e1::Def e2::Defs -{ - top.typeList = e1.typeList ++ e2.typeList; - top.valueList = e1.valueList ++ e2.valueList; - top.attrList = e1.attrList ++ e2.attrList; - - top.prodOccursList = e1.prodOccursList ++ e2.prodOccursList; - - top.prodDclList = e1.prodDclList ++ e2.prodDclList; -} - --------------------------------------------------------------------------------- - --- Transformations on lists of Def --- This is to support computing the defs introduced by qualified imports --- (import foo only bar, import foo as bar, import foo with bar as baz) -inherited attribute filterFn::(Boolean ::= EnvItem); -synthesized attribute filterDef::Boolean; -inherited attribute mapFn::(EnvItem ::= EnvItem); -synthesized attribute mapDef::Def; - -closed nonterminal Def with typeList, valueList, attrList, prodOccursList, prodDclList, dcl, filterFn, filterDef, mapFn, mapDef; - -aspect default production -top::Def ::= -{ - top.typeList = []; - top.valueList = []; - top.attrList = []; - - top.prodOccursList = []; - - top.prodDclList = []; - - top.filterDef = true; -- We don't do any renaming for production attribute or occurs defs - top.mapDef = top; -- ditto -} -abstract production typeDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.typeList = [d]; - top.filterDef = top.filterFn(d); - top.mapDef = typeDef(top.mapFn(d)); -} -abstract production valueDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.valueList = [d]; - top.filterDef = top.filterFn(d); - top.mapDef = valueDef(top.mapFn(d)); -} -abstract production typeValueDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.typeList = [d]; - top.valueList = [d]; - top.filterDef = top.filterFn(d); - top.mapDef = typeValueDef(top.mapFn(d)); -} -abstract production attrDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.attrList = [d]; - top.filterDef = top.filterFn(d); - top.mapDef = attrDef(top.mapFn(d)); -} -abstract production prodDclDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.valueList = [d]; - -- unlike normal valueDef, also affect production lookups: - top.prodDclList = [d.dcl]; - top.filterDef = top.filterFn(d); - top.mapDef = prodDclDef(top.mapFn(d)); -} -abstract production paDef -top::Def ::= d::DclInfo -{ - top.dcl = d; - top.prodOccursList = [d]; -} - - -function childDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(childDcl(sg,sl,fn,ty))); -} -function lhsDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(lhsDcl(sg,sl,fn,ty))); -} -function localDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(localDcl(sg,sl,fn,ty))); -} -function prodDef -Def ::= sg::String sl::Location ns::NamedSignature hasForward::Boolean -{ - return prodDclDef(defaultEnvItem(prodDcl(sg,sl,ns,hasForward))); -} -function funDef -Def ::= sg::String sl::Location ns::NamedSignature -{ - return valueDef(defaultEnvItem(funDcl(sg,sl,ns))); -} -function globalDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(globalValueDcl(sg,sl,fn,ty))); -} -function ntDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return typeDef(defaultEnvItem(ntDcl(sg,sl,fn,bound,ty,false))); -} -function closedNtDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return typeDef(defaultEnvItem(ntDcl(sg,sl,fn,bound,ty,true))); -} -function termDef -Def ::= sg::String sl::Location fn::String regex::Regex -{ - -- Terminals are also in the value namespace as terminal identifiers - return typeValueDef(defaultEnvItem(termDcl(sg,sl,fn,regex))); -} -function lexTyVarDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return typeDef(defaultEnvItem(lexTyVarDcl(sg,sl,fn,ty))); -} -function synDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return attrDef(defaultEnvItem(synDcl(sg,sl,fn,bound,ty))); -} -function inhDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return attrDef(defaultEnvItem(inhDcl(sg,sl,fn,bound,ty))); -} -function prodOccursDef -Def ::= sg::String sl::Location ns::NamedSignature dcls::[Def] -{ - return paDef(paDcl(sg,sl,ns,dcls)); -} -function forwardDef -Def ::= sg::String sl::Location ty::Type -{ - return valueDef(defaultEnvItem(forwardDcl(sg,sl,ty))); -} --- These aliased functions are used for aspects. -function aliasedLhsDef -Def ::= sg::String sl::Location fn::String ty::Type alias::String -{ - return valueDef(onlyRenamedEnvItem(alias, lhsDcl(sg,sl,fn,ty))); -} -function aliasedChildDef -Def ::= sg::String sl::Location fn::String ty::Type alias::String -{ - return valueDef(onlyRenamedEnvItem(alias, childDcl(sg,sl,fn,ty))); -} -function annoDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return attrDef(defaultEnvItem(annoDcl(sg,sl,fn,bound,ty))); -} - - - --- I'm leaving "Defsironment" here just for the lols ----------------------------------------------------------------------------------------------------- ---Defsironment creation functions-------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- - -{-- - - Used only on what we get from production attributes. - - We encode those assumptions: - - 1. We expect ONLY valueDefs. - - 2. We expect ONLY 'defaultEnvItems' - -} -function performSubstitutionDef -Def ::= d::Def s::Substitution -{ - return valueDef(defaultEnvItem(performSubstitutionDclInfo(d.dcl, s))); -} - -function filterDefOnEnvItem -Boolean ::= fn::(Boolean ::= EnvItem) d::Def -{ - d.filterFn = fn; - return d.filterDef; -} -function mapDefOnEnvItem -Def ::= fn::(EnvItem ::= EnvItem) d::Def -{ - d.mapFn = fn; - return d.mapDef; -} - diff --git a/grammars/silver/definition/env/Env.sv b/grammars/silver/definition/env/Env.sv deleted file mode 100644 index 142b47849..000000000 --- a/grammars/silver/definition/env/Env.sv +++ /dev/null @@ -1,269 +0,0 @@ -grammar silver:definition:env; - - --- emptyEnv Decorated Env ::= --- toEnv Decorated Env ::= d::Defs --- appendEnv Decorated Env ::= e1::Decorated Env e2::Decorated Env --- newScopeEnv Decorated Env ::= e1::Defs e2::Decorated Env - --- [DclInfo] ::= search::String e::Decorated Env --- getValueDclInScope getValueDcl getValueDclAll --- getTypeDcl --- getAttrDcl - --- getProdAttrs [DclInfo] ::= prod::String e::Decorated Env - -nonterminal Env with typeTree, valueTree, attrTree, prodOccursTree, occursTree, prodsForNtTree; - -synthesized attribute typeTree :: [EnvScope]; -- Expr is type tau -synthesized attribute valueTree :: [EnvScope]; -- x has type tau -synthesized attribute attrTree :: [EnvScope]; -- attr a has type tau - -synthesized attribute prodOccursTree :: EnvScope; -- value on prod -synthesized attribute occursTree :: EnvScope; -- attr on NT - -synthesized attribute prodsForNtTree :: [EnvScope]; -- maps nt fname to prods known to construct it - ----------------------------------------------------------------------------------------------------- ---Environment creation functions-------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- - -function emptyEnv -Decorated Env ::= -{ - return decorate i_emptyEnv() with {}; -} -abstract production i_emptyEnv -top::Env ::= -{ - top.typeTree = [emptyEnvScope()]; - top.valueTree = [emptyEnvScope()]; - top.attrTree = [emptyEnvScope()]; - - top.prodOccursTree = emptyEnvScope(); - top.occursTree = emptyEnvScope(); - - top.prodsForNtTree = [emptyEnvScope()]; -} - -function toEnv -Decorated Env ::= d::[Def] -{ - return newScopeEnv(d, emptyEnv()); -} - -{-- - - appendEnv exists because we do a weird scope swizzling. - - Our scopes go [global, import, grammar] but hierarchically we - - should have [global, grammar, import] because grammar-wide names would - - seem to have wider scope than per-file imports. - - But this would be horrible semantics: the file in question is even included - - in the grammar-wide scope. So imports would hide things in the local file, even. - - - - Instead, we build these three scopes as essentially separate environments, - - and append them together in the correct order. - -} -function appendEnv -Decorated Env ::= e1::Decorated Env e2::Decorated Env -{ - return decorate i_appendEnv(e1, e2) with {}; -} -abstract production i_appendEnv -top::Env ::= e1::Decorated Env e2::Decorated Env -{ - top.typeTree = e1.typeTree ++ e2.typeTree; - top.valueTree = e1.valueTree ++ e2.valueTree; - top.attrTree = e1.attrTree ++ e2.attrTree; - - top.prodOccursTree = appendEnvScope(e1.prodOccursTree, e2.prodOccursTree); - top.occursTree = appendEnvScope(e1.occursTree, e2.occursTree); - - top.prodsForNtTree = e1.prodsForNtTree ++ e2.prodsForNtTree; -} - -{-- - - The usual means of introducing new defs to an environment, by creating a new nested scope. - -} -function newScopeEnv -Decorated Env ::= d::[Def] e::Decorated Env -{ - return decorate i_newScopeEnv(foldr(consDefs, nilDefs(), d), e) with {}; -} -abstract production i_newScopeEnv -top::Env ::= d::Defs e::Decorated Env -{ - top.typeTree = oneEnvScope(buildTree(d.typeList)) :: e.typeTree; - top.valueTree = oneEnvScope(buildTree(d.valueList)) :: e.valueTree; - top.attrTree = oneEnvScope(buildTree(d.attrList)) :: e.attrTree; - - top.prodOccursTree = consEnvScope(buildTree(mapFullnameDcls(d.prodOccursList)), e.prodOccursTree); - top.occursTree = e.occursTree; - - top.prodsForNtTree = oneEnvScope(buildTree(map(envItemNTFromProdDcl, d.prodDclList))) :: e.prodsForNtTree; -} - -{-- - - Introduces new occurs defs to an environment. - - This is seperate from newEnvScope as we must be able to build the other env trees without having the occurs tree. - -} -function occursEnv -Decorated Env ::= d::[DclInfo] e::Decorated Env -{ - return decorate i_occursEnv(d, e) with {}; -} -abstract production i_occursEnv -top::Env ::= d::[DclInfo] e::Decorated Env -{ - top.typeTree = e.typeTree; - top.valueTree = e.valueTree; - top.attrTree = e.attrTree; - - top.prodOccursTree = e.prodOccursTree; - top.occursTree = consEnvScope(buildTree(mapFullnameDcls(d)), e.occursTree); - - top.prodsForNtTree = e.prodsForNtTree; -} - ----------------------------------------------------------------------------------------------------- ---Environment query functions----------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------- - -function searchEnvAll -[a] ::= search::String e::[EnvScope] -{ - return flatMap(searchEnvScope(search, _), e); -} - -function searchEnv -[a] ::= search::String e::[EnvScope] -{ - local found :: [a] = searchEnvScope(search, head(e)); - - return if null(e) then [] - else if null(found) then searchEnv(search, tail(e)) - else found; -} - -function getValueDclInScope -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvScope(search, head(e.valueTree)); -} -function getValueDcl -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnv(search, e.valueTree); -} -function getValueDclAll -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvAll(search, e.valueTree); -} - -function getTypeDclInScope -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvScope(search, head(e.typeTree)); -} -function getTypeDcl -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnv(search, e.typeTree); -} -function getTypeDclAll -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvAll(search, e.typeTree); -} - -function getAttrDclInScope -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvScope(search, head(e.attrTree)); -} -function getAttrDcl -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnv(search, e.attrTree); -} -function getAttrDclAll -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvAll(search, e.attrTree); -} - -function getOccursDcl -[DclInfo] ::= fnat::String fnnt::String e::Decorated Env -{ - -- retrieve all attribute Dcls on NT fnnt - return occursOnHelp(searchEnvScope(fnnt, e.occursTree), fnat); -} -function occursOnHelp -[DclInfo] ::= i::[DclInfo] fnat::String -{ - -- Inefficiency. Linear search for attribute on a nonterminal - return if null(i) then [] - else if head(i).attrOccurring == fnat - then head(i) :: occursOnHelp(tail(i), fnat) - else occursOnHelp(tail(i), fnat); -} - -function getProdAttrs -[DclInfo] ::= fnprod::String e::Decorated Env -{ - return searchEnvScope(fnprod, e.prodOccursTree); -} - -{-- - - Get all productions for a nonterminal known to local environment. - - (forwarding and non-forwarding.) - - Current known use cases: - - 1. Interference testing code generation (randomly generate trees) - - - Because we test each production independently, we may not actually need this? - - 2. MWDA checking known forwarding productions on attribute occurrence declaration. - - - We need to be able to look from forwarding to occurs, and from - - occurs to forwarding to cover all cases. - - 3. For the automatic attributes extension - - - to implement propagate on all the known non-forwarding productions of a nonterminal. - - You should probably have a good reason for using this, and document it here if you do. - -} -function getKnownProds -[DclInfo] ::= fnnt::String e::Decorated Env -{ - return searchEnvAll(fnnt, e.prodsForNtTree); -} - --- The list of non-forwarding productions may contain productions from `options` not --- imported locally, and so we must consult the "flow environment" for that information: ---function getNonforwardingProds - -{-- - - Returns all attributes known locally to occur on a nonterminal. - - Obviously we can never know all attributes, but we generally don't need to for - - any reason. - -} -function getAttrsOn -[DclInfo] ::= fnnt::String e::Decorated Env -{ - return searchEnvScope(fnnt, e.occursTree); -} - --- This ensure the annotation list is in the properly sorted order! -function annotationsForNonterminal -[NamedSignatureElement] ::= nt::Type env::Decorated Env -{ - local annos :: [DclInfo] = - filter((.isAnnotation), getAttrsOn(nt.typeName, env)); - - return sortBy(namedSignatureElementLte, map(annoInstanceToNamed(nt, _), annos)); -} --- only used by the above -function annoInstanceToNamed -NamedSignatureElement ::= nt::Type anno::DclInfo -{ - -- Used to compute the local typerep for this nonterminal - anno.givenNonterminalType = nt; - - return namedSignatureElement(anno.attrOccurring, anno.typerep); -} - diff --git a/grammars/silver/definition/env/EnvItem.sv b/grammars/silver/definition/env/EnvItem.sv deleted file mode 100644 index 3ada0e035..000000000 --- a/grammars/silver/definition/env/EnvItem.sv +++ /dev/null @@ -1,123 +0,0 @@ -grammar silver:definition:env; - -{-- - - An entry in the environment. - -} -nonterminal EnvItem with itemName, dcl, envContribs; - -synthesized attribute itemName :: String; -synthesized attribute dcl :: DclInfo; -synthesized attribute envContribs :: [Pair]; - -{-- - - Rare case: use of `import _ with _ as _` or `import _ as _` to rename. - - Common case: `grammar:full:name` aka `name`. See `defaultEnvItem`. - -} -abstract production renamedEnvItem -ei::EnvItem ::= newname::String di::DclInfo -{ - ei.itemName = newname; - ei.dcl = di; - ei.envContribs = - if newname != di.fullName - then [pair(newname, di), pair(di.fullName, di)] - else [pair(newname, di)]; -} -{-- - - Entries at fullname ONLY. - - Used for occurrences & production attributes, which are looked up - - by the full nonterminal name (or production name) only, and a shortname is nonsense. - -} -abstract production fullNameEnvItem -ei::EnvItem ::= di::DclInfo -{ - ei.itemName = di.fullName; - ei.dcl = di; - ei.envContribs = [pair(di.fullName, di)]; -} -{-- - - Used for aspect local variables. The LHS and children have a full name - - like `newname` and in the aspect we can rename it anything we want. - - We should *not* see `newname` in the environment in those cases. - -} -abstract production onlyRenamedEnvItem -ei::EnvItem ::= newname::String di::DclInfo -{ - ei.itemName = newname; - ei.dcl = di; - ei.envContribs = [pair(newname, di)]; -} - -{-- - - The common case, normal shortnames. - -} -function defaultEnvItem -EnvItem ::= di::DclInfo -{ - return renamedEnvItem(fullNameToShort(di.fullName), di); -} -function fullNameToShort -String ::= s::String -{ - -- Works just fine, even when lastIndexOf returns -1 - return substring(lastIndexOf(":", s) + 1, length(s), s); -} - - - -function mapGetDcls -[DclInfo] ::= i::[EnvItem] -{ - return map((.dcl), i); -} - -function mapFullnameDcls -[EnvItem] ::= i::[DclInfo] -{ - return map(fullNameEnvItem, i); -} - -function mapDefaultWrapDcls -[EnvItem] ::= i::[DclInfo] -{ - return map(defaultEnvItem, i); -} - -function envItemExclude -Boolean ::= ei::EnvItem exclude::[String] -{ - return !containsBy(stringEq, ei.itemName, exclude); -} -function envItemInclude -Boolean ::= ei::EnvItem include::[String] -{ - return containsBy(stringEq, ei.itemName, include); -} -function envItemPrepend -EnvItem ::= ei::EnvItem pfx::String -{ - -- This clobbers 'onlyRenamed' but that's okay because this is only used - -- by imports, where that doesn't appear. - return renamedEnvItem(pfx ++ ei.itemName, ei.dcl); -} -function envItemApplyRenaming -EnvItem ::= ei::EnvItem renames::[Pair] -{ - local result :: Maybe = lookupBy(stringEq, ei.itemName, renames); - - return if !result.isJust then ei - -- this would clobber any 'onlyrenamed' but those shouldn't appear in imports, where this is used. - else renamedEnvItem(result.fromJust, ei.dcl); -} - - -{-- - - Maps a production's DclInfo into an EnvItem named for the nonterminal it constructs. - -} -function envItemNTFromProdDcl -EnvItem ::= di::DclInfo -{ - -- loooking up the full name of the nonterminal it creates will resolve this prodDcl - return onlyRenamedEnvItem(di.namedSignature.outputElement.typerep.typeName, di); -} - diff --git a/grammars/silver/definition/env/EnvScope.sv b/grammars/silver/definition/env/EnvScope.sv deleted file mode 100644 index 7fc4b12af..000000000 --- a/grammars/silver/definition/env/EnvScope.sv +++ /dev/null @@ -1,51 +0,0 @@ -grammar silver:definition:env; - -{-- - - Just an abstraction for representing one scope. - -} -nonterminal EnvScope with envTrees; - -{-- - - Our EnvTree abstraction is immutable, so we have a list here - - to accomodate any situation where we might need to add to an - - existing scope, rather than create a new one. - -} -synthesized attribute envTrees :: [EnvTree]; - -abstract production envScope -top::EnvScope ::= trees::[EnvTree] -{ - top.envTrees = trees; -} - - -function emptyEnvScope -EnvScope ::= -{ - return envScope([]); -} - -function oneEnvScope -EnvScope ::= eis::EnvTree -{ - return envScope([eis]); -} - -function appendEnvScope -EnvScope ::= l::EnvScope r::EnvScope -{ - return envScope(l.envTrees ++ r.envTrees); -} - -function consEnvScope -EnvScope ::= l::EnvTree r::EnvScope -{ - return envScope(l :: r.envTrees); -} - -function searchEnvScope -[a] ::= search::String e::EnvScope -{ - return flatMap(searchEnvTree(search, _), e.envTrees); -} - diff --git a/grammars/silver/definition/env/EnvTree.sv b/grammars/silver/definition/env/EnvTree.sv deleted file mode 100644 index 1c990a2a0..000000000 --- a/grammars/silver/definition/env/EnvTree.sv +++ /dev/null @@ -1,39 +0,0 @@ -grammar silver:definition:env; - -import silver:util:raw:treemap as rtm; -- good performance (mostly due to strictness!) ---import silver:util:fixedmap as fm; -- decent performance ---import silver:util:treemap as tm; -- poor performance - -{-- - - The abstraction for maps throughout the Silver compiler. - -} -type EnvTree = rtm:Map; - -{-- - - Look up function for maps. - -} -function searchEnvTree -[a] ::= search::String et::EnvTree -{ - return rtm:lookup(search, et); -} - -{-- - - Standard environment constructor for a map. - - Obey's EnvItem's rules for what names should appear for each item. - -} -function buildTree -EnvTree ::= eis::[EnvItem] -{ - return directBuildTree(flatMap((.envContribs), eis)); -} - -{-- - - Arbitrary environment constructor for a map. - -} -function directBuildTree -EnvTree ::= eis::[Pair] -{ - return rtm:add(eis, rtm:empty(compareString)); -} - diff --git a/grammars/silver/definition/env/NamedSignature.sv b/grammars/silver/definition/env/NamedSignature.sv deleted file mode 100644 index 53964e266..000000000 --- a/grammars/silver/definition/env/NamedSignature.sv +++ /dev/null @@ -1,129 +0,0 @@ -grammar silver:definition:env; - -{-- - - The fully named and fully type signature of a production (or function). - - Includes full name of the production and names for all input and output elements (and annotations). - - - - TODO: we might want to remove the full name of the production from this, and make it just `Signature`? - - It's not clear if this information really belongs here, or not. - -} -nonterminal NamedSignature with fullName, inputElements, outputElement, namedInputElements, typerep, inputNames, inputTypes; - -synthesized attribute inputElements :: [NamedSignatureElement]; -synthesized attribute outputElement :: NamedSignatureElement; -synthesized attribute namedInputElements :: [NamedSignatureElement]; -synthesized attribute inputNames :: [String]; --- inputTypes comes from the types grammar. - -{-- - - Represents the signature of a production (or function). - - @param fn The full name - - @param ie The input elements - - @param oe The output element - - @param np Named parameters (or annotations) - -} -abstract production namedSignature -top::NamedSignature ::= fn::String ie::[NamedSignatureElement] oe::NamedSignatureElement np::[NamedSignatureElement] -{ - top.fullName = fn; - top.inputElements = ie; - top.outputElement = oe; - top.namedInputElements = np; - top.inputNames = map((.elementName), ie); - top.inputTypes = map((.typerep), ie); -- Does anything actually use this? TODO: eliminate? - top.typerep = functionType(oe.typerep, top.inputTypes, map((.toNamedArgType), np)); -} - -{-- - - Used when an error occurs. e.g. aspecting a non-existant production. - - Or, in contexts that have no valid signature, which maybe we should do something about... - -} -abstract production bogusNamedSignature -top::NamedSignature ::= -{ - forwards to namedSignature("_NULL_", [], bogusNamedSignatureElement(), []); -} - -{-- - - Represents an elements of a signature, whether input, output, or annotation. - -} -nonterminal NamedSignatureElement with elementName, typerep, toNamedArgType; - -synthesized attribute elementName :: String; -synthesized attribute toNamedArgType :: NamedArgType; - -{-- - - Represents an element of the function/production signature. - -} -abstract production namedSignatureElement -top::NamedSignatureElement ::= n::String ty::Type -{ - top.elementName = n; - top.typerep = ty; - - -- When we convert from a SignatureElement to a NamedArg, we cut down to the short name only: - local annoShortName :: String = - substring(lastIndexOf(":", n) + 1, length(n), n); - - top.toNamedArgType = namedArgType(annoShortName, ty); -} - -{-- - - A bogus output element, because an error occurred, or because - - There is no output type. - -} -abstract production bogusNamedSignatureElement -top::NamedSignatureElement ::= -{ - forwards to namedSignatureElement("__SV_BOGUS_ELEM", errorType()); -} - ----------------- - -function namedSignatureElementLte -Boolean ::= a::NamedSignatureElement b::NamedSignatureElement -{ - return a.elementName <= b.elementName; -} - --- This is a big of an awful pile. Related to annotations, for now. -function findNamedSigElem -Integer ::= s::String l::[NamedSignatureElement] z::Integer -{ - return if null(l) then -1 - else if s == head(l).elementName then z - else findNamedSigElem(s, tail(l), z+1); -} - --------------- - --- Applies function to constituent types -function mapNamedSignature -NamedSignature ::= f::(Type ::= Type) ns::NamedSignature -{ - return case ns of - | namedSignature(fn, ie, oe, np) -> - namedSignature(fn, map(mapNamedSignatureElement(f, _), ie), mapNamedSignatureElement(f, oe), map(mapNamedSignatureElement(f, _), np)) - end; -} --- Ditto, for elements -function mapNamedSignatureElement -NamedSignatureElement ::= f::(Type ::= Type) nse::NamedSignatureElement -{ - return case nse of - | namedSignatureElement(n, ty) -> - namedSignatureElement(n, f(ty)) - end; -} - --- Freshens all the signature's types -function freshenNamedSignature -NamedSignature ::= ns::NamedSignature -{ - local fvs :: [TyVar] = ns.typerep.freeVariables; - local s :: Substitution = zipVarsIntoSubstitution(fvs, freshTyVars(length(fvs))); - - -- Apply the freshening within the signature's types - return mapNamedSignature(performRenaming(_, s), ns); -} - diff --git a/grammars/silver/definition/env/Type.sv b/grammars/silver/definition/env/Type.sv deleted file mode 100644 index 3b54f3225..000000000 --- a/grammars/silver/definition/env/Type.sv +++ /dev/null @@ -1,34 +0,0 @@ -grammar silver:definition:env; - --- Just to clarify: --- call prettyType to pretty print the type. --- get typeName to find out what nonterminal a NT or DNT is - -attribute typeName occurs on Type; - -synthesized attribute typeName :: String; - -aspect default production -top::Type ::= -{ - top.typeName = ""; -- We actually put a value here, since it's possible for us to request typeName of nonsensical things. -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.typeName = fn; -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.typeName = fn; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.typeName = te.typeName; -} - diff --git a/grammars/silver/definition/flow/ast/Flow.sv b/grammars/silver/definition/flow/ast/Flow.sv deleted file mode 100644 index 27ac9255d..000000000 --- a/grammars/silver/definition/flow/ast/Flow.sv +++ /dev/null @@ -1,432 +0,0 @@ -grammar silver:definition:flow:ast; - - -{-- - - Info for constructing production flow graphs. - - Follows Figure 5.12 (p138, pdf p154 of Kaminski's PhD) - - with notable additions of: - - - ntRef, specification (for dealing with 'flowtype' decls and the ref set) - - - implicitFwdAffects (for improving accuracy of inference) - - - extraEq (handling collections '<-') - - which the thesis does not address. - -} -nonterminal FlowDef with synTreeContribs, inhTreeContribs, defTreeContribs, fwdTreeContribs, fwdInhTreeContribs, prodTreeContribs, prodGraphContribs, flowEdges, refTreeContribs, localInhTreeContribs, suspectFlowEdges, hostSynTreeContribs, nonSuspectContribs, localTreeContribs, specContribs; -nonterminal FlowDefs with synTreeContribs, inhTreeContribs, defTreeContribs, fwdTreeContribs, fwdInhTreeContribs, prodTreeContribs, prodGraphContribs, refTreeContribs, localInhTreeContribs, hostSynTreeContribs, nonSuspectContribs, localTreeContribs, specContribs; - -{-- lookup (production, attribute) to find synthesized equations - - Used to ensure a necessary lhs.syn equation exists. - - Also decides whether to add a forward or default equation while computing flow types. -} -synthesized attribute synTreeContribs :: [Pair]; - -{-- lookup (production, sig, attribute) to find inherited equation - - Used to ensure a necessary rhs.inh equation exists. - - Also decides whether to add a copy equation for autocopy attributes to rhs elements. -} -synthesized attribute inhTreeContribs :: [Pair]; - -{-- lookup (nonterminal, attribute) to find default syn equations - - Used to obtain default equation dependencies, when it exists. -} -synthesized attribute defTreeContribs :: [Pair]; - -{-- lookup (production) to find forward equations. - - Decides whether default or forward equations should be added. -} -synthesized attribute fwdTreeContribs :: [Pair]; - -{-- lookup (production, attr) to find forward INHERITED equations - - Used to ensure equations for inherited attributes exist for all inh of a fwd. -} -synthesized attribute fwdInhTreeContribs :: [Pair]; - -{-- lookup (production, local, attr) to find local INHERITED equations. - - ONLY used to check whether an equation exists. -} -synthesized attribute localInhTreeContribs :: [Pair]; - -{-- lookup (production, local) to find the local equation -} -synthesized attribute localTreeContribs :: [Pair]; - -{-- lookup (nonterminal) to find all non-forwarding production. - - ONLY used to determine all productions that need an equation for a new attribute. -} -synthesized attribute prodTreeContribs :: [Pair]; - -{-- lookup (nonterminal) to find all inherited attributes in the host -} -synthesized attribute refTreeContribs :: [Pair]; - -{-- find all equations having to do DIRECTLY with a production - (directly meaning e.g. no default equations, even if they might - affect it) These FlowDefs MUST have a flowEdges for this production. -} -synthesized attribute prodGraphContribs :: [Pair]; - -{-- Edge lists from equations - - ONLY used to extract edges for a production graph from production-internal flowDefs. -} -synthesized attribute flowEdges :: [Pair]; - -{-- Like flowEdges, but these edges originate from equations that are not - - allowed to affect their OWN flow type. We must still track them because - - they may affect others' flow types. - - (e.g. extsyn = hostsyn; hostsyn = hostinh; need to reflect extsyn's dep on hostinh) -} -synthesized attribute suspectFlowEdges :: [Pair]; - -{-- A list of extension syn attr occurrences, subject to ft lower bounds. -} -synthesized attribute hostSynTreeContribs :: [Pair]; - -{-- A list of attributes for a production that are non-suspect -} -synthesized attribute nonSuspectContribs :: [Pair]; - -{-- Explicit flow type specificiations -} -synthesized attribute specContribs :: [Pair>]; - -abstract production consFlow -top::FlowDefs ::= h::FlowDef t::FlowDefs -{ - top.synTreeContribs = h.synTreeContribs ++ t.synTreeContribs; - top.inhTreeContribs = h.inhTreeContribs ++ t.inhTreeContribs; - top.defTreeContribs = h.defTreeContribs ++ t.defTreeContribs; - top.fwdTreeContribs = h.fwdTreeContribs ++ t.fwdTreeContribs; - top.fwdInhTreeContribs = h.fwdInhTreeContribs ++ t.fwdInhTreeContribs; - top.prodTreeContribs = h.prodTreeContribs ++ t.prodTreeContribs; - top.prodGraphContribs = h.prodGraphContribs ++ t.prodGraphContribs; - top.refTreeContribs = h.refTreeContribs ++ t.refTreeContribs; - top.localInhTreeContribs = h.localInhTreeContribs ++ t.localInhTreeContribs; - top.localTreeContribs = h.localTreeContribs ++ t.localTreeContribs; - top.hostSynTreeContribs = h.hostSynTreeContribs ++ t.hostSynTreeContribs; - top.nonSuspectContribs = h.nonSuspectContribs ++ t.nonSuspectContribs; - top.specContribs = h.specContribs ++ t.specContribs; -} - -abstract production nilFlow -top::FlowDefs ::= -{ - top.synTreeContribs = []; - top.inhTreeContribs = []; - top.defTreeContribs = []; - top.fwdTreeContribs = []; - top.fwdInhTreeContribs = []; - top.prodTreeContribs = []; - top.prodGraphContribs = []; - top.refTreeContribs = []; - top.localInhTreeContribs = []; - top.localTreeContribs = []; - top.hostSynTreeContribs = []; - top.nonSuspectContribs = []; - top.specContribs = []; -} - --- At the time of writing, this is one giant work in progress. --- Currently, all we're going to report is whether a synthesized --- equation EXISTS or whether a production forwards at all. --- This will be implemented in such a way that it returns the --- FlowDef, but presently that has no special information. - -aspect default production -top::FlowDef ::= -{ - top.synTreeContribs = []; - top.inhTreeContribs = []; - top.defTreeContribs = []; - top.fwdTreeContribs = []; - top.fwdInhTreeContribs = []; - top.prodTreeContribs = []; - top.refTreeContribs = []; - top.localInhTreeContribs = []; - top.localTreeContribs = []; - top.hostSynTreeContribs = []; - top.nonSuspectContribs = []; - top.specContribs = []; - top.suspectFlowEdges = []; -- flowEdges is required, but suspect is typically not! - -- require prodGraphContibs, flowEdges -} - -{-- - - Declaration of a NON-FORWARDING production. Exists to allow lookups of productions - - from nonterminal name. - - - - @param nt The full name of the nonterminal it constructs - - @param prod The full name of the production - -} -abstract production prodFlowDef -top::FlowDef ::= nt::String prod::String -{ - top.prodTreeContribs = [pair(nt, top)]; - top.prodGraphContribs = []; - top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); -} - -{-- - - Declaration that a synthesized attribute occurrence is in the host language - - (exported by the nonterminal) and therefore is *NOT* subject to the restriction - - for extensions synthesized attributes (i.e. must be ft(syn) >= ft(fwd)). - - - - @param nt the full name of the nonterminal - - @param attr the full name of the synthesized attribute - -} -abstract production hostSynFlowDef -top::FlowDef ::= nt::String attr::String -{ - top.hostSynTreeContribs = [pair(nt, top)]; - top.prodGraphContribs = []; - top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); -} - -{-- - - The definition of a default equation for a synthesized attribute on a nonterminal. - - - - @param nt the full name of the *nonterminal* - - @param attr the full name of the attribute - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production defaultSynEq -top::FlowDef ::= nt::String attr::String deps::[FlowVertex] -{ - top.defTreeContribs = [pair(crossnames(nt, attr), top)]; - top.prodGraphContribs = []; -- defaults don't show up in the prod graph!! - top.flowEdges = map(pair(lhsSynVertex(attr), _), deps); -- but their edges WILL end up added to graphs in fixup-phase!! -} - -{-- - - Declaration of the inherited attributes known from the host language - - - - @param nt The full name of a nonterminal declaration - - @param inhs The Blessed Ref Set, to be used for decorated nodes of this type. - -} -abstract production ntRefFlowDef -top::FlowDef ::= nt::String inhs::[String] -{ - top.refTreeContribs = [pair(nt, top)]; - top.prodGraphContribs = []; - top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); -} - -{-- - - Explicit specification of the flow type on a nonterminal for - - 1. A synthesized attributes - - 2. The forward - - 3. The reference set? - - - - @param nt the full name of the nonterminal - - @param attr the full name of the synthesized attribute (or "forward", or "decorate") - - @param inhs the full names of inherited attributes to use as the flow type - -} -abstract production specificationFlowDef -top::FlowDef ::= nt::String attr::String inhs::[String] -{ - top.specContribs = [pair(nt, pair(attr, inhs))]; - top.prodGraphContribs = []; - top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); -} - -{-- - - The definition of a synthesized attribute in a production. - - - - @param prod the full name of the production - - @param attr the full name of the attribute - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production synEq -top::FlowDef ::= prod::String attr::String deps::[FlowVertex] mayAffectFlowType::Boolean -{ - top.synTreeContribs = [pair(crossnames(prod, attr), top)]; - top.prodGraphContribs = [pair(prod, top)]; - local edges :: [Pair] = map(pair(lhsSynVertex(attr), _), deps); - top.flowEdges = if mayAffectFlowType then edges else []; - top.suspectFlowEdges = if mayAffectFlowType then [] else edges; -} - -{-- - - The definition of a inherited attribute for a signature element in a production. - - - - @param prod the full name of the production - - @param sigName the name of the RHS element - - @param attr the full name of the attribute - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production inhEq -top::FlowDef ::= prod::String sigName::String attr::String deps::[FlowVertex] -{ - top.inhTreeContribs = [pair(crossnames(prod, crossnames(sigName, attr)), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(rhsVertex(sigName, attr), _), deps); -} - -{-- - - The definition of the forward of a production. - - - - @param prod the full name of the production - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE *NOT* repeat *NOT* POSSIBLE - -} -abstract production fwdEq -top::FlowDef ::= prod::String deps::[FlowVertex] mayAffectFlowType::Boolean -{ - top.fwdTreeContribs = [pair(prod, top)]; - top.prodGraphContribs = [pair(prod, top)]; - local edges :: [Pair] = map(pair(forwardEqVertex(), _), deps); - top.flowEdges = if mayAffectFlowType then edges else []; - top.suspectFlowEdges = if mayAffectFlowType then [] else edges; -} - -{-- - - Attributes that are non-suspect. - - - - These are *allowed* to have their *implicit forward copy equations* affect the flow type. - - - - This is basically a hack to make flow type inference work a little better. - -} -abstract production implicitFwdAffects -top::FlowDef ::= prod::String attrs::[String] -{ - top.nonSuspectContribs = [pair(prod, attrs)]; - top.prodGraphContribs = []; - top.flowEdges = error("Internal compiler error: this sort of def should not be in a context where edges are requested."); -} - -{-- - - The definition of an inherited attribute on the forward - - - - @param prod the full name of the production - - @param attrName the full name of the inherited attribute given to the forward - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production fwdInhEq -top::FlowDef ::= prod::String attr::String deps::[FlowVertex] -{ - top.fwdInhTreeContribs = [pair(crossnames(prod, attr), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(forwardVertex(attr), _), deps); -} - -{-- - - The definition of a local or production attribute's equation. - - MAY not be a nonterminal type! - - - - @param prod the full name of the production - - @param fName the name of the local/production attribute - - @param typeName the full name of the type, or empty string if not a decorable type! - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production localEq -top::FlowDef ::= prod::String fName::String typeName::String deps::[FlowVertex] -{ - top.localTreeContribs = [pair(crossnames(prod, fName), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(localEqVertex(fName), _), deps); -} - -{-- - - The definition of an inherited attribute for a local attribute. - - - - @param prod the full name of the production - - @param fName the name of the local/production attribute - - @param attr the full name of the attribute - - @param deps the dependencies of this equation on other flow graph elements - - CONTRIBUTIONS ARE POSSIBLE - -} -abstract production localInhEq -top::FlowDef ::= prod::String fName::String attr::String deps::[FlowVertex] -{ - top.localInhTreeContribs = [pair(crossnames(prod, crossnames(fName, attr)), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(localVertex(fName, attr), _), deps); -} - -{-- - - Used for contributions to collections. Allows tacking on dependencies - - to vertices. - - - - @param prod the full name of the production - - @param src the vertex to add dependencies to - - @param deps the dependencies of this vertex - -} -abstract production extraEq -top::FlowDef ::= prod::String src::FlowVertex deps::[FlowVertex] mayAffectFlowType::Boolean -{ - top.prodGraphContribs = [pair(prod, top)]; - local edges :: [Pair] = map(pair(src, _), deps); - top.flowEdges = if mayAffectFlowType then edges else []; - top.suspectFlowEdges = if mayAffectFlowType then [] else edges; -} - -{-- - - The definition of an anonymous decoration site e.g. 'decorate with' - - - - @param prod the full name of the production - - @param fName the generated anonymous name for this decoration site - - @param typeName the full name of the nonterminal - - @param deps the dependencies of this equation on other flow graph elements - - (no contributions are possible) - -} -abstract production anonEq -top::FlowDef ::= prod::String fName::String typeName::String loc::Location deps::[FlowVertex] -{ - top.localTreeContribs = [pair(crossnames(prod, fName), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(anonEqVertex(fName), _), deps); -} - -{-- - - The definition of an inherited attribute for an anonymous decoration site. - - - - @param prod the full name of the production - - @param fName the generated anonymous name for this decoration site - - @param attr the full name of the attribute - - @param deps the dependencies of this equation on other flow graph elements - - (no contributions are possible) - -} -abstract production anonInhEq -top::FlowDef ::= prod::String fName::String attr::String deps::[FlowVertex] -{ - top.localInhTreeContribs = [pair(crossnames(prod, crossnames(fName, attr)), top)]; - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = map(pair(anonVertex(fName, attr), _), deps); -} - -{-- - - A match rule from a pattern matching equation. - - Matching against 'scrutinee' this rule matches 'matchProd'. It extracts the pattern variables in 'vars'. - - This info is used, not to directly create edges, but to inform which stitch points are needed. - -} -abstract production patternRuleEq -top::FlowDef ::= prod::String matchProd::String scrutinee::VertexType vars::[PatternVarProjection] -{ - top.prodGraphContribs = [pair(prod, top)]; - top.flowEdges = []; -} - - -nonterminal PatternVarProjection; - -abstract production patternVarProjection -top::PatternVarProjection ::= child::String typeName::String patternVar::String -{ -} - --- - -function crossnames -String ::= a::String b::String -{ - return a ++ " @ " ++ b; -} - --- - --- Used to get better error messages -function collectAnonOrigin -[Pair] ::= f::[FlowDef] -{ - return foldr(collectAnonOriginItem, [], f); -} -function collectAnonOriginItem -[Pair] ::= f::FlowDef rest::[Pair] -{ - return case f of - | anonEq(_, fN, _, l, _) -> - -- Small hack to improve error messages. Ignore anonEq's that come from patterns - if startsWith("__scrutinee", fN) - then rest - else pair(fN, l) :: rest - | _ -> rest - end; -} diff --git a/grammars/silver/definition/flow/ast/Vertex.sv b/grammars/silver/definition/flow/ast/Vertex.sv deleted file mode 100644 index 3b62c2908..000000000 --- a/grammars/silver/definition/flow/ast/Vertex.sv +++ /dev/null @@ -1,144 +0,0 @@ -grammar silver:definition:flow:ast; - -{-- - - Data structure representing vertices in the flow graph within a single production. - - - - See VertexType for some extra information organizing these vertexes somewhat. - -} -nonterminal FlowVertex with vertexComparisonKey; - --- It's kinda hard to implement a comparison of a type like this, so we serialize to string and compare that. -synthesized attribute vertexComparisonKey :: String; - -{-- - - A vertex representing a synthesized attribute on the nonterminal being constructed by this production. - - - - @param attrName the full name of a synthesized attribute on the lhs. - -} -abstract production lhsSynVertex -top::FlowVertex ::= attrName::String -{ - top.vertexComparisonKey = "lhsSynV(" ++ (attrName) ++ ")"; -} - -{-- - - A vertex representing an inherited attribute on the nonterminal being constructed by this production. - - - - Inherited attributes are separate for 'lhs' and not for 'rhs' because we care rather specially about lhsInh, - - as that's the bit that contributes to computing a flow type. - - - - @param attrName the full name of an inherited attribute on the lhs. - -} -abstract production lhsInhVertex -top::FlowVertex ::= attrName::String -{ - top.vertexComparisonKey = "lhsInhV(" ++ (attrName) ++ ")"; -} - --- TODO: we should do the above syn/inh separation for everything below too. - -{-- - - A vertex representing an attribute on an element of the signature RHS. - - - - @param sigName the name given to a signature nonterminal. - - @param attrName the full name of an attribute on that signature element. - -} -abstract production rhsVertex -top::FlowVertex ::= sigName::String attrName::String -{ - top.vertexComparisonKey = "rhsV(" ++ (sigName) ++ ", " ++ (attrName) ++ ")"; -} - -{-- - - A vertex representing a local equation. i.e. forward, local attribute, production - - attribute, etc. Note that this may be defined for MORE than just those with - - nonterminal type!! (e.g. local foo :: String will appear!) - - This is because the dependencies for these local equations still matter, of coursee. - - - - @param fName the full name of the NTA/FWD being defined - -} -abstract production localEqVertex -top::FlowVertex ::= fName::String -{ - top.vertexComparisonKey = "localEqV(" ++ (fName) ++ ")"; -} - -{-- - - A vertex representing an attribute on a local equation. i.e. forward, local - - attribute, production attribute, etc. Note this this implies the equation - - above IS a nonterminal type! - - - - @param fName the full name of the NTA/FWD - - @param attrName the full name of the attribute on that element - -} -abstract production localVertex -top::FlowVertex ::= fName::String attrName::String -{ - top.vertexComparisonKey = "localV(" ++ (fName) ++ ", " ++ (attrName) ++ ")"; -} - --- TODO: we should distinguish these! - --- The forward equation for this production. We do not care to distinguish it. -function forwardEqVertex -FlowVertex ::= -{ - return localEqVertex("forward"); -} - --- An attribute on the forward node for this production -function forwardVertex -FlowVertex ::= attrName::String -{ - return localVertex("forward", attrName); -} - -{-- - - A vertex representing an anonymous equation. i.e. a 'decorate e with..' - - expression, this production will represent 'e'. - - - - @param fName an anonymous name (typically generated with genInt) - -} -abstract production anonEqVertex -top::FlowVertex ::= fName::String -{ - top.vertexComparisonKey = "anonEqV(" ++ (fName) ++ ")"; -} - -{-- - - A vertex representing an attribute on an anonymous equation. - - e.g. 'decorate e with { a = d }' this represents 'e_nt.a's deps 'd'. - - - - @param fName the anonymous name - - @param attrName the full name of the attribute on that element - -} -abstract production anonVertex -top::FlowVertex ::= fName::String attrName::String -{ - top.vertexComparisonKey = "anonV(" ++ (fName) ++ ", " ++ (attrName) ++ ")"; -} - --------------------------------------------------------------------------------- - -function equalFlowVertex -Boolean ::= a::FlowVertex b::FlowVertex -{ - return case a, b of - | lhsSynVertex(a1), lhsSynVertex(a2) -> a1 == a2 - | lhsInhVertex(a1), lhsInhVertex(a2) -> a1 == a2 - | rhsVertex(s1, a1), rhsVertex(s2, a2) -> s1 == s2 && a1 == a2 - | localEqVertex(f1), localEqVertex(f2) -> f1 == f2 - | localVertex(f1, a1), localVertex(f2, a2) -> f1 == f2 && a1 == a2 - | anonEqVertex(f1), anonEqVertex(f2) -> f1 == f2 - | anonVertex(f1, a1), anonVertex(f2, a2) -> f1 == f2 && a1 == a2 - | _, _ -> false - end; -} - -function compareFlowVertex -Integer ::= a::FlowVertex b::FlowVertex -{ - return compareString(a.vertexComparisonKey, b.vertexComparisonKey); -} - - diff --git a/grammars/silver/definition/flow/driver/FlowTypes.sv b/grammars/silver/definition/flow/driver/FlowTypes.sv deleted file mode 100644 index 4de664db8..000000000 --- a/grammars/silver/definition/flow/driver/FlowTypes.sv +++ /dev/null @@ -1,214 +0,0 @@ -grammar silver:definition:flow:driver; - -imports silver:definition:core; -imports silver:definition:env; ---import silver:definition:flow:env; -imports silver:definition:flow:ast; -imports silver:analysis:warnings:flow only isOccursSynthesized, isAutocopy; - -imports silver:modification:autocopyattr; - -imports silver:util:raw:treemap as rtm; -imports silver:util:raw:graph as g; -imports silver:util:raw:treeset as set; - -import silver:util only rem; - --- Help some type signatures suck a little less -type ProdName = String; -type NtName = String; - --- from explicit specifications -function computeInitialFlowTypes -EnvTree ::= flowEnv::Decorated FlowDefs -{ - local specs :: [Pair]>] = - ntListCoalesce(groupBy(ntListEq, sortBy(ntListLte, flowEnv.specContribs))); - - return rtm:add(map(initialFlowType, specs), rtm:empty(compareString)); -} -function initialFlowType -Pair ::= x::Pair]> -{ - return pair(x.fst, g:add(flatMap(toFlatEdges, x.snd), g:empty(compareString))); -} -function ntListLte -Boolean ::= a::Pair b::Pair -{ - return a.fst <= b.fst; -} -function ntListEq -Boolean ::= a::Pair b::Pair -{ - return a.fst == b.fst; -} -function ntListCoalesce -[Pair]>] ::= l::[[Pair>]] -{ - return if null(l) then [] - else pair(head(head(l)).fst, map(snd, head(l))) :: ntListCoalesce(tail(l)); -} -function toFlatEdges -[Pair] ::= x::Pair -{ - return map(pair(x.fst, _), x.snd); -} - - -{-- - - Produces flow types for every nonterminal. - - Iterates until convergence. - -} -function fullySolveFlowTypes -Pair<[ProductionGraph] EnvTree> ::= - graphs::[ProductionGraph] - ntEnv::EnvTree -{ - -- Each iteration, we rebuild this... :/ - -- TODO: consider a 'mapValuesWithMapState' function :: 'k1, v1, map, map -> v1, k2, v2' - local prodEnv :: EnvTree = - directBuildTree(map(prodGraphToEnv, graphs)); - - local iter :: Pair>> = - solveFlowTypes(graphs, prodEnv, ntEnv); - - -- Just iterate until no new edges are added - return if !iter.fst then iter.snd - else fullySolveFlowTypes(iter.snd.fst, iter.snd.snd); -} - -{-- - - One iteration of solving flow type equations. Goes through each production once. - -} -function solveFlowTypes -Pair>> ::= - graphs::[ProductionGraph] - prodEnv::EnvTree - ntEnv::EnvTree -{ - local updatedGraph :: ProductionGraph = updateGraph(head(graphs), prodEnv, ntEnv); - - local currentFlowType :: FlowType = findFlowType(updatedGraph.lhsNt, ntEnv); - - -- The New Improved Flow Type - local synExpansion :: [Pair] = - map(expandVertexFilterTo(_, updatedGraph), updatedGraph.flowTypeVertexes); - - -- Find what edges are NEW NEW NEW - local brandNewEdges :: [Pair] = - findBrandNewEdges(synExpansion, currentFlowType); - - local newFlowType :: FlowType = - g:add(brandNewEdges, currentFlowType); - -- TODO: we could just always "add everything unconditionally" but we also need to know if there were - -- any new additions... so we'd need something added to graph to support that. - - local recurse :: Pair>> = - solveFlowTypes(tail(graphs), prodEnv, rtm:update(updatedGraph.lhsNt, [newFlowType], ntEnv)); - - return if null(graphs) then pair(false, pair([], ntEnv)) - else pair(!null(brandNewEdges) || recurse.fst, pair(updatedGraph :: recurse.snd.fst, recurse.snd.snd)); -} - - -function findBrandNewEdges -[Pair] ::= candidates::[Pair] currentFlowType::FlowType -{ - local syn :: String = head(candidates).fst; - local inhs :: [String] = head(candidates).snd; - - -- TODO: we might take '[Pair>]' insteadof [String] and gain speed? - local newinhs :: [String] = rem(inhs, set:toList(g:edgesFrom(syn, currentFlowType))); - - local newEdges :: [Pair] = map(pair(syn, _), newinhs); - - return if null(candidates) then [] else newEdges ++ findBrandNewEdges(tail(candidates), currentFlowType); -} - --- Expand 'ver' using 'graph', then filter down to just those in 'inhs' -function expandVertexFilterTo -Pair ::= ver::FlowVertex graph::ProductionGraph -{ - -- TODO: we might return 'Pair>' instead of [String] and gain speed? - -- Have set:filter, don't have "set:map" yet... (FlowVertex->String) - return pair(ver.flowTypeName, filterLhsInh(set:toList(graph.edgeMap(ver)))); -} - -{-- - - Filters vertexes down to just the names of inherited attributes on the LHS - -} -function filterLhsInh -[String] ::= f::[FlowVertex] -{ - return foldr(collectInhs, [], f); -} - -{-- - - Used to filter down to just the inherited attributes (on the LHS) - - - - @param f The flow vertex in question - - @param l The current set of inherited attribute dependencies - - @return {l} with {f} added to it - -} -function collectInhs -[String] ::= f::FlowVertex l::[String] -{ - return case f of - | lhsInhVertex(a) -> a::l - | _ -> l - end; -} - -function flowVertexEq -Boolean ::= a::FlowVertex b::FlowVertex -{ - -- eh, good enough TODO - return a.dotName == b.dotName; -} - - - - -{-- - - Flow type lookup names for vertices - -} -synthesized attribute flowTypeName :: String occurs on FlowVertex; - -aspect production lhsSynVertex -top::FlowVertex ::= attrName::String -{ - top.flowTypeName = attrName; -} -aspect production lhsInhVertex -top::FlowVertex ::= attrName::String -{ - top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for inherited attributes?"); -} -aspect production rhsVertex -top::FlowVertex ::= sigName::String attrName::String -{ - top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for child attributes?"); -} -aspect production localEqVertex -top::FlowVertex ::= fName::String -{ - top.flowTypeName = fName; -- secretly only ever "forward" when we actually demand flowTypeName -} -aspect production localVertex -top::FlowVertex ::= fName::String attrName::String -{ - top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for local inherited attributes?"); -} -aspect production anonEqVertex -top::FlowVertex ::= fName::String -{ - top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for anon equations?"); -} -aspect production anonVertex -top::FlowVertex ::= fName::String attrName::String -{ - top.flowTypeName = error("Internal compiler error: shouldn't be solving flow types for anon inherited attributes?"); -} - diff --git a/grammars/silver/definition/flow/env/Expr.sv b/grammars/silver/definition/flow/env/Expr.sv deleted file mode 100644 index 700642b90..000000000 --- a/grammars/silver/definition/flow/env/Expr.sv +++ /dev/null @@ -1,371 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:definition:type:syntax; -import silver:definition:type; -import silver:modification:copper; -import silver:modification:primitivepattern; -import silver:extension:patternmatching only Arrow_kwd, Vbar_kwd; -- TODO remove -import silver:modification:let_fix; - -{-- - - Direct (potential) dependencies this expression has on nodes in the production flow graph. - -} -monoid attribute flowDeps :: [FlowVertex] with [], ++; -{-- - - Determines whether this expression corresponds to a node in the flow graph, and how - - to treat it specially if so. - -} -synthesized attribute flowVertexInfo :: ExprVertexInfo; - --- flowDefs because expressions (decorate, patterns) can now generate stitchpoints -attribute flowDeps, flowDefs, flowEnv occurs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr; -attribute flowVertexInfo occurs on Expr; - -propagate flowDeps on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr - excluding - childReference, lhsReference, localReference, forwardReference, forwardAccess, synDecoratedAccessHandler, inhDecoratedAccessHandler, - decorateExprWith, newFunction, letp, lexicalLocalReference, matchPrimitiveReal; -propagate flowDefs on Expr, ExprInhs, ExprInh, Exprs, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr; - - -function inhsForTakingRef -[String] ::= nt::String flowEnv::Decorated FlowEnv -{ - return case getInhsForNtRef(nt, flowEnv) of - | ntRefFlowDef(_, inhs) :: _ -> inhs - | _ -> [] - end; -} - -function depsForTakingRef -[FlowVertex] ::= f::VertexType nt::String flowEnv::Decorated FlowEnv -{ - return map(f.inhVertex, inhsForTakingRef(nt, flowEnv)); -} - -aspect default production -top::Expr ::= -{ - -- We go with using default here because - -- (a) it's safe. vertexInfo is for being less conservative and more precise. - -- (b) only a few productions actually provide it. - top.flowVertexInfo = noVertex(); -} - -aspect production childReference -top::Expr ::= q::Decorated QName -{ - -- Note that q should find the actual type written in the signature, and so - -- isDecorable on that indeed tells us whether it's something autodecorated. - top.flowDeps := - if q.lookupValue.typerep.isDecorable && !performSubstitution(top.typerep, top.finalSubst).isDecorable - then depsForTakingRef(rhsVertexType(q.lookupValue.fullName), q.lookupValue.typerep.typeName, top.flowEnv) - else []; - top.flowVertexInfo = - if q.lookupValue.typerep.isDecorable && !performSubstitution(top.typerep, top.finalSubst).isDecorable - then hasVertex(rhsVertexType(q.lookupValue.fullName)) - else noVertex(); -} -aspect production lhsReference -top::Expr ::= q::Decorated QName -{ - -- Always a decorable type, so just check how it's being used: - top.flowDeps := - if !performSubstitution(top.typerep, top.finalSubst).isDecorable - then depsForTakingRef(lhsVertexType, q.lookupValue.typerep.typeName, top.flowEnv) - else []; - top.flowVertexInfo = - if !performSubstitution(top.typerep, top.finalSubst).isDecorable - then hasVertex(lhsVertexType) - else noVertex(); -} -aspect production localReference -top::Expr ::= q::Decorated QName -{ - -- Again, q give the actual type written. - top.flowDeps := [localEqVertex(q.lookupValue.fullName)] ++ - if q.lookupValue.typerep.isDecorable && !performSubstitution(top.typerep, top.finalSubst).isDecorable - then depsForTakingRef(localVertexType(q.lookupValue.fullName), q.lookupValue.typerep.typeName, top.flowEnv) - else []; - - top.flowVertexInfo = - if q.lookupValue.typerep.isDecorable && !performSubstitution(top.typerep, top.finalSubst).isDecorable - then hasVertex(localVertexType(q.lookupValue.fullName)) - else noVertex(); -} -aspect production forwardReference -top::Expr ::= q::Decorated QName -{ - -- Again, always a decorable type. - top.flowDeps := [forwardEqVertex()]++ - if !performSubstitution(top.typerep, top.finalSubst).isDecorable - then depsForTakingRef(forwardVertexType, q.lookupValue.typerep.typeName, top.flowEnv) - else []; - - top.flowVertexInfo = - if !performSubstitution(top.typerep, top.finalSubst).isDecorable - then hasVertex(forwardVertexType) - else noVertex(); -} - --- Still need these equations since propagate ignores decorated references -aspect production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ - top.flowDeps <- e.flowDeps ++ es.flowDeps ++ annos.flowDeps; - top.flowDefs <- e.flowDefs ++ es.flowDefs ++ annos.flowDefs; -} -aspect production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ - top.flowDeps <- e.flowDeps ++ es.flowDeps ++ annos.flowDeps; - top.flowDefs <- e.flowDefs ++ es.flowDefs ++ annos.flowDefs; -} - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - top.flowDeps := - case e.flowVertexInfo of - | hasVertex(vertex) -> vertex.fwdVertex :: vertex.eqVertex - | noVertex() -> e.flowDeps - end; -} - -aspect production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDefs <- e.flowDefs; -} --- Note that below we IGNORE the flow deps of the lhs if we know what it is --- this is because by default the lhs will have 'taking ref' flow deps (see above) -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDeps := - case e.flowVertexInfo of - | hasVertex(vertex) -> vertex.synVertex(q.attrDcl.fullName) :: vertex.eqVertex - | noVertex() -> e.flowDeps - end; - top.flowDefs <- e.flowDefs; -} -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDeps := - case e.flowVertexInfo of - | hasVertex(vertex) -> vertex.inhVertex(q.attrDcl.fullName) :: vertex.eqVertex - | noVertex() -> e.flowDeps - end; - top.flowDefs <- e.flowDefs; -} -aspect production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDeps <- []; -- errors, who cares? - top.flowDefs <- e.flowDefs; -} -aspect production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDeps <- e.flowDeps; - top.flowDefs <- e.flowDefs; -} -aspect production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.flowDeps <- e.flowDeps; - top.flowDefs <- e.flowDefs; -} - - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - -- The general theory: - -- ... some expr ... decorate EXP1 with { ... inhs ... } ... - -- is equivalent to: - -- local ANON :: EXP1.typerep = EXP1; - -- ANON.inhN = inhNexp; -- etc... - -- an the expr is now ... ANON ... - - -- We don't actually do this transform, of course, but that's what we're representing - -- this as to the flow analysis, and justifies all the choices below: - - -- First, generate our "anonymous" flow vertex name: - inh.decorationVertex = "__decorate" ++ toString(genInt()) ++ ":line" ++ toString(top.location.line); - - -- Next, emit the "local equation" for this anonymous flow vertex. - -- This means only the deps in 'e', see above conceptual transformation to see why. - -- N.B. 'inh.flowDefs' will emit 'localInhEq's for this anonymous flow vertex. - top.flowDefs <- - [anonEq(top.frame.fullName, inh.decorationVertex, performSubstitution(e.typerep, top.finalSubst).typeName, top.location, e.flowDeps)]; - - -- Now, we represent ourselves to anything that might use us specially - -- as though we were a reference to this anonymous local - top.flowVertexInfo = hasVertex(anonVertexType(inh.decorationVertex)); - - -- Finally, our standard flow deps mimic those of a local: "taking a reference" - -- This are of course ignored when treated specially. - top.flowDeps := [anonEqVertex(inh.decorationVertex)] ++ - depsForTakingRef(anonVertexType(inh.decorationVertex), performSubstitution(e.typerep, top.finalSubst).typeName, top.flowEnv); -} - -autocopy attribute decorationVertex :: String occurs on ExprInhs, ExprInh; - -aspect production exprInh -top::ExprInh ::= lhs::ExprLHSExpr '=' e1::Expr ';' -{ - top.flowDefs <- - if !null(lhs.errors) then [] else - case lhs of - | exprLhsExpr(q) -> [anonInhEq(top.frame.fullName, top.decorationVertex, q.attrDcl.fullName, e1.flowDeps)] - end; - -} - -aspect production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.flowDeps <- []; -- error, so who cares? - top.flowDefs <- e1.flowDefs ++ e2.flowDefs; -} -aspect production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.flowDeps <- e1.flowDeps ++ e2.flowDeps; - top.flowDefs <- e1.flowDefs ++ e2.flowDefs; -} - -aspect production exprRef -top::Expr ::= e::Decorated Expr -{ - -- This production is somewhat special, for example, error is := [] - -- That's because the errors should have already been appeared wherever it's anchored. - - -- But, here we DO pass flowDeps through because this affects wherever this expression - -- is used, not just where it appears. - - -- So definitely don't consider making this []! - - top.flowDeps <- e.flowDeps; - top.flowVertexInfo = e.flowVertexInfo; - top.flowDefs <- e.flowDefs; -- I guess? I haven't thought about this exactly. - -- i.e. whether this has already been included. shouldn't hurt to do so though. -} - - --- builtins - -aspect production stringLength -top::Expr ::= e::Decorated Expr -{ - top.flowDeps <- e.flowDeps; - top.flowDefs <- e.flowDefs; -} -aspect production errorLength -top::Expr ::= e::Decorated Expr -{ - top.flowDeps <- e.flowDeps; - top.flowDefs <- e.flowDefs; -} - -aspect production newFunction -top::Expr ::= 'new' '(' e1::Expr ')' -{ - -- Emit nothing except the keepDeps, for a vertex node - top.flowDeps := - case e1.flowVertexInfo of - | hasVertex(vertex) -> vertex.eqVertex - | noVertex() -> e1.flowDeps - end; -} - - --- FROM LET TODO -attribute flowDefs, flowEnv occurs on AssignExpr; -propagate flowDefs on AssignExpr; - -aspect production letp -top::Expr ::= la::AssignExpr e::Expr -{ - top.flowDeps := e.flowDeps; -} - -aspect production lexicalLocalReference -top::Expr ::= q::Decorated QName fi::ExprVertexInfo fd::[FlowVertex] -{ - -- Because of the auto-undecorate behavior, we need to check for the case - -- where `t` should be equivalent to `new(t)` and report accoringly. - - -- If we: - -- 1. Have a flow vertex - -- 2. Are a decorated type - -- 3. Used as undecorated type - -- Then: Suppress `fd` and report just `fi.eq` - - top.flowDeps := - case fi of - | hasVertex(vertex) -> - if performSubstitution(q.lookupValue.typerep, top.finalSubst).isDecorated && - !performSubstitution(top.typerep, top.finalSubst).isDecorated - then vertex.eqVertex -- we're a `t` emulating `new(t)` - else fd -- we're passing along our vertex-ness to the outer expression - | noVertex() -> fd -- we're actually being used as a ref-set-taking decorated var - end; - top.flowVertexInfo = fi; -} - - --- FROM PATTERN TODO -attribute flowDeps, flowDefs, flowEnv, scrutineeVertexType occurs on PrimPatterns, PrimPattern; -propagate flowDeps, flowDefs on PrimPatterns, PrimPattern; - -autocopy attribute scrutineeVertexType :: VertexType; - -aspect production matchPrimitiveReal -top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr -{ - -- If we take e.flowDeps ++ f.flowDeps, look them all up in the production - -- graph, and take the union, then filter down to just those on our anon vertex - -- we can discover what's needed, and use that to raise errors. - - -- We do have to do the lookups, though: we can't just use those Deps directly. - -- consider 'case e of prod(x) -> decorate x.syn with ...' - -- that introduces the use of 'x.syn' in a flowDef, and then emits the anonEq in flowDep - -- so we DO need to be transitive. Unfortunately. - - -- hack note: there's a test that depends on this name starting with __scrutinee. grep for it if you have to change this - local anonName :: String = "__scrutinee" ++ toString(genInt()) ++ ":line" ++ toString(e.location.line); - - pr.scrutineeVertexType = - case e.flowVertexInfo of - | hasVertex(vertex) -> vertex - | noVertex() -> anonVertexType(anonName) - end; - - -- Let's make sure for decorated types, we only demand what's necessary for forward - -- evaluation. - top.flowDeps := pr.flowDeps ++ f.flowDeps ++ - (pr.scrutineeVertexType.fwdVertex :: pr.scrutineeVertexType.eqVertex); - - top.flowDefs <- - case e.flowVertexInfo of - | hasVertex(vertex) -> [] - | noVertex() -> [anonEq(top.frame.fullName, anonName, performSubstitution(e.typerep, top.finalSubst).typeName, top.location, e.flowDeps)] - end; - -- We want to use anonEq here because that introduces the nonterminal stitch point for our vertex. -} - -aspect production prodPatternNormal -top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr -{ - top.flowDefs <- - [patternRuleEq(top.frame.fullName, qn.lookupValue.fullName, top.scrutineeVertexType, ns.flowProjections)]; -} -aspect production prodPatternGadt -top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr -{ - top.flowDefs <- - [patternRuleEq(top.frame.fullName, qn.lookupValue.fullName, top.scrutineeVertexType, ns.flowProjections)]; -} diff --git a/grammars/silver/definition/flow/env/FlowEnv.sv b/grammars/silver/definition/flow/env/FlowEnv.sv deleted file mode 100644 index 42e5540c6..000000000 --- a/grammars/silver/definition/flow/env/FlowEnv.sv +++ /dev/null @@ -1,154 +0,0 @@ -grammar silver:definition:flow:env; - -imports silver:definition:flow:ast; -imports silver:definition:env; -imports silver:definition:core; - - -autocopy attribute flowEnv :: Decorated FlowEnv; -monoid attribute flowDefs :: [FlowDef] with [], ++; - -nonterminal FlowEnv with synTree, inhTree, defTree, fwdTree, prodTree, fwdInhTree, refTree, localInhTree, localTree, nonSuspectTree, hostSynTree, specTree, prodGraphTree; - -inherited attribute synTree :: EnvTree; -inherited attribute inhTree :: EnvTree; -inherited attribute defTree :: EnvTree; -inherited attribute fwdTree :: EnvTree; -inherited attribute fwdInhTree :: EnvTree; -inherited attribute prodTree :: EnvTree; -inherited attribute refTree :: EnvTree; -inherited attribute localInhTree ::EnvTree; -inherited attribute localTree :: EnvTree; -inherited attribute nonSuspectTree :: EnvTree<[String]>; -inherited attribute hostSynTree :: EnvTree; -inherited attribute specTree :: EnvTree>; -inherited attribute prodGraphTree :: EnvTree; - -abstract production dummyFlowEnv -top::FlowEnv ::= -{ -} - -function fromFlowDefs -Decorated FlowEnv ::= d::FlowDefs -{ - production attribute e::FlowEnv; - e = dummyFlowEnv(); - e.synTree = directBuildTree(d.synTreeContribs); - e.inhTree = directBuildTree(d.inhTreeContribs); - e.defTree = directBuildTree(d.defTreeContribs); - e.fwdTree = directBuildTree(d.fwdTreeContribs); - e.fwdInhTree = directBuildTree(d.fwdInhTreeContribs); - e.prodTree = directBuildTree(d.prodTreeContribs); - e.refTree = directBuildTree(d.refTreeContribs); - e.localInhTree = directBuildTree(d.localInhTreeContribs); - e.localTree = directBuildTree(d.localTreeContribs); - e.nonSuspectTree = directBuildTree(d.nonSuspectContribs); - e.hostSynTree = directBuildTree(d.hostSynTreeContribs); - e.specTree = directBuildTree(d.specContribs); - e.prodGraphTree = directBuildTree(d.prodGraphContribs); - - return e; -} - - --- synthesized equation in a production -function lookupSyn -[FlowDef] ::= prod::String attr::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(prod, attr), e.synTree); -} - --- inherited equation for a child in a production -function lookupInh -[FlowDef] ::= prod::String sigName::String attr::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(prod, crossnames(sigName, attr)), e.inhTree); -} - --- default equation for a nonterminal -function lookupDef -[FlowDef] ::= nt::String attr::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(nt, attr), e.defTree); -} - --- forward equation for a production -function lookupFwd -[FlowDef] ::= prod::String e::Decorated FlowEnv -{ - return searchEnvTree(prod, e.fwdTree); -} - --- inherited equation for the forward in a production -function lookupFwdInh -[FlowDef] ::= prod::String attr::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(prod, attr), e.fwdInhTree); -} - --- inherited equation for a local in a production -function lookupLocalInh -[FlowDef] ::= prod::String fName::String attr::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(prod, crossnames(fName, attr)), e.localInhTree); -} - -function lookupLocalEq -[FlowDef] ::= prod::String fName::String e::Decorated FlowEnv -{ - return searchEnvTree(crossnames(prod, fName), e.localTree); -} - --- "blessed set" of inherited attribute required/assumed to exist for references -function getInhsForNtRef -[FlowDef] ::= nt::String e::Decorated FlowEnv -{ - return searchEnvTree(nt, e.refTree); -} - --- implicit forward syn copy equations that are allowed to affect the flow type -function getNonSuspectAttrsForProd -[String] ::= prod::String e::Decorated FlowEnv -{ - return concat(searchEnvTree(prod, e.nonSuspectTree)); -} - --- all (non-forwarding) productions constructing a nonterminal -function getNonforwardingProds -[String] ::= nt::String e::Decorated FlowEnv -{ - local extractProdName :: (String ::= FlowDef) = - \p::FlowDef -> case p of prodFlowDef(_, p) -> p end; - - return map(extractProdName, searchEnvTree(nt, e.prodTree)); -} - --- Ext Syns subject to ft lower bound -function getHostSynsFor -[String] ::= nt::String e::Decorated FlowEnv -{ - local extractHostSynName :: (String ::= FlowDef) = - \f::FlowDef -> case f of hostSynFlowDef(_, at) -> at end; - - return map(extractHostSynName, searchEnvTree(nt, e.hostSynTree)); -} - --- Get syns (and "forward") that have flow types specified -function getSpecifiedSynsForNt -[String] ::= nt::String e::Decorated FlowEnv -{ - return map(fst, searchEnvTree(nt, e.specTree)); -} -function getFlowTypeSpecFor -[Pair] ::= nt::String e::Decorated FlowEnv -{ - return searchEnvTree(nt, e.specTree); -} - -function getGraphContribsFor -[FlowDef] ::= prod::String e::Decorated FlowEnv -{ - return searchEnvTree(prod, e.prodGraphTree); -} - diff --git a/grammars/silver/definition/flow/env/FunctionDcl.sv b/grammars/silver/definition/flow/env/FunctionDcl.sv deleted file mode 100644 index c358315ff..000000000 --- a/grammars/silver/definition/flow/env/FunctionDcl.sv +++ /dev/null @@ -1,29 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:definition:flow:driver only ProductionGraph, FlowType, constructFunctionGraph; -import silver:driver:util only RootSpec; -- actually we just want the occurrences - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - {-- Used by core to send down with .frame -} - production myFlowGraph :: ProductionGraph = - constructFunctionGraph(namedSig, top.flowEnv, top.env, myProds, myFlow); -} - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - {-- Used by core to send down with .frame -} - production myFlowGraph :: ProductionGraph = - constructFunctionGraph(namedSig, top.flowEnv, top.env, myProds, myFlow); -} - diff --git a/grammars/silver/definition/flow/env/NonterminalDcl.sv b/grammars/silver/definition/flow/env/NonterminalDcl.sv deleted file mode 100644 index 585a1c44a..000000000 --- a/grammars/silver/definition/flow/env/NonterminalDcl.sv +++ /dev/null @@ -1,37 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:definition:type:syntax only BracketedOptTypeExprs; -import silver:driver:util only isStrictlyExportedBy; - -aspect production nonterminalDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - -- Normally the flow analysis consider options to be the same as exports. - -- Here, to avoid creating a hard dependency on options, we ignore options when - -- deciding the include things in the *inferred* ref set. (Thus, isStrictlyExportedBy.) - local inferredInhs :: [String] = - flatMap( - filterOccursForReferences(_, top.env, isStrictlyExportedBy(_, [top.grammarName], top.compiledGrammars)), - getAttrsOn(fName, top.env)); - - local specInhs :: Maybe<[String]> = - lookupBy(stringEq, "decorate", getFlowTypeSpecFor(fName, top.flowEnv)); - - -- Notice the circularity: flowDefs uses flowEnv. Works fine because only - -- the (lazy) parameter of ntRefFlowDef isn't computable until later. - - top.flowDefs <- [ntRefFlowDef(fName, fromMaybe(inferredInhs, specInhs))]; -} - --- If it is inherited and exported by this grammar (according to authority) -function filterOccursForReferences -[String] ::= occ::DclInfo e::Decorated Env authority::(Boolean ::= String) -{ - return case getAttrDcl(occ.attrOccurring, e) of - | at :: _ -> - if at.isInherited && authority(occ.sourceGrammar) - then [occ.attrOccurring] - else [] - | _ -> [] - end; -} diff --git a/grammars/silver/definition/flow/env/Occurs.sv b/grammars/silver/definition/flow/env/Occurs.sv deleted file mode 100644 index fab7ae615..000000000 --- a/grammars/silver/definition/flow/env/Occurs.sv +++ /dev/null @@ -1,20 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:util only contains; -import silver:definition:type:syntax; -import silver:driver:util only isExportedBy; - -aspect production attributionDcl -top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - local isHost :: Boolean = isExportedBy(top.grammarName, [nt.lookupType.dcl.sourceGrammar], top.compiledGrammars); - local isSyn :: Boolean = at.lookupAttribute.dcl.isSynthesized; - - -- We must be able to identify host-language synthesized attributes from the flow environment - top.flowDefs <- - if !at.lookupAttribute.found || !nt.lookupType.found || !isHost || !isSyn then - [] - else - [hostSynFlowDef(nt.lookupType.fullName, at.lookupAttribute.fullName)]; -} - diff --git a/grammars/silver/definition/flow/env/ProductionBody.sv b/grammars/silver/definition/flow/env/ProductionBody.sv deleted file mode 100644 index 87943ad94..000000000 --- a/grammars/silver/definition/flow/env/ProductionBody.sv +++ /dev/null @@ -1,206 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:definition:type:syntax; -import silver:modification:defaultattr; -import silver:modification:collection; -import silver:modification:copper; -import silver:util only contains; -import silver:driver:util only isExportedBy, RootSpec; - -attribute flowDefs, flowEnv occurs on ProductionBody, ProductionStmts, ProductionStmt, ForwardInhs, ForwardInh; -attribute flowEnv occurs on DefLHS; - -propagate flowDefs on ProductionBody, ProductionStmts, ProductionStmt, ForwardInhs, ForwardInh; - -{- A short note on how flowDefs are generated: - - - We ALWAYS produce the flowDef itself. This is necessary to catch missing or duplicate equations. - - We omit the dependencies if it appears in a location not permitted to affect the flow type. - This is to allow us to just compute flow types once, globally. --} - -{-- - - An occurs dcl info 's flow type can be affected here - -} -function isAffectable -Boolean ::= prodgram::String ntgram::String cg::EnvTree d::DclInfo -{ - return isExportedBy(prodgram, [ntgram, d.sourceGrammar], cg); -} - -aspect production forwardsTo -top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' -{ - local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); - - local mayAffectFlowType :: Boolean = - isExportedBy(top.grammarName, [ntDefGram], top.compiledGrammars); - - top.flowDefs <- [ - fwdEq(top.frame.fullName, e.flowDeps, mayAffectFlowType), - -- These are attributes that we know, here, occurs on this nonterminal. - -- The point is, these are the implicit equations we KNOW get generated, so - -- we regard these as non-suspect. That is, we implicitly insert these copy - -- equations here. - -- Currently, we don't bother to filter this to just synthesized, but we should? - implicitFwdAffects(top.frame.fullName, map((.attrOccurring), - filter(isAffectable(top.grammarName, ntDefGram, top.compiledGrammars, _), - getAttrsOn(top.frame.lhsNtName, top.env))))]; -} -aspect production forwardInh -top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' -{ - -- TODO: we need to figure out how to introduce any new lhsinh deps to the - -- forward flow type automatically. - top.flowDefs <- - case lhs of - | forwardLhsExpr(q) -> [fwdInhEq(top.frame.fullName, q.attrDcl.fullName, e.flowDeps)] - end; -} - -aspect production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); - - local srcGrams :: [String] = [ntDefGram, hackGramFromDcl(attr)]; - - local mayAffectFlowType :: Boolean = - isExportedBy(top.grammarName, srcGrams, top.compiledGrammars); - - top.flowDefs <- - if top.frame.hasPartialSignature then - [synEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps, mayAffectFlowType)] - else - [defaultSynEq(top.frame.lhsNtName, attr.attrDcl.fullName, e.flowDeps)]; -} -aspect production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.flowDefs <- - case dl of - | childDefLHS(q) -> [inhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] - | localDefLHS(q) -> [localInhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] - | forwardDefLHS(q) -> [fwdInhEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps)] - | _ -> [] -- TODO : this isn't quite extensible... more better way eventually, plz - end; -} - -aspect production localValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - -- TODO: So, I'm just going to assume for the moment that we're always allowed to define the eq for a local... - -- technically, it's possible to break this if you declare it in one grammar, but define it in another, but - -- I think we should forbid that syntactically, later on... - top.flowDefs <- - [localEq(top.frame.fullName, val.lookupValue.fullName, val.lookupValue.typerep.typeName, e.flowDeps)]; -} - --- FROM COLLECTIONS TODO - -aspect production synAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- <- -} e::Expr -{ - local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); - - local mayAffectFlowType :: Boolean = - isExportedBy(top.grammarName, [ntDefGram, hackGramFromDcl(attr)], top.compiledGrammars); - - top.flowDefs <- - [extraEq(top.frame.fullName, lhsSynVertex(attr.attrDcl.fullName), e.flowDeps, mayAffectFlowType)]; -} - -aspect production inhAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- <- -} e::Expr -{ - local vertex :: FlowVertex = - case dl of - | childDefLHS(q) -> rhsVertex(q.lookupValue.fullName, attr.attrDcl.fullName) - | localDefLHS(q) -> localVertex(q.lookupValue.fullName, attr.attrDcl.fullName) - | forwardDefLHS(q) -> forwardVertex(attr.attrDcl.fullName) - | _ -> localEqVertex("bogus:value:from:inhcontrib:flow") - end; - top.flowDefs <- - [extraEq(top.frame.fullName, vertex, e.flowDeps, true)]; -} -aspect production synBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - local ntDefGram :: String = hackGramFromFName(top.frame.lhsNtName); - - local srcGrams :: [String] = [ntDefGram, hackGramFromDcl(attr)]; - - local mayAffectFlowType :: Boolean = - isExportedBy(top.grammarName, srcGrams, top.compiledGrammars); - - top.flowDefs <- - if top.frame.hasPartialSignature then - [synEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps, mayAffectFlowType)] - else - [defaultSynEq(top.frame.lhsNtName, attr.attrDcl.fullName, e.flowDeps)]; -} -aspect production inhBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.flowDefs <- - case dl of - | childDefLHS(q) -> [inhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] - | localDefLHS(q) -> [localInhEq(top.frame.fullName, q.lookupValue.fullName, attr.attrDcl.fullName, e.flowDeps)] - | forwardDefLHS(q) -> [fwdInhEq(top.frame.fullName, attr.attrDcl.fullName, e.flowDeps)] - | _ -> [] -- TODO : this isn't quite extensible... more better way eventually, plz - end; -} - - -aspect production appendCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - local locDefGram :: String = hackGramFromQName(val.lookupValue); - -- TODO: possible bug? this would include ":local" in the gram wouldn't it? - - local mayAffectFlowType :: Boolean = - isExportedBy(top.grammarName, [locDefGram], top.compiledGrammars); - - -- TODO: So, locals that may affect flow types' suspect edges can NEVER have an effect - -- so we don't bother to even emit the extra equations in that case. - -- But, this means we might lose out on knowing there's a contribution here. - -- If we ever start using this information to locate contributions. - -- If we do, we'll have to come back here to add 'location' info anyway, - -- so if we do that, uhhh... fix this! Because you're here! Reading this! - - top.flowDefs <- - if mayAffectFlowType - then [extraEq(top.frame.fullName, localEqVertex(val.lookupValue.fullName), e.flowDeps, true)] - else []; -} - --- TODO: Copper ProductuionStmts - --- We're in the unfortunate position of HAVING to compute values for 'flowDefs' --- even if there are errors in the larger grammar, as remote errors in binding --- cannot be observed to suppress the analysis of flow. - --- It's not clear how to ideally fix this problem. What we do here is just report --- harmless junk if we can't determine a good value. - --- Source grammar of a nonterminal's fullName -function hackGramFromFName -String ::= s::String -{ - -- As a safety feature, rather than crash in this instance, report no known grammar - local i :: Integer = lastIndexOf(":", s); - return if i > 0 then substring(0, i, s) else ""; -} --- Source grammar of a lookup of an attribute occurrence dcl -function hackGramFromDcl -String ::= qn::Decorated QNameAttrOccur -{ - return if qn.found then qn.dcl.sourceGrammar else ""; -} --- Source grammar of a lookup of a local dcl -function hackGramFromQName -String ::= qn::Decorated QNameLookup -{ - return if qn.found then qn.dcl.sourceGrammar else ""; -} - diff --git a/grammars/silver/definition/flow/env/ProductionDcl.sv b/grammars/silver/definition/flow/env/ProductionDcl.sv deleted file mode 100644 index ae47a3f9c..000000000 --- a/grammars/silver/definition/flow/env/ProductionDcl.sv +++ /dev/null @@ -1,32 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:modification:defaultattr; -import silver:definition:flow:driver only ProductionGraph, findProductionGraph; -import silver:driver:util; -- only for productionFlowGraphs occurrence? - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - -- TODO: bit of a hack, isn't it? - local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - {-- Used by core to send down with .frame -} - production myFlowGraph :: ProductionGraph = - findProductionGraph(fName, myGraphs); - - top.flowDefs <- - if null(body.uniqueSignificantExpression) - then [prodFlowDef(namedSig.outputElement.typerep.typeName, fName)] - else []; -} - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - -- TODO: bit of a hack, isn't it? - local myGraphs :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - {-- Used by core to send down with .frame -} - production myFlowGraph :: ProductionGraph = - findProductionGraph(id.lookupValue.fullName, myGraphs); -} diff --git a/grammars/silver/definition/flow/env/Root.sv b/grammars/silver/definition/flow/env/Root.sv deleted file mode 100644 index 86fd7e85b..000000000 --- a/grammars/silver/definition/flow/env/Root.sv +++ /dev/null @@ -1,10 +0,0 @@ -grammar silver:definition:flow:env; - -attribute flowDefs, flowEnv occurs on Root, AGDcls, AGDcl, Grammar; -propagate flowDefs on Root, AGDcls, AGDcl, Grammar; - -aspect default production -top::AGDcl ::= -{ - top.flowDefs := []; -} diff --git a/grammars/silver/definition/flow/env/RootSpec.sv b/grammars/silver/definition/flow/env/RootSpec.sv deleted file mode 100644 index c45a65caf..000000000 --- a/grammars/silver/definition/flow/env/RootSpec.sv +++ /dev/null @@ -1,52 +0,0 @@ -grammar silver:definition:flow:env; - -import silver:driver:util; - -monoid attribute maybeFlowDefs::Maybe<[FlowDef]> with nothing(), orElse; -attribute maybeFlowDefs occurs on InterfaceItems, InterfaceItem; -propagate maybeFlowDefs on InterfaceItems; - -aspect production consInterfaceItem -top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems -{ - top.interfaceErrors <- if !top.maybeFlowDefs.isJust then ["Missing item flowDefs"] else []; -} - -aspect default production -top::InterfaceItem ::= -{ - propagate maybeFlowDefs; -} - -abstract production flowDefsInterfaceItem -top::InterfaceItem ::= val::[FlowDef] -{ - top.maybeFlowDefs := just(val); -} - -aspect function unparseRootSpec -String ::= r::Decorated RootSpec -{ - interfaceItems <- [flowDefsInterfaceItem(r.flowDefs)]; -} - -attribute flowDefs occurs on RootSpec; - -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - top.flowDefs := []; -} - -aspect production grammarRootSpec -top::RootSpec ::= g::Grammar _ _ _ _ -{ - top.flowDefs := g.flowDefs; -} - -aspect production interfaceRootSpec -top::RootSpec ::= i::InterfaceItems interfaceTime::Integer _ -{ - top.flowDefs := i.maybeFlowDefs.fromJust; -} - diff --git a/grammars/silver/definition/flow/syntax/FlowSpec.sv b/grammars/silver/definition/flow/syntax/FlowSpec.sv deleted file mode 100644 index bdd147a8d..000000000 --- a/grammars/silver/definition/flow/syntax/FlowSpec.sv +++ /dev/null @@ -1,268 +0,0 @@ -grammar silver:definition:flow:syntax; - -imports silver:definition:core; -imports silver:definition:flow:ast; -imports silver:definition:flow:driver only FlowType, inhDepsForSyn; -imports silver:definition:env; -imports silver:definition:type; -imports silver:driver:util only isExportedBy; -imports silver:util:raw:treeset as set; - --- unfortunate... -import silver:analysis:warnings only warnAll; -import silver:analysis:warnings:flow only warnMissingInh; - -terminal Flowtype 'flowtype' lexer classes {KEYWORD}; - -concrete production flowtypeDcl -top::AGDcl ::= 'flowtype' nt::QName '=' specs::FlowSpecs ';' -{ - top.unparse = "flowtype " ++ nt.unparse ++ " = " ++ specs.unparse ++ ";"; - top.errors := - if nt.lookupType.found - then specs.errors - else nt.lookupType.errors; - top.flowDefs := - if nt.lookupType.found - then specs.flowDefs - else []; - - -- This is only ever used for its name, really, so no need to freshen - specs.onNt = nt.lookupType.typerep; -} - -concrete production flowtypeAttrDcl -top::AGDcl ::= 'flowtype' attr::FlowSpec 'on' nts::NtList ';' -{ - top.unparse = "flowtype " ++ attr.unparse ++ " on " ++ nts.unparse ++ ";"; - top.errors := nts.errors; - top.flowDefs := nts.flowDefs; - - nts.flowSpecSpec = attr; -} - - -nonterminal FlowSpecs with config, location, grammarName, errors, env, unparse, onNt, flowDefs, compiledGrammars, flowEnv; - -propagate errors, flowDefs on FlowSpecs; - -concrete production oneFlowSpec -top::FlowSpecs ::= h::FlowSpec -{ - top.unparse = h.unparse; -} -concrete production snocFlowSpec -top::FlowSpecs ::= h::FlowSpecs ',' t::FlowSpec -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; -} - -nonterminal FlowSpec with config, location, grammarName, errors, env, unparse, onNt, flowDefs, compiledGrammars, flowEnv; - -autocopy attribute onNt :: Type; - -propagate errors on FlowSpec; - -concrete production flowSpecDcl -top::FlowSpec ::= attr::FlowSpecId '{' inhs::FlowSpecInhs '}' -{ - top.unparse = attr.unparse ++ " {" ++ inhs.unparse ++ "}"; - - top.errors <- - if !attr.found || - isExportedBy(top.grammarName, [attr.authorityGrammar], top.compiledGrammars) - then [] - else [err(attr.location, "flow type for " ++ attr.name ++ " must be exported by " ++ attr.authorityGrammar)]; - - top.errors <- - if attr.found && - length(filter(stringEq(attr.synName, _), getSpecifiedSynsForNt(top.onNt.typeName, top.flowEnv))) > 1 - then [err(attr.location, "duplicate specification of flow type for " ++ attr.name ++ " on " ++ top.onNt.typeName)] - else []; - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local missingFt :: [String] = - set:toList(set:removeAll(inhs.inhList, inhDepsForSyn("forward", top.onNt.typeName, myFlow))); - - top.errors <- - if !attr.found || - !(top.config.warnAll || top.config.warnMissingInh) || -- we don't want to compute flow graphs unless told to - isExportedBy(attr.authorityGrammar, [hackGramFromFName(top.onNt.typeName)], top.compiledGrammars) || - null(missingFt) - then [] - else [err(attr.location, attr.name ++ " is an extension synthesized attribute, and must contain at least the forward flow type. It is missing " ++ implode(", ", missingFt))]; - - -- We want to put the spec in even if there are errors in 'inhs' so that - -- we can look up specs from inhs. - top.flowDefs := - if !attr.found then [] - else [specificationFlowDef(top.onNt.typeName, attr.synName, inhs.inhList)]; -} - -nonterminal FlowSpecId with config, location, grammarName, errors, env, unparse, onNt, synName, authorityGrammar, found, name; - -synthesized attribute synName :: String; -synthesized attribute authorityGrammar :: String; - -propagate errors on FlowSpecId; - -concrete production qnameSpecId -top::FlowSpecId ::= syn::QNameAttrOccur -{ - top.name = syn.name; - top.unparse = syn.unparse; - top.synName = syn.attrDcl.fullName; - top.authorityGrammar = syn.dcl.sourceGrammar; - top.found = syn.found && syn.attrDcl.isSynthesized; - - syn.attrFor = top.onNt; - - top.errors <- - if !syn.found || syn.attrDcl.isSynthesized then [] - else [err(syn.location, syn.name ++ " is not a synthesized attribute, and so cannot have a flow type")]; -} - -concrete production forwardSpecId -top::FlowSpecId ::= 'forward' -{ - top.name = "forward"; - top.unparse = top.name; - top.synName = "forward"; - top.authorityGrammar = hackGramFromFName(top.onNt.typeName); - top.found = true; -} - -concrete production decorateSpecId -top::FlowSpecId ::= 'decorate' -{ - top.name = "decorate"; - top.unparse = top.name; - top.synName = "decorate"; - top.authorityGrammar = hackGramFromFName(top.onNt.typeName); - top.found = true; -} - - -nonterminal FlowSpecInhs with config, location, grammarName, errors, env, unparse, onNt, inhList, flowEnv; - -propagate errors on FlowSpecInhs; - -concrete production nilFlowSpecInhs -top::FlowSpecInhs ::= -{ - top.unparse = ""; - top.inhList = []; -} -concrete production oneFlowSpecInhs -top::FlowSpecInhs ::= h::FlowSpecInh -{ - top.unparse = h.unparse; - top.inhList = h.inhList; -} -concrete production consFlowSpecInhs -top::FlowSpecInhs ::= h::FlowSpecInh ',' t::FlowSpecInhs -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; - top.inhList = h.inhList ++ t.inhList; -} - -nonterminal FlowSpecInh with config, location, grammarName, errors, env, unparse, onNt, inhList, flowEnv; - -synthesized attribute inhList :: [String]; - -propagate errors on FlowSpecInh; - -concrete production flowSpecInh -top::FlowSpecInh ::= inh::QNameAttrOccur -{ - top.unparse = inh.unparse; - top.inhList = if inh.found then [inh.attrDcl.fullName] else []; - - inh.attrFor = top.onNt; - - top.errors <- - if !inh.found || inh.attrDcl.isInherited then [] - else [err(inh.location, inh.name ++ " is not an inherited attribute and so cannot be within a flow type")]; -} - -{-- - - Inherit a flow spec from another flow spec. - - - - 1. This is exclusively for other things given explicit specifications: - - (a) by design: we want things documented in the code. - - (b) because it dramatically simplifies the implementation. - - We can do everything here, and not have to worry about having - - to somehow make this work in the inference process. (Which would - - be kinda tricky: probably we'd need to remove this attribute - - from the normal 'infer' process EXCEPT on the phantom production, - - where we'd stash the info given here as edges...) - - 2. I'm only implementing 'decorate' and not 'forward'/syns. - - It's the only version demanded so far, let's wait until there's - - motivation to consider that extension. - -} -concrete production flowSpecDec -top::FlowSpecInh ::= 'decorate' -{ - top.unparse = "decorate"; - - local specs :: [Pair] = getFlowTypeSpecFor(top.onNt.typeName, top.flowEnv); - local decSpec :: Maybe<[String]> = lookupBy(stringEq, "decorate", specs); - - top.errors <- - if decSpec.isJust then [] - else [err(top.location, s"to use 'decorate' in a flow type for nonterminal ${top.onNt.typeName}, 'decorate' must also have an explicit flow type")]; - - top.inhList = fromMaybe([], decSpec); -} - - -nonterminal NtList with config, location, grammarName, errors, env, unparse, flowSpecSpec, flowDefs, compiledGrammars, flowEnv; - -propagate errors, flowDefs on NtList; - -concrete production nilNtList -top::NtList ::= -{ - top.unparse = ""; -} -concrete production oneNtList -top::NtList ::= h::NtName -{ - top.unparse = h.unparse; -} -concrete production consNtList -top::NtList ::= h::NtName ',' t::NtList -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; -} - -nonterminal NtName with config, location, grammarName, errors, env, unparse, flowSpecSpec, flowDefs, compiledGrammars, flowEnv; - -autocopy attribute flowSpecSpec :: FlowSpec; - -concrete production ntName -top::NtName ::= nt::QName -{ - top.unparse = nt.unparse; - top.errors := - if nt.lookupType.found - then myCopy.errors - else nt.lookupType.errors; - - top.flowDefs := - if nt.lookupType.found - then myCopy.flowDefs - else []; - - local myCopy :: FlowSpec = top.flowSpecSpec; - myCopy.config = top.config; - myCopy.grammarName = top.grammarName; - myCopy.env = top.env; - myCopy.compiledGrammars = top.compiledGrammars; - myCopy.flowEnv = top.flowEnv; - - -- This is only ever used for its name, really, so no need to freshen - myCopy.onNt = nt.lookupType.typerep; -} - diff --git a/grammars/silver/definition/regex/Regex.sv b/grammars/silver/definition/regex/Regex.sv deleted file mode 100644 index 5b4aeebfd..000000000 --- a/grammars/silver/definition/regex/Regex.sv +++ /dev/null @@ -1,249 +0,0 @@ -grammar silver:definition:regex; - -synthesized attribute regString :: String; - -lexer class Operator; -lexer class Escape; - -terminal Plus_t '+' lexer classes { Operator }; -terminal Kleene_t '*' lexer classes { Operator }; -terminal Optional_t '?' lexer classes { Operator }; -terminal Choice_t '|' lexer classes { Operator }; -terminal Range_t '-' lexer classes { Operator }; -terminal RegexNot_t '^' lexer classes { Operator }; -terminal RegexLBrack_t '[' lexer classes { Operator }; -terminal RegexRBrack_t ']' lexer classes { Operator }; -terminal RegexLParen_t '(' lexer classes { Operator }; -terminal RegexRParen_t ')' lexer classes { Operator }; -terminal RegexWildcard_t '.' lexer classes { Operator }; -terminal RegexChar_t /./ lexer classes { Escape }; -terminal EscapedChar_t /\\./ lexer classes { Escape }; - --- Disambiguate these, rather than using lexical precedence, --- so we can avoid superfluous escapes (e.g. /--.*/). --- This is the behavior of most regex libraries. -disambiguate RegexChar_t, Plus_t { pluck Plus_t; } -disambiguate RegexChar_t, Kleene_t { pluck Kleene_t; } -disambiguate RegexChar_t, Optional_t { pluck Optional_t; } -disambiguate RegexChar_t, Choice_t { pluck Choice_t; } -disambiguate RegexChar_t, Range_t { pluck Range_t; } -disambiguate RegexChar_t, RegexNot_t { pluck RegexNot_t; } -disambiguate RegexChar_t, RegexLBrack_t { pluck RegexLBrack_t; } -disambiguate RegexChar_t, RegexRBrack_t { pluck RegexRBrack_t; } -disambiguate RegexChar_t, RegexLParen_t { pluck RegexLParen_t; } -disambiguate RegexChar_t, RegexRParen_t { pluck RegexRParen_t; } -disambiguate RegexChar_t, RegexWildcard_t { pluck RegexWildcard_t; } - --- TODO: It might be wise to someday do a CST/AST split on this. - - -{-- - - A basic regular expression. - - - - At lowest precedence, a regex consists of a series of choices (a|b|c) - -} -nonterminal Regex with regString; - -concrete production regexEpsilon -top::Regex ::= -{ - top.regString = ""; -} - -concrete production regexSeq -top::Regex ::= h::RegexSeq -{ - top.regString = h.regString; -} - -concrete production regexChoice -top::Regex ::= h::RegexSeq '|' t::Regex -{ - top.regString = h.regString ++ "|" ++ t.regString; -} - - -{-- - - A sequence of regular expressions. - -} -nonterminal RegexSeq with regString; - -concrete production regexSeqSnoc -top::RegexSeq ::= h::RegexSeq t::RegexRepetition -{ - top.regString = h.regString ++ t.regString; -} - -concrete production regexSeqOne -top::RegexSeq ::= t::RegexRepetition -{ - top.regString = t.regString; -} - - -{-- - - A RegexItem with an optional repetition operator (*+?) - -} -nonterminal RegexRepetition with regString; - -concrete production regexKleene -top::RegexRepetition ::= i::RegexItem '*' -{ - top.regString = i.regString ++ "*"; -} - -concrete production regexPlus -top::RegexRepetition ::= i::RegexItem '+' -{ - top.regString = i.regString ++ "+"; -} - -concrete production regexOptional -top::RegexRepetition ::= i::RegexItem '?' -{ - top.regString = i.regString ++ "?"; -} - -concrete production regexOnce -top::RegexRepetition ::= i::RegexItem -{ - top.regString = i.regString; -} - - -{-- - - A single matched entity (char, wildcard, set, group) - -} -nonterminal RegexItem with regString; -- characters or sequences/sets - -concrete production regexCharItem -top::RegexItem ::= char::RegexChar -{ - top.regString = char.regString; -} - -concrete production regexWildcard -top::RegexItem ::= '.' -{ - top.regString = "."; -} - -concrete production regexSet -top::RegexItem ::= '[' g::RegexCharSet ']' -{ - top.regString = "[" ++ g.regString ++ "]"; -} - -concrete production regexSetInverted -top::RegexItem ::= '[' '^' g::RegexCharSet ']' -{ - top.regString = "[^" ++ g.regString ++ "]"; -} - -concrete production regexGroup -top::RegexItem ::= '(' r::Regex ')' -{ - top.regString = "(" ++ r.regString ++ ")"; -} - - -{-- - - A list of options or ranges within a regexSet. - -} -nonterminal RegexCharSet with regString; - -concrete production regexCharSetSnoc -top::RegexCharSet ::= h::RegexCharSet t::RegexCharSetItem -{ - top.regString = h.regString ++ t.regString; -} - -concrete production regexCharSetOne -top::RegexCharSet ::= t::RegexCharSetItem -{ - top.regString = t.regString; -} - - -{-- - - An option or range within a regexSet. - -} -nonterminal RegexCharSetItem with regString; - -concrete production regexSetChar -top::RegexCharSetItem ::= char::RegexChar -{ - top.regString = char.regString; -} - -concrete production regexSetRange -top::RegexCharSetItem ::= l::RegexChar '-' u::RegexChar -{ - top.regString = l.regString ++ "-" ++ u.regString; -} - - -{-- - - A character, escaped or otherwise. - -} -nonterminal RegexChar with regString; - -concrete production regexChar -top::RegexChar ::= char::RegexChar_t -{ - top.regString = char.lexeme; -} - -concrete production regexEscapedChar -top::RegexChar ::= esc::EscapedChar_t -{ - top.regString = esc.lexeme; -} - - ----- Helper functions - - -{-- - - Concatenates two regular expressions. - -} -function regexConcatenate -Regex ::= l::Regex r::Regex -{ - return regexSeq(concatRegexItems([regexGroup('(', l, ')'), regexGroup('(', r, ')')])); -} - -{-- - - Concatenates a list of RegexItems, must be non-empty. - -} -function concatRegexItems -RegexSeq ::= l::[RegexItem] -{ - return foldl(regexSeqSnoc, regexSeqOne(regexOnce(head(l))), map(regexOnce, tail(l))); -} - -{-- - - Converts a character to a RegexItem, escaping if necessary. - -} -function regexCharToItem -RegexItem ::= ch::String -{ - return regexCharItem( - if isAlpha(ch) || isDigit(ch) - then regexChar(terminal(RegexChar_t, ch)) - else regexEscapedChar(terminal(EscapedChar_t, "\\" ++ ch))); -} - -{-- - - Returns a regex that matches a string literal. - - (i.e. no interpretation of special characters.) - -} -function regexLiteral -Regex ::= s::String -{ - return if length(s) == 0 - then regexEpsilon() - else regexSeq(concatRegexItems(map(regexCharToItem, explode("", s)))); -} - - diff --git a/grammars/silver/definition/type/Helpers.sv b/grammars/silver/definition/type/Helpers.sv deleted file mode 100644 index ace197ccc..000000000 --- a/grammars/silver/definition/type/Helpers.sv +++ /dev/null @@ -1,34 +0,0 @@ -grammar silver:definition:type; - --- set contains: s 'in' sl -function containsTyVar -Boolean ::= s::TyVar sl::[TyVar] -{ - return (!null(sl)) && (tyVarEqual(s, head(sl)) || containsTyVar(s, tail(sl))); -} - --- set difference: startswith - toremove -function removeTyVars -[TyVar] ::= startswith::[TyVar] toremove::[TyVar] -{ - return if null(startswith) then [] - else if containsTyVar(head(startswith), toremove) - then removeTyVars(tail(startswith), toremove) - else cons(head(startswith), removeTyVars(tail(startswith), toremove)); -} - --- HUGE LIE: This is not a set. Well, maybe. We *might* depend on this being ordered. -function setUnionTyVars -[TyVar] ::= a::[TyVar] b::[TyVar] -{ - return a ++ removeTyVars(b, a); -} -function setUnionTyVarsAll -[TyVar] ::= tvs::[[TyVar]] -{ - return if null(tvs) - then [] - else if null(tail(tvs)) - then head(tvs) - else setUnionTyVars( head(tvs), setUnionTyVarsAll(tail(tvs))); -} diff --git a/grammars/silver/definition/type/Legacy.sv b/grammars/silver/definition/type/Legacy.sv deleted file mode 100644 index 4734f6382..000000000 --- a/grammars/silver/definition/type/Legacy.sv +++ /dev/null @@ -1,122 +0,0 @@ -grammar silver:definition:type; - --- DEPRECATED STUFF -attribute isError, inputTypes, outputType, namedTypes, isDecorated, isDecorable, isTerminal, decoratedType, unifyInstanceNonterminal, unifyInstanceDecorated occurs on Type; - --- Quick check to see if an error message should be suppressed -synthesized attribute isError :: Boolean; - --- exists because we want to access both these and pattern matching can only extract one thing at a time (so far) -synthesized attribute inputTypes :: [Type]; -synthesized attribute outputType :: Type; -synthesized attribute namedTypes :: [NamedArgType]; - --- Used by Expr, could possibly be replaced by pattern matching for decoratedType --- Also used by 'new()' -synthesized attribute isDecorated :: Boolean; - --- Determines whether a type is automatically promoted to a decorated type --- and whether a type may be supplied with inherited attributes. --- Used by expression (id refs), decorate type checking, and translations. -synthesized attribute isDecorable :: Boolean; - --- Used for type checking by 'terminal()' -synthesized attribute isTerminal :: Boolean; - --- Used by 'new' and type-determination for attributes (NOT on regular nonterminals) -synthesized attribute decoratedType :: Type; - --- Used instead of unify() when we want to just know its decorated or undecorated -synthesized attribute unifyInstanceNonterminal :: Substitution; -synthesized attribute unifyInstanceDecorated :: Substitution; - -aspect default production -top::Type ::= -{ - top.inputTypes = []; - top.outputType = errorType(); - top.namedTypes = []; - - top.isDecorated = false; - top.isDecorable = false; - top.isTerminal = false; - top.isError = false; - - top.decoratedType = errorType(); - - top.unifyInstanceNonterminal = errorSubst("not nt"); - top.unifyInstanceDecorated = errorSubst("not dec"); -} - -aspect production varType -top::Type ::= tv::TyVar -{ -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ -} - -aspect production errorType -top::Type ::= -{ - top.isError = true; -} - -aspect production intType -top::Type ::= -{ -} - -aspect production boolType -top::Type ::= -{ -} - -aspect production floatType -top::Type ::= -{ -} - -aspect production stringType -top::Type ::= -{ -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.isDecorable = true; - top.unifyInstanceNonterminal = emptySubst(); -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.isTerminal = true; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.isDecorated = true; - top.decoratedType = te; - top.unifyInstanceDecorated = emptySubst(); -} - -aspect production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ - top.unifyInstanceNonterminal = unify(hidden, nt); - top.unifyInstanceDecorated = unify(hidden, decoratedType(nt)); -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.inputTypes = params; - top.outputType = out; - top.namedTypes = namedParams; -} - diff --git a/grammars/silver/definition/type/PrettyPrinting.sv b/grammars/silver/definition/type/PrettyPrinting.sv deleted file mode 100644 index a731975b3..000000000 --- a/grammars/silver/definition/type/PrettyPrinting.sv +++ /dev/null @@ -1,140 +0,0 @@ -grammar silver:definition:type; - -import silver:util; - -synthesized attribute typepp :: String occurs on Type; -autocopy attribute boundVariables :: [TyVar] occurs on Type; - -function prettyType -String ::= te::Type -{ - te.boundVariables = te.freeVariables; - return te.typepp; -} - -function prettyTypeWith -String ::= te::Type tvs::[TyVar] -{ - te.boundVariables = tvs; - return te.typepp; -} --------------------------------------------------------------------------------- -aspect production varType -top::Type ::= tv::TyVar -{ - top.typepp = findAbbrevFor(tv, top.boundVariables); -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ - top.typepp = findAbbrevFor(tv, top.boundVariables); -} - -aspect production errorType -top::Type ::= -{ - top.typepp = ""; -- probably shouldn't ever get printed? -} - -aspect production intType -top::Type ::= -{ - top.typepp = "Integer"; -} - -aspect production boolType -top::Type ::= -{ - top.typepp = "Boolean"; -} - -aspect production floatType -top::Type ::= -{ - top.typepp = "Float"; -} - -aspect production stringType -top::Type ::= -{ - top.typepp = "String"; -} - -aspect production terminalIdType -top::Type ::= -{ - top.typepp = "TerminalId"; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.typepp = fn ++ if !null(params) then "<" ++ implode(" ", mapTypePP(params, top.boundVariables)) ++ ">" else ""; -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.typepp = fn; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.typepp = "Decorated " ++ te.typepp; -} - -aspect production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ --- Sometimes useful for debugging. --- top.typepp = "Undecorable " ++ nt.typepp ++ "{" ++ prettyTypeWith(hidden, []) ++ "}"; -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.typepp = "(" ++ out.typepp ++ " ::= " ++ implode(" ", mapTypePP(params, top.boundVariables)) ++ - (if null(namedParams) then ")" else mapNamedPP(namedParams, top.boundVariables) ++ ")"); -} - --------------------------------------------------------------------------------- -function findAbbrevFor -String ::= tv::TyVar bv::[TyVar] -{ - return findAbbrevHelp(tv, bv, ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p"]); -} - -function findAbbrevHelp -String ::= tv::TyVar bv::[TyVar] vn::[String] -{ - local attribute tvi :: Integer; - tvi = case tv of tyVar(i) -> i end; - - return if null(vn) || null(bv) then "V_" ++ toString(tvi) - else if tyVarEqual(tv, head(bv)) - then head(vn) - else findAbbrevHelp(tv, tail(bv), tail(vn)); -} - --- TODO: oh crap is this stupid -function mapTypePP -[String] ::= tes::[Type] bv::[TyVar] -{ - local fst :: Type = head(tes); - fst.boundVariables = bv; - - return if null(tes) then [] - else fst.typepp :: mapTypePP(tail(tes), bv); -} --- This is crummy: -function mapNamedPP -String ::= tes::[NamedArgType] bv::[TyVar] -{ - local fst :: NamedArgType = head(tes); - fst.boundVariables = bv; - - return if null(tes) then "" - else fst.typepp ++ mapNamedPP(tail(tes), bv); -} diff --git a/grammars/silver/definition/type/Substitutions.sv b/grammars/silver/definition/type/Substitutions.sv deleted file mode 100644 index a33205030..000000000 --- a/grammars/silver/definition/type/Substitutions.sv +++ /dev/null @@ -1,342 +0,0 @@ -grammar silver:definition:type; - -{-- - - A representation of: (1) tyvar->type substitutions. (2) success/failure of unification - - - - History note: This code was written before Silver had polymorphism (it is - - what gave Silver polymorphism!) and so is incredibly archaic. - - - - TODO: Separate success/failure of unification from this type. - - Consider unify returning Maybe or Pair depending. - - TODO: More efficient type representations than a assoc-list, somehow. - -} -nonterminal Substitution with substList, substErrors, failure; - -synthesized attribute substList :: [Pair]; -synthesized attribute substErrors :: [String]; -synthesized attribute failure :: Boolean; -- this is a bad hack to work around unify being unable to return a pair - --------------------------------------------------------------------------------- - -abstract production goodSubst -top::Substitution ::= sublst::[Pair] -{ - top.substList = sublst; - top.substErrors = []; - top.failure = false; -} - -abstract production badSubst -top::Substitution ::= sublst::[Pair] errs::[String] -{ - top.substList = sublst; - top.substErrors = errs; - top.failure = true; -} - -function emptySubst -Substitution ::= -{ - return goodSubst([]); -} -function errorSubst -Substitution ::= e::String -{ - return badSubst([], [e]); -} -function subst -Substitution ::= tv::TyVar te::Type -{ - return goodSubst([pair(tv,te)]); -} -function composeSubst -Substitution ::= s1::Substitution s2::Substitution -{ - return case s1, s2 of - | goodSubst(s1l), goodSubst(s2l) -> goodSubst(s1l ++ s2l) - | goodSubst(s1l), badSubst(s2l, s2e) -> badSubst(s1l ++ s2l, s2e) - | badSubst(s1l, s1e), goodSubst(s2l) -> badSubst(s1l ++ s2l, s1e) - | badSubst(s1l, s1e), badSubst(s2l, s2e) -> badSubst(s1l ++ s2l, s1e ++ s2e) - end; -} - -function ignoreFailure -Substitution ::= s::Substitution -{ - return case s of - | goodSubst(_) -> s - | badSubst(sl,_) -> goodSubst(sl) - end; -} - --------------------------------------------------------------------------------- - -function findSubst -Maybe ::= tv::TyVar s::Substitution -{ - return lookupBy(tyVarEqual, tv, s.substList); -} - --------------------------------------------------------------------------------- - --- These are for ordinary tyvar substitutions. -autocopy attribute substitution :: Substitution occurs on Type; -synthesized attribute substituted :: Type occurs on Type; --- These are for flat, non-recursive replacement of tyvars with something else directly -synthesized attribute flatRenamed :: Type occurs on Type; - -aspect production varType -top::Type ::= tv::TyVar -{ - -- Important: we recursively substitute, until no more substitutions happen! - -- This also means the substitution list must not be circular! - - -- Perform one iteration of substitution - local partialsubst :: Maybe = findSubst(tv, top.substitution); - - -- recursively substitute only if we changed! - top.substituted = - if partialsubst.isJust - then performSubstitution(partialsubst.fromJust, top.substitution) - else top; - top.flatRenamed = - if partialsubst.isJust - then partialsubst.fromJust - else top; -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ - -- This may be counter intuitive! I don't know! - - -- I'm allowing Skolem constants to be subtituted for. - -- Now, the "real" behavior of Skolem constants is all in unification: - -- there, they behave as you would expect. However, once we quantify over the - -- "Skolem constant type variables", they should sort of go back to behaving - -- like ordinary type variables. So to get this behavior, we allow them to be - -- substituted. - - -- The only way we can construct a substitution for one though is by non-unification - -- means. (And there's only one way to do that: by quantifying over it.) - - -- (See the only non-unification place where subst(...) is called directly at the bottom of this file.) - - local partialsubst :: Maybe = findSubst(tv, top.substitution); - - -- recursively substitute only if we changed! - top.substituted = - if partialsubst.isJust - then performSubstitution(partialsubst.fromJust, top.substitution) - else top; - top.flatRenamed = - if partialsubst.isJust - then partialsubst.fromJust - else top; -} - -aspect production errorType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production intType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production boolType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production floatType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production stringType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production terminalIdType -top::Type ::= -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.substituted = nonterminalType(fn, mapSubst(params, top.substitution)); - top.flatRenamed = nonterminalType(fn, mapRenameSubst(params, top.substitution)); -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.substituted = top; - top.flatRenamed = top; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.substituted = decoratedType(te.substituted); - top.flatRenamed = decoratedType(te.flatRenamed); -} - -aspect production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ - -- We rely very carefully on eliminating ourselves once we've specialized! - -- Note: we're matching on hidden.subsituted, not just hidden. Important! - top.substituted = - case hidden.substituted of - | varType(_) -> ntOrDecType(nt.substituted, hidden.substituted) - | _ -> hidden.substituted - end; - -- For a renaming, we don't need to specialize. - top.flatRenamed = ntOrDecType(nt.flatRenamed, hidden.flatRenamed); -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.substituted = - functionType( - out.substituted, - mapSubst(params, top.substitution), - mapNamedSubst(namedParams, top.substitution)); - top.flatRenamed = - functionType( - out.flatRenamed, - mapRenameSubst(params, top.substitution), - mapNamedRenameSubst(namedParams, top.substitution)); -} - --------------------------------------------------------------------------------- - -function performSubstitution -Type ::= te::Type s::Substitution -{ - te.substitution = s; - return te.substituted; -} - -function mapSubst -[Type] ::= tes::[Type] s::Substitution -{ - return map(performSubstitution(_, s), tes); -} - -function mapNamedSubst -[NamedArgType] ::= np::[NamedArgType] s::Substitution -{ - return map(mapNamedArgType(performSubstitution(_, s), _), np); -} - ----- - -function performRenaming -Type ::= te::Type s::Substitution -{ - te.substitution = s; - return te.flatRenamed; -} - -function mapRenameSubst -[Type] ::= tes::[Type] s::Substitution -{ - return map(performRenaming(_, s), tes); -} - -function mapNamedRenameSubst -[NamedArgType] ::= np::[NamedArgType] s::Substitution -{ - return map(mapNamedArgType(performRenaming(_, s), _), np); -} - ----- - -function mapNamedArgType -NamedArgType ::= f::(Type ::= Type) nat::NamedArgType -{ - return case nat of - | namedArgType(n, t) -> namedArgType(n, f(t)) - end; -} - --------------------------------------------------------------------------------- - -function freshTyVars -[TyVar] ::= n::Integer -{ - return if n > 0 then freshTyVar() :: freshTyVars(n-1) - else []; -} - -function zipVarsIntoSubstitution -Substitution ::= original::[TyVar] sub::[TyVar] -{ - -- once we have "productions are subtypes of functions" then make this just map 'varType' and call the other one below - return if null(original) || null(sub) then emptySubst() - else composeSubst(subst(head(original), varType(head(sub))), - zipVarsIntoSubstitution(tail(original), tail(sub))); -} - -function zipVarsIntoSkolemizedSubstitution -Substitution ::= original::[TyVar] sub::[TyVar] -{ - -- once we have "productions are subtypes of functions" then make this just map 'varType' and call the other one below - return if null(original) || null(sub) then emptySubst() - else composeSubst(subst(head(original), skolemType(head(sub))), - zipVarsIntoSkolemizedSubstitution(tail(original), tail(sub))); -} - - -function zipVarsAndTypesIntoSubstitution -Substitution ::= original::[TyVar] sub::[Type] -{ - return if null(original) || null(sub) then emptySubst() - else composeSubst(subst(head(original), head(sub)), - zipVarsAndTypesIntoSubstitution(tail(original), tail(sub))); -} - -function freshenType -Type ::= te::Type tvs::[TyVar] -{ - return freshenTypeWith(te, tvs, freshTyVars(length(tvs))); -} - -function freshenTypeWith -Type ::= te::Type tvs::[TyVar] ntvs::[TyVar] -{ - -- Freshening just straight replaces variables, not deeply substituting them. - return performRenaming(te, zipVarsIntoSubstitution(tvs, ntvs)); -} - --- This function is an artifact of the fact that we ONLY do generalization at the top level, so we don't have (un)bound variables. -function freshenCompletely -Type ::= te::Type -{ - return freshenType(te, te.freeVariables); -} - -function errorSubstitution -Substitution ::= t::Type -{ - return zipVarsAndTypesIntoSubstitution(t.freeVariables, repeat(errorType(), length(t.freeVariables))); -} diff --git a/grammars/silver/definition/type/Type.sv b/grammars/silver/definition/type/Type.sv deleted file mode 100644 index 219e2e7d3..000000000 --- a/grammars/silver/definition/type/Type.sv +++ /dev/null @@ -1,233 +0,0 @@ -grammar silver:definition:type; - -option silver:modification:ffi; -- foreign types - -{-- - - Silver Type Representations. - -} -nonterminal Type with freeVariables; - -synthesized attribute freeVariables :: [TyVar]; - -{-- - - This is a (universally quantified) type variable. - -} -abstract production varType -top::Type ::= tv::TyVar -{ - top.freeVariables = [tv]; -} - -{-- - - This is an (existentially quantified) type variable, i.e. skolem constant. - - Type are pretty much (exists sks. forall tys. type) - -} -abstract production skolemType -top::Type ::= tv::TyVar -{ - top.freeVariables = [tv]; -} - -{-- - - When an error message has **already** been reported, and we must supply a type, - - and we wish to suppress further error messages, use errorType. - -} -abstract production errorType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - Integer type. - -} -abstract production intType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - Boolean type. - -} -abstract production boolType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - Float type. - -} -abstract production floatType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - String type. - -} -abstract production stringType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - Terminal identifier type. - - This isn't a foreign type, since we want equality checking. - - TODO: Revisit this once we have type classes. - -} -abstract production terminalIdType -top::Type ::= -{ - top.freeVariables = []; -} - -{-- - - An (undecorated) nonterminal type. - - @param fn The fully qualified name of the nonterminal. - - @param params The type parameters for that nonterminal. - -} -abstract production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.freeVariables = setUnionTyVarsAll(map((.freeVariables), params)); -} - -{-- - - A terminal type. - - @param fn The fully qualified name of the terminal. - -} -abstract production terminalType -top::Type ::= fn::String -{ - top.freeVariables = []; -} - -{-- - - A *decorated* nonterminal type. - - @param te MUST be a 'nonterminalType' (TODO: should probably just put that here) - -} -abstract production decoratedType -top::Type ::= te::Type -{ - top.freeVariables = te.freeVariables; -} - -{-- - - An intermediate type. This *should* never appear as the type of a symbol, - - etc. Rather, this is a helper type only used within expressions. - - - - It represents a nonterminal that is *either* decorated or undecorated - - (e.g. when referencing a child) but has not yet been specialized. - - @param nt MUST be a 'nonterminalType' - - @param hidden One of: (a) a type variable (b) 'nt' (c) 'decoratedType(nt)' - - representing state: unspecialized, undecorated, or decorated. - -} - --- This will ONLY appear in the types of expressions, nowhere else! -abstract production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ - top.freeVariables = case hidden of - | varType(_) -> nt.freeVariables - | _ -> hidden.freeVariables - end; - - -- If we never specialize, we're decorated. - forwards to decoratedType(nt); -} - -{-- - - Function type. (Whether production or function.) - - @param out The result type of the function - - @param params The (ordered) input types of the function - - @param namedParams Named parameters for this nonterminal. - - NOTE: These must always be *IN SORTED ORDER* - -} -abstract production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.freeVariables = setUnionTyVarsAll(map((.freeVariables), - out :: params ++ map((.argType), namedParams))); -} - --------------------------------------------------------------------------------- - -nonterminal NamedArgType with argName, argType, typepp, boundVariables; - -synthesized attribute argName :: String; -synthesized attribute argType :: Type; - -abstract production namedArgType -top::NamedArgType ::= s::String ty::Type -{ - top.typepp = "; " ++ s ++ "::" ++ ty.typepp; - top.argName = s; - top.argType = ty; -} - -function namedArgTypeLte -Boolean ::= a::NamedArgType b::NamedArgType -{ - return a.argName <= b.argName; -} - -function extractNamedArg -Pair [NamedArgType]> ::= n::String l::[NamedArgType] -{ - local recurse :: Pair [NamedArgType]> = - extractNamedArg(n, tail(l)); - - return if null(l) then pair(nothing(), []) - else if head(l).argName == n then pair(just(head(l)), tail(l)) - else pair(recurse.fst, head(l) :: recurse.snd); -} - -function findNamedArgType -Integer ::= s::String l::[NamedArgType] z::Integer -{ - return if null(l) then -1 - else if s == head(l).argName then z - else findNamedArgType(s, tail(l), z+1); -} - --------------------------------------------------------------------------------- - -nonterminal TyVar ; - --- In essence, this should be 'private' to this file. -synthesized attribute extractTyVarRep :: Integer occurs on TyVar; - -abstract production tyVar -top::TyVar ::= i::Integer -{ - top.extractTyVarRep = i; -} - -function freshTyVar -TyVar ::= -{ - return tyVar(genInt()); -} - -function tyVarEqual -Boolean ::= tv1::TyVar tv2::TyVar -{ - return tv1.extractTyVarRep == tv2.extractTyVarRep; -} - -function freshType -Type ::= -{ - return varType(freshTyVar()); -} - -function newSkolemConstant -Type ::= -{ - return skolemType(freshTyVar()); -} - diff --git a/grammars/silver/definition/type/Unification.sv b/grammars/silver/definition/type/Unification.sv deleted file mode 100644 index 303c18442..000000000 --- a/grammars/silver/definition/type/Unification.sv +++ /dev/null @@ -1,253 +0,0 @@ -grammar silver:definition:type; - -inherited attribute unifyWith :: Type occurs on Type; -synthesized attribute unify :: Substitution occurs on Type; - --------------------------------------------------------------------------------- -aspect production varType -top::Type ::= tv::TyVar -{ - top.unify = - case top.unifyWith of - | varType(j) -> - if tyVarEqual(tv, j) - then emptySubst() - else subst(tv, top.unifyWith) - | _ -> if containsTyVar(tv, top.unifyWith.freeVariables) - then errorSubst("Infinite type! Tried to unify with " ++ prettyType(top.unifyWith)) - else subst(tv, top.unifyWith) - end; -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ - top.unify = - case top.unifyWith of - | skolemType(otv) -> - if tyVarEqual(tv, otv) - then emptySubst() - else errorSubst("Tried to unify skolem constant with incompatible skolem constant") - | _ -> errorSubst("Tried to unify skolem constant with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production errorType -top::Type ::= -{ - top.unify = emptySubst(); -- report no additional errors -} - -aspect production intType -top::Type ::= -{ - top.unify = - case top.unifyWith of - | intType() -> emptySubst() - | _ -> errorSubst("Tried to unify Integer with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production boolType -top::Type ::= -{ - top.unify = - case top.unifyWith of - | boolType() -> emptySubst() - | _ -> errorSubst("Tried to unify Boolean with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production floatType -top::Type ::= -{ - top.unify = - case top.unifyWith of - | floatType() -> emptySubst() - | _ -> errorSubst("Tried to unify Float with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production stringType -top::Type ::= -{ - top.unify = - case top.unifyWith of - | stringType() -> emptySubst() - | _ -> errorSubst("Tried to unify Boolean with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production terminalIdType -top::Type ::= -{ - top.unify = - case top.unifyWith of - | terminalIdType() -> emptySubst() - | _ -> errorSubst("Tried to unify TerminalId with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.unify = - case top.unifyWith of - | nonterminalType(ofn, op) -> - if fn == ofn - then unifyAll(params, op) - else errorSubst("Tried to unify conflicting nonterminal types " ++ fn ++ " and " ++ ofn) - | ntOrDecType(_, _) -> errorSubst("nte-nodte: try again") - | _ -> errorSubst("Tried to unify nonterminal type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.unify = - case top.unifyWith of - | terminalType(ofn) -> - if fn == ofn - then emptySubst() - else errorSubst("Tried to unify conflicting terminal types " ++ fn ++ " and " ++ ofn) - | _ -> errorSubst("Tried to unify terminal type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.unify = - case top.unifyWith of - | decoratedType(ote) -> unify(te, ote) - | ntOrDecType(_,_) -> errorSubst("dte-nodte: try again") - | _ -> errorSubst("Tried to unify decorated type with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ - -- If were being asked to unify, then we know hidden is still a type variable, - -- since we shouldn't be unifying with anything but fully-substituted types. - -- And we kill off this type once hidden is specialized. - top.unify = - case top.unifyWith of - | decoratedType(ote) -> - -- Ensure compatibility between Decorated nonterminal types, then specialize ourselves - unifyAllShortCircuit([ote, top.unifyWith], - [nt, hidden]) - | nonterminalType(_, _) -> - -- Ensure compatibility between nonterminal types, then specialize ourselves - unifyAllShortCircuit([top.unifyWith, top.unifyWith], - [nt, hidden]) - | ntOrDecType(ont1, ohidden1) -> - -- Ensure compatibility between nonterminal types, then merge our specializations - unifyAllShortCircuit([ont1, ohidden1], - [nt, hidden]) - | _ -> errorSubst("Tried to unify decorated type with " ++ prettyType(top.unifyWith)) - end; -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.unify = - case top.unifyWith of - | functionType(oo, op, onp) -> unifyFunctions(out :: params, oo :: op, namedParams, onp) - | _ -> errorSubst("Tried to unify function type with " ++ prettyType(top.unifyWith)) - end; -} - --------------------------------------------------------------------------------- - -function unify -Substitution ::= te1::Type te2::Type -{ - local leftward :: Substitution = te1.unify; - te1.unifyWith = te2; - - local rightward :: Substitution = te2.unify; - te2.unifyWith = te1; - - return if null(leftward.substErrors) - then leftward -- arbitrary choice if both work, but if they are confluent, it's okay - else rightward; -- arbitrary choice of errors. Non-confluent!! -} - -function unifyCheck -Substitution ::= te1::Type te2::Type s::Substitution -{ - return composeSubst(ignoreFailure(s), unify(performSubstitution(te1, s), performSubstitution(te2, s))); -} - --- This function is meant to produce a simple rewriting FROM `fromte` to `tote` --- suitable for use with `performRenaming` (vs `performSubstitution`). --- Basically, it's supposed to structurally rewrite type variables from --- stale variables in the environment, to contextually valid variables/types. --- e.g. (v1 ::= v1 v2) U (int ::= int v1) --- should yield: v1 -> int, v2 -> v1. --- Rewriting should apply this without `v2` becoming `int`. (As normal subst would do.) --- TODO this code is obviously implemented in a fragile way. -function unifyDirectional -Substitution ::= fromte::Type tote::Type -{ - -- Currently, this is built on the assumption that the unification will not fail. - -- Therefore, for now we will FRAGILEY just call unify - -- This is a possible source of bugs/unexpected behavior? - return unify(fromte, tote); -} - -function unifyAll -Substitution ::= te1::[Type] te2::[Type] -{ - local first :: Substitution = unify(head(te1), head(te2)); - - return if null(te1) && null(te2) - then emptySubst() - else if null(te1) || null(te2) - then errorSubst("Internal error: unifying mismatching numbers") - else composeSubst(first, unifyAll( mapSubst(tail(te1), first), - mapSubst(tail(te2), first) )); -} - -function unifyAllShortCircuit -Substitution ::= te1::[Type] te2::[Type] -{ - local first :: Substitution = unify(head(te1), head(te2)); - - return if null(te1) && null(te2) - then emptySubst() - else if null(te1) || null(te2) - then errorSubst("Internal error: unifying mismatching numbers") - else if first.failure - then first -- terminate recursion! - else composeSubst(first, unifyAllShortCircuit( mapSubst(tail(te1), first), - mapSubst(tail(te2), first) )); -} - -function unifyAllNamed -Substitution ::= te1::[NamedArgType] te2::[NamedArgType] -{ - local first :: Substitution = unify(head(te1).argType, head(te2).argType); - - return if null(te1) && null(te2) - then emptySubst() - else if null(te1) || null(te2) - then errorSubst("Internal error: unifying mismatching numbers") - else if head(te1).argName != head(te2).argName -- additionally check names - then errorSubst("Mismatching named parameters") - else composeSubst(first, unifyAllNamed( mapNamedSubst(tail(te1), first), - mapNamedSubst(tail(te2), first) )); -} - -function unifyFunctions -Substitution ::= te1::[Type] te2::[Type] n1::[NamedArgType] n2::[NamedArgType] -{ - local first :: Substitution = unifyAll(te1, te2); - local second :: Substitution = unifyAllNamed(mapNamedSubst(n1, first), mapNamedSubst(n2, first)); - - return composeSubst(first, second); -} - diff --git a/grammars/silver/definition/type/syntax/AspectDcl.sv b/grammars/silver/definition/type/syntax/AspectDcl.sv deleted file mode 100644 index 3bc1239fd..000000000 --- a/grammars/silver/definition/type/syntax/AspectDcl.sv +++ /dev/null @@ -1,88 +0,0 @@ -grammar silver:definition:type:syntax; - -attribute lexicalTypeVariables occurs on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectRHSElem, AspectFunctionSignature, AspectFunctionLHS; - -flowtype lexicalTypeVariables {realSignature, env} on AspectProductionSignature, AspectProductionLHS, AspectRHS, AspectFunctionSignature, AspectFunctionLHS; -flowtype lexicalTypeVariables {deterministicCount, realSignature, env} on AspectRHSElem; - -function addNewLexicalTyVars_ActuallyVariables -[Def] ::= gn::String sl::Location l::[String] -{ - return if null(l) then [] - else lexTyVarDef(gn, sl, head(l), freshType()) :: - addNewLexicalTyVars_ActuallyVariables(gn, sl, tail(l)); -} - --- This binds variables that appear in the signature to type variables, rather than skolem constants --- as in productions declarations. They will be unified with the "real" type, and therefore --- will become those skolem constants. - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - production attribute allLexicalTyVars :: [String]; - allLexicalTyVars = makeSet(ns.lexicalTypeVariables); - - sigDefs <- addNewLexicalTyVars_ActuallyVariables(top.grammarName, top.location, allLexicalTyVars); -} - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - production attribute allLexicalTyVars :: [String]; - allLexicalTyVars = makeSet(ns.lexicalTypeVariables); - - sigDefs <- addNewLexicalTyVars_ActuallyVariables(top.grammarName, top.location, allLexicalTyVars); -} - -aspect production aspectProductionSignature -top::AspectProductionSignature ::= lhs::AspectProductionLHS '::=' rhs::AspectRHS -{ - top.lexicalTypeVariables = makeSet(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); -} - -aspect production aspectProductionLHSTyped -top::AspectProductionLHS ::= id::Name '::' t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} -aspect production aspectProductionLHSFull -top::AspectProductionLHS ::= id::Name t::Type -{ - top.lexicalTypeVariables = []; -- The above overrides this -} - -aspect production aspectRHSElemNil -top::AspectRHS ::= -{ - top.lexicalTypeVariables = []; -} - -aspect production aspectRHSElemCons -top::AspectRHS ::= h::AspectRHSElem t::AspectRHS -{ - top.lexicalTypeVariables = makeSet(h.lexicalTypeVariables ++ t.lexicalTypeVariables); -} - -aspect production aspectRHSElemTyped -top::AspectRHSElem ::= id::Name '::' t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} -aspect production aspectRHSElemFull -top::AspectRHSElem ::= id::Name t::Type -{ - top.lexicalTypeVariables = []; -- The above overrides this -} - -aspect production aspectFunctionSignature -top::AspectFunctionSignature ::= lhs::AspectFunctionLHS '::=' rhs::AspectRHS -{ - top.lexicalTypeVariables = makeSet(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); -} - -aspect production functionLHSType -top::AspectFunctionLHS ::= t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} diff --git a/grammars/silver/definition/type/syntax/FunctionDcl.sv b/grammars/silver/definition/type/syntax/FunctionDcl.sv deleted file mode 100644 index 84c59cf23..000000000 --- a/grammars/silver/definition/type/syntax/FunctionDcl.sv +++ /dev/null @@ -1,25 +0,0 @@ -grammar silver:definition:type:syntax; - -attribute lexicalTypeVariables occurs on FunctionSignature, FunctionLHS; - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - production attribute allLexicalTyVars :: [String]; - allLexicalTyVars = makeSet(ns.lexicalTypeVariables); - - sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, allLexicalTyVars); -} - -aspect production functionSignature -top::FunctionSignature ::= lhs::FunctionLHS '::=' rhs::ProductionRHS -{ - top.lexicalTypeVariables = makeSet(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); -} - -aspect production functionLHS -top::FunctionLHS ::= t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - diff --git a/grammars/silver/definition/type/syntax/ProductionDcl.sv b/grammars/silver/definition/type/syntax/ProductionDcl.sv deleted file mode 100644 index 930167b10..000000000 --- a/grammars/silver/definition/type/syntax/ProductionDcl.sv +++ /dev/null @@ -1,46 +0,0 @@ -grammar silver:definition:type:syntax; - -attribute lexicalTypeVariables occurs on ProductionSignature, ProductionLHS, ProductionRHS, ProductionRHSElem; - -flowtype lexicalTypeVariables {env} on ProductionSignature, ProductionLHS, ProductionRHS; -flowtype lexicalTypeVariables {deterministicCount, env} on ProductionRHSElem; - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - production attribute allLexicalTyVars :: [String]; - allLexicalTyVars = makeSet(ns.lexicalTypeVariables); - - sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, allLexicalTyVars); -} - -aspect production productionSignature -top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS -{ - top.lexicalTypeVariables = makeSet(lhs.lexicalTypeVariables ++ rhs.lexicalTypeVariables); -} - -aspect production productionLHS -top::ProductionLHS ::= id::Name '::' t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - -aspect production productionRHSNil -top::ProductionRHS ::= -{ - top.lexicalTypeVariables = []; -} - -aspect production productionRHSCons -top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS -{ - top.lexicalTypeVariables = makeSet(h.lexicalTypeVariables ++ t.lexicalTypeVariables); -} - -aspect production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - diff --git a/grammars/silver/definition/type/syntax/Terminals.sv b/grammars/silver/definition/type/syntax/Terminals.sv deleted file mode 100644 index 1a61d95e4..000000000 --- a/grammars/silver/definition/type/syntax/Terminals.sv +++ /dev/null @@ -1,10 +0,0 @@ -grammar silver:definition:type:syntax; - -terminal Boolean_tkwd 'Boolean' lexer classes {TYPE,RESERVED}; -terminal Decorated_tkwd 'Decorated' lexer classes {TYPE,RESERVED}; -terminal Float_tkwd 'Float' lexer classes {TYPE,RESERVED}; -terminal Integer_tkwd 'Integer' lexer classes {TYPE,RESERVED}; -terminal String_tkwd 'String' lexer classes {TYPE,RESERVED}; -terminal TerminalId_tkwd 'TerminalId' lexer classes {TYPE,RESERVED}; - - diff --git a/grammars/silver/definition/type/syntax/TypeExpr.sv b/grammars/silver/definition/type/syntax/TypeExpr.sv deleted file mode 100644 index ae89fba21..000000000 --- a/grammars/silver/definition/type/syntax/TypeExpr.sv +++ /dev/null @@ -1,308 +0,0 @@ -grammar silver:definition:type:syntax; - -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:env; -imports silver:util; - -nonterminal TypeExpr - -- This grammar doesn't export silver:definition:core, so the type concrete - -- syntax doesn't "know about" the core layout terminals. - -- Thus we have to set the layout explicitly for the "root" nonterminal here. - layout {BlockComments, Comments, WhiteSpace} - with config, location, grammarName, errors, env, unparse, typerep, lexicalTypeVariables; -nonterminal Signature with config, location, grammarName, errors, env, unparse, types, lexicalTypeVariables; -nonterminal TypeExprs with config, location, grammarName, errors, env, unparse, types, lexicalTypeVariables, errorsTyVars, freeVariables; -nonterminal BracketedOptTypeExprs with config, location, grammarName, errors, env, unparse, types, lexicalTypeVariables, errorsTyVars, freeVariables, envBindingTyVars, initialEnv; - -synthesized attribute types :: [Type]; - --- Important: These should be IN-ORDER and include ALL type variables that appear, including duplicates! -synthesized attribute lexicalTypeVariables :: [String]; --- freeVariables also occurs on TypeExprs, and should be IN ORDER - --- These attributes are used if we're using the TypeExprs as type variables-only. -synthesized attribute errorsTyVars :: [Message] with ++; --- A new environment, with the type variables in this list appearing bound -inherited attribute initialEnv :: Decorated Env; -synthesized attribute envBindingTyVars :: Decorated Env; - --- TODO: This function should go away because it doesn't do location correctly. --- But for now, we'll use it. It might be easier to get rid of once we know exactly --- how ty vars end up in the environment. -function addNewLexicalTyVars -[Def] ::= gn::String sl::Location l::[String] -{ - return if null(l) then [] - else lexTyVarDef(gn, sl, head(l), skolemType(freshTyVar())) :: - addNewLexicalTyVars(gn, sl, tail(l)); -} - -abstract production errorTypeExpr -top::TypeExpr ::= e::[Message] -{ - top.unparse = s"{- Errors:\n${messagesToString(e)} -}"; - - top.typerep = errorType(); - - top.errors := e; - - top.lexicalTypeVariables = []; -} - -abstract production typerepTypeExpr -top::TypeExpr ::= t::Type -{ - top.unparse = prettyType(t); - - top.typerep = t; - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production integerTypeExpr -top::TypeExpr ::= 'Integer' -{ - top.unparse = "Integer"; - - top.typerep = intType(); - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production floatTypeExpr -top::TypeExpr ::= 'Float' -{ - top.unparse = "Float"; - - top.typerep = floatType(); - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production stringTypeExpr -top::TypeExpr ::= 'String' -{ - top.unparse = "String"; - - top.typerep = stringType(); - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production booleanTypeExpr -top::TypeExpr ::= 'Boolean' -{ - top.unparse = "Boolean"; - - top.typerep = boolType(); - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production termnalIdTypeExpr -top::TypeExpr ::= 'TerminalId' -{ - top.unparse = "TerminalId"; - - top.typerep = terminalIdType(); - - top.errors := []; - - top.lexicalTypeVariables = []; -} - -concrete production nominalTypeExpr -top::TypeExpr ::= q::QNameType tl::BracketedOptTypeExprs -{ - top.unparse = q.unparse ++ tl.unparse; - - top.errors := q.lookupType.errors ++ tl.errors; - top.lexicalTypeVariables = tl.lexicalTypeVariables; - - top.errors <- if length(tl.types) != length(q.lookupType.dclBoundVars) - then [err(top.location, q.name ++ " has " ++ toString(length(q.lookupType.dclBoundVars)) ++ " type variables, but there are " ++ toString(length(tl.types)) ++ " supplied here.")] - else []; - - -- Not necessarily a nonterminalType, so we should take original type and substitution - -- e.g. consider `type Blah = Foo` - top.typerep = performRenaming(q.lookupType.typerep, zipVarsAndTypesIntoSubstitution(q.lookupType.dclBoundVars, tl.types)); -} - -concrete production typeVariableTypeExpr -top::TypeExpr ::= tv::IdLower_t -{ - top.unparse = tv.lexeme; - - local attribute hack::QNameLookup; - hack = customLookup("type", getTypeDcl(tv.lexeme, top.env), tv.lexeme, top.location); - - top.typerep = hack.typerep; - top.errors := hack.errors; - - top.lexicalTypeVariables = [tv.lexeme]; -} - -concrete production refTypeExpr -top::TypeExpr ::= 'Decorated' t::TypeExpr -{ - top.unparse = "Decorated " ++ t.unparse; - - top.typerep = decoratedType(t.typerep); - - top.errors := t.errors; - - top.errors <- case t.typerep of - nonterminalType(_,_) -> [] - | _ -> [err(t.location, t.unparse ++ " is not a nonterminal, and cannot be Decorated.")] - end; - - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - -concrete production funTypeExpr -top::TypeExpr ::= '(' sig::Signature ')' -{ - top.unparse = "(" ++ sig.unparse ++ ")"; - - top.errors := sig.errors; - - top.typerep = functionType(head(sig.types), tail(sig.types), []); - - top.lexicalTypeVariables = sig.lexicalTypeVariables; -} - -concrete production signatureEmptyRhs -top::Signature ::= t::TypeExpr '::=' -{ - top.unparse = t.unparse ++ " ::="; - - top.errors := t.errors; - - top.types = [t.typerep]; - - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - -concrete production psignature -top::Signature ::= t::TypeExpr '::=' list::TypeExprs -{ - top.unparse = t.unparse ++ " ::= " ++ list.unparse; - - top.errors := t.errors ++ list.errors; - - top.types = [t.typerep] ++ list.types; - - top.lexicalTypeVariables = t.lexicalTypeVariables ++ list.lexicalTypeVariables; -} - --- Bracketed Optional Type Lists ----------------------------------------------- - -concrete production botlNone -top::BracketedOptTypeExprs ::= -{ - top.unparse = ""; - forwards to botlSome('<', typeListNone(location=top.location), '>', location=top.location); -} - -concrete production botlSome -top::BracketedOptTypeExprs ::= '<' tl::TypeExprs '>' -{ - top.unparse = "<" ++ tl.unparse ++ ">"; - - top.errors := tl.errors; - top.types = tl.types; - - top.lexicalTypeVariables = tl.lexicalTypeVariables; - top.freeVariables = tl.freeVariables; - - top.errorsTyVars := tl.errorsTyVars ++ - if containsDuplicates(tl.lexicalTypeVariables) - then [err(top.location, "Type parameter list repeats type variable names")] - else []; - - top.envBindingTyVars = - newScopeEnv( - addNewLexicalTyVars(top.grammarName, top.location, tl.lexicalTypeVariables), - top.initialEnv); -} - --- TypeExprss ------------------------------------------------------------------- - -abstract production typeListNone -top::TypeExprs ::= -{ - top.unparse = ""; - top.errors := []; - top.types = []; - top.lexicalTypeVariables = []; -} - - -concrete production typeListSingle -top::TypeExprs ::= t::TypeExpr -{ - top.unparse = t.unparse; - - top.errors := t.errors; - - top.types = [t.typerep]; - - top.lexicalTypeVariables = t.lexicalTypeVariables; -} - -concrete production typeListCons -top::TypeExprs ::= t::TypeExpr list::TypeExprs -{ - top.unparse = t.unparse ++ " " ++ list.unparse; - - top.errors := t.errors ++ list.errors; - - top.types = [t.typerep] ++ list.types; - - top.lexicalTypeVariables = t.lexicalTypeVariables ++ list.lexicalTypeVariables; -} - --------------------------------------------------------------------------------- --- Aspecting the above three here, just to separate out these concerns: --- This has to do with type lists that are type variables only. --- We don't have a separate nonterminal for this, because we'd like to produce --- "semantic" errors, rather than parse errors for this. - -aspect production typeListNone -top::TypeExprs ::= -{ - top.errorsTyVars := []; - top.freeVariables = []; -} - -aspect production typeListSingle -top::TypeExprs ::= t::TypeExpr -{ - top.errorsTyVars := case t of - typeVariableTypeExpr(_) -> [] - | _ -> [err(t.location, t.unparse ++ " is not permitted here, only type variables are")] - end; - top.freeVariables = t.typerep.freeVariables; -} - -aspect production typeListCons -top::TypeExprs ::= t::TypeExpr list::TypeExprs -{ - top.errorsTyVars := case t of - typeVariableTypeExpr(_) -> [] - | _ -> [err(t.location, t.unparse ++ " is not permitted here, only type variables are")] - end ++ list.errorsTyVars; - top.freeVariables = t.typerep.freeVariables ++ list.freeVariables; -} - diff --git a/grammars/silver/driver/BuildProcess.sv b/grammars/silver/driver/BuildProcess.sv deleted file mode 100644 index 3dc4e46f5..000000000 --- a/grammars/silver/driver/BuildProcess.sv +++ /dev/null @@ -1,225 +0,0 @@ -grammar silver:driver; - -imports silver:definition:core; -imports silver:definition:env; - -imports silver:util; -imports silver:util:cmdargs; - -exports silver:driver:util; - -type SVParser = (ParseResult ::= String String); - -{-- - - Run the silver compiler, as if invoked from the command line. - -} -function cmdLineRun -IOVal ::= args::[String] svParser::SVParser ioin::IO -{ - local unit :: IOErrorable = - cmdLineRunInitial(args, svParser, ioin); - - return performActions(unit); -} - --- Compute the environment, and then setup and do a build run. No postOps executed, though. -function cmdLineRunInitial -IOErrorable ::= - args::[String] svParser::SVParser ioin::IO -{ - return - runChainArg( - computeEnv, - setupBuildRun(svParser, _, _), - args, ioin); -} - --- Perform the postOps from a cmdLineRunInitial. -function performActions -IOVal ::= unitin::IOErrorable -{ - return case unitin.iovalue of - | left(re) -> ioval(print(re.message ++ "\n", unitin.io), re.code) - | right(comp) -> runAll(sortUnits(comp.postOps), unitin.io) - end; -} - --- Parser args and environment -function computeEnv -IOErrorable> ::= - args::[String] - ioin::IO -{ - -- Figure out arguments - local argResult :: Either = - parseArgs(args); - local a :: Decorated CmdArgs = - case argResult of right(t) -> t end; - local argErrors :: [String] = - case argResult of | left(s) -> [s] | _ -> [] end; - - -- Figure out build env from environment and args - local benvResult :: IOVal> = - determineBuildEnv(a, ioin); - local benv :: BuildEnv = - case benvResult.iovalue of left(t) -> t end; - local envErrors :: [String] = - case benvResult.iovalue of | right(s) -> s | _ -> [] end; - - return if !null(argErrors) then - ioval(ioin, left(runError(1, head(argErrors)))) - -- Because we want printing the version to work even if the environment is messed up - -- we premptively handle that here. This is slightly unfortunate. - -- Ideally, version printing would be just another thing we could have the command - -- line decide to go do, but currently it's hard to re-use code if we do that. - else if !null(envErrors) then - ioval(benvResult.io, left(runError(1, implode("\n", envErrors)))) - else if a.displayVersion then - ioval(benvResult.io, left(runError(1, -- error code so 'ant' isnt run - "Silver Version 0.4.1-dev\n" ++ - "SILVER_HOME = " ++ benv.silverHome ++ "\n" ++ - "SILVER_GEN = " ++ benv.silverGen ++ "\n" ++ - "GRAMMAR_PATH:\n" ++ implode("\n", benv.grammarPath) ++ "\n\n" ++ - implode("\n", envErrors)))) - else - ioval(benvResult.io, right(pair(a, benv))); -} - --- Upon deciding that we're to build a single grammar into a jar, we do this -function setupBuildRun -IOErrorable ::= - svParser::SVParser - envin::Pair - ioin::IO -{ - local a::Decorated CmdArgs = envin.fst; - local benv::BuildEnv = envin.snd; - - -- Check environment stuff specific to building a grammar - local buildGrammar :: String = head(a.buildGrammar); - local checkbuild :: IOVal<[String]> = - checkPreBuild(a, benv, buildGrammar, ioin); - - -- Build! - local buildrun :: IOVal = - buildRun(svParser, a, benv, buildGrammar, checkbuild.io); - - return if !null(checkbuild.iovalue) then - ioval(checkbuild.io, left(runError(1, implode("\n", checkbuild.iovalue)))) - else if null(buildrun.iovalue.grammarList) then - ioval(buildrun.io, left(runError(1, "The specified grammar (" ++ buildGrammar ++ ") could not be found.\n"))) - else - ioval(buildrun.io, right(buildrun.iovalue)); -} - -{-- - - Given an environment and a grammar to build, returns a Compilation. - - Note that it's the caller's responsibility to actually evaluation that - - compilation's actions. - -} -function buildRun -IOVal ::= - svParser::SVParser - a::Decorated CmdArgs - benv::BuildEnv - buildGrammar::String - ioin::IO -{ - -- Compile grammars. There's some tricky circular program data flow here. - -- This does an "initial grammar stream" composed of - -- grammars and interface files that *locally* seem good. - local rootStream :: IOVal<[Maybe]> = - compileGrammars(svParser, benv, grammarStream, a.doClean, ioin); - - -- The list of grammars to build. This is circular with the above, producing - -- a list that's terminated when the response count is equal to the number of emitted - -- grammar names. - local grammarStream :: [String] = - buildGrammar :: eatGrammars(1, [buildGrammar], rootStream.iovalue, unit.grammarList); - - -- This is, essentially, a data structure representing a compilation. - -- Note that it is pure: it doesn't take any actions. - local unit :: Compilation = - compilation( - foldr(consGrammars, nilGrammars(), catMaybes(rootStream.iovalue)), - foldr(consGrammars, nilGrammars(), catMaybes(reRootStream.iovalue)), - buildGrammar, benv); - -- This is something we should probably get rid of, someday. Somehow. It's hard. - unit.config = a; - - -- There is a second circularity here where we use unit.recheckGrammars - -- to supply the second parameter to unit. - local reRootStream :: IOVal<[Maybe]> = - compileGrammars(svParser, benv, unit.recheckGrammars, true, rootStream.io); - - return ioval(reRootStream.io, unit); -} - - -{-- - - Consumes a stream of parses, outputs a stream of new dependencies. - - Typically used as a circular program with 'compileGrammars' - - - - @param n Expected number of new inputs from rootStream - - @param sofar Set of grammars already seen, and should not be requested again - - @param rootStream Stream of found/not found info. Should not be used except to test presence - - @param grammars List of grammars *in the same order as 'just' appears in rootStream* - - @return A stream of new dependencies - -} -function eatGrammars -[String] ::= n::Integer sofar::[String] rootStream::[Maybe] grammars::[Decorated RootSpec] -{ - local it :: Decorated RootSpec = head(grammars); - - local directDeps :: [String] = mentionedGrammars(it); - - local newDeps :: [String] = rem(directDeps, sofar); - - return - if n == 0 then - [] - else if !head(rootStream).isJust then - eatGrammars(n-1, sofar, tail(rootStream), grammars) - else - newDeps ++ eatGrammars(n-1+length(newDeps), newDeps ++ sofar, tail(rootStream), tail(grammars)); -} - - -nonterminal RunError with code, message; --- from silver:langutil, and silver:driver:util; - -abstract production runError -top::RunError ::= c::Integer m::String -{ - top.code = c; - top.message = m; -} - --- A common return type for IO functions. Does IO and returns error or whatever. -type IOErrorable = IOVal>; --- A function that does IO and either errors or returns a value -type RunChain = (IOErrorable ::= a IO); - --- Function composition of RunChains. IO-y Either monad, of sorts. -function runChain -RunChain ::= l::RunChain r::RunChain -{ - return runChainArg(l, r, _, _); -} - -function runChainArg -IOErrorable ::= l::RunChain r::RunChain x::a ioin::IO -{ - -- Apply to left. - local lcall :: IOErrorable = l(x, ioin); - - -- apply return value to right, if possible. otherwise, propagate error - local rcall :: IOErrorable = - case lcall.iovalue of - | left(re) -> ioval(lcall.io, left(re)) - | right(y) -> r(y, lcall.io) - end; - - return rcall; -} - diff --git a/grammars/silver/driver/Command.sv b/grammars/silver/driver/Command.sv deleted file mode 100644 index 688a1a4a6..000000000 --- a/grammars/silver/driver/Command.sv +++ /dev/null @@ -1,221 +0,0 @@ -grammar silver:driver; - -attribute genLocation, doClean, displayVersion, warnError, searchPath, outName, buildGrammar, silverHomeOption, noBindingChecking occurs on CmdArgs; - -synthesized attribute searchPath :: [String]; -synthesized attribute outName :: [String]; -synthesized attribute genLocation :: [String]; -synthesized attribute silverHomeOption :: [String]; - -synthesized attribute displayVersion :: Boolean; -synthesized attribute doClean :: Boolean; -synthesized attribute warnError :: Boolean; - -synthesized attribute buildGrammar :: [String]; - -synthesized attribute noBindingChecking :: Boolean; - -aspect production endCmdArgs -top::CmdArgs ::= l::[String] -{ - top.doClean = false; - top.displayVersion = false; - top.warnError = false; - top.outName = []; - top.searchPath = []; - top.genLocation = []; - top.silverHomeOption = []; - top.buildGrammar= l; - top.noBindingChecking = false; -} -abstract production versionFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.displayVersion = true; - forwards to rest; -} -abstract production cleanFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.doClean = true; - forwards to rest; -} -abstract production warnErrorFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.warnError = true; - forwards to rest; -} -abstract production outFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.outName = s :: forward.outName; - forwards to rest; -} -abstract production includeFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.searchPath = s :: forward.searchPath; - forwards to rest; -} -abstract production genFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.genLocation = s :: forward.genLocation; - forwards to rest; -} -abstract production homeFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.silverHomeOption = s :: forward.silverHomeOption; - forwards to rest; -} -abstract production nobindingFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.noBindingChecking = true; - forwards to rest; -} - -function parseArgs -Either ::= args::[String] -{ - production attribute flags::[Pair] with ++; - flags := []; - production attribute flagdescs::[String] with ++; - flagdescs := []; - - -- General rules of thumb: - -- Use -- as your prefix - -- Unless it's an OPTION, and it's commonly used, and it's obvious from context what it means - -- e.g. -I my/grammars is obvious because it refers to a location to include. - - flags <- - [pair("-I", option(includeFlag)), - pair("-o", option(outFlag)), - pair("-G", option(genFlag)), - pair("--silver-home", option(homeFlag)), - pair("--version", flag(versionFlag)), - pair("--clean", flag(cleanFlag)), - pair("--dont-analyze", flag(nobindingFlag)), - pair("--warn-error", flag(warnErrorFlag)) - ]; - -- Always start with \t, name options descriptively in <>, do not end with \n! - flagdescs <- - ["\t-I : path to grammars (GRAMMAR_PATH)", - "\t-o : name of binary file", - "\t--version : display version", - "\t--clean : overwrite interface files", - "\t-G : Location to store generate files (SILVER_GEN)", - "\t--warn-error : treat warnings as errors" - ]; - - local usage :: String = - "Usage: silver [options] grammar:to:build\n\nFlag options:\n" ++ implode("\n", sortBy(stringLte, flagdescs)) ++ "\n"; - - -- Parse the command line - production a :: CmdArgs = interpretCmdArgs(flags, args); - - production attribute errors :: [String] with ++; - errors := if a.cmdError.isJust then [a.cmdError.fromJust] else []; - - errors <- - if length(a.cmdRemaining) > 1 then ["Unable to interpret arguments: " ++ implode(" ", a.cmdRemaining)] - else if length(a.outName) > 1 then ["Multiple options given for -o flag: " ++ implode(" ", a.outName)] - else if length(a.genLocation) > 1 then ["Multiple options given for -G flag: " ++ implode(" ", a.genLocation)] - else if length(a.silverHomeOption) > 1 then ["Multiple options given for --silver-home flag: " ++ implode(" ", a.silverHomeOption)] - else []; - - return if !null(errors) - then left(implode("\n", errors) ++ "\n\n" ++ usage) - else right(a); -} - --- This uses Either backwards. TODO: flip order? "right is correct" also TODO: use RunError? -function determineBuildEnv -IOVal> ::= a::Decorated CmdArgs ioin::IO -{ - -- Let's locally set up and verify the environment - local envSH :: IOVal = envVar("SILVER_HOME", ioin); - local envGP :: IOVal = envVar("GRAMMAR_PATH", envSH.io); - local envSHG :: IOVal = envVar("SILVER_HOST_GEN", envGP.io); - local envSG :: IOVal = envVar("SILVER_GEN", envSHG.io); - - -- If SILVER_HOME isn't set, determine it from where this jar is - local derivedSH :: IOVal = - if envSH.iovalue == "" then - determineDefaultSilverHome(envSG.io) - else - ioval(envSG.io, envSH.iovalue); - - local benv :: BuildEnv = - fromArgsAndEnv( - -- TODO: maybe we should use the java platform separator here? - derivedSH.iovalue, envSG.iovalue, - explode(":", envGP.iovalue), explode(":", envSHG.iovalue), - a.silverHomeOption, a.genLocation, a.searchPath); - - -- Let's do some checks on the environment - local checkenv :: IOVal<[String]> = checkEnvironment(benv, derivedSH.io); - - return if null(checkenv.iovalue) then - ioval(checkenv.io, left(benv)) - else - ioval(checkenv.io, right(checkenv.iovalue)); -} - -function checkEnvironment -IOVal<[String]> ::= benv::BuildEnv ioin::IO -{ - local isGenDir :: IOVal = isDirectory(benv.silverGen, ioin); - local isGramDir :: IOVal = isDirectory(benv.defaultGrammarPath, isGenDir.io); - - local errors :: [String] = - if benv.silverHome == "/" -- because we called 'endWithSlash' on empty string - then ["Missing SILVER_HOME or --silver-home .\nThis should have been set up by the 'silver' script.\n"] - else if !isGenDir.iovalue - then if benv.silverGen == benv.defaultSilverGen - then ["Missing SILVER_GEN or -G .\nThis should have been inferable, but " ++ benv.silverGen ++ " is not a directory.\n"] - else ["Supplied SILVER_GEN location " ++ benv.silverGen ++ " is not a directory.\n"] - else if !isGramDir.iovalue - then ["Missing standard library grammars: tried " ++ benv.defaultGrammarPath ++ " but this did not exist.\n"] - else []; - -- TODO: We should probably check everything in grammarPath? - -- TODO: Maybe look for 'core' specifically? - - return ioval(isGramDir.io, errors); -} - -function checkPreBuild -IOVal<[String]> ::= - a::Decorated CmdArgs - benv::BuildEnv - buildGrammar::String - ioin::IO -{ - local errors :: [String] = - if null(a.cmdRemaining) then ["No grammar to build was specified.\n"] - else if indexOf("/", buildGrammar) != -1 -- basic sanity check - then ["Build grammar appears to contain slashes: " ++ buildGrammar ++ "\n"] - else if indexOf(".", buildGrammar) != -1 -- also, now - then ["Build grammar appears to contain dots: " ++ buildGrammar ++ "\n"] - else []; - -- TODO: presently, we check whether we find this grammar elsewhere. Maybe it should be here? not sure. - - return ioval(ioin, errors); -} - --- This code has to live in the generated jar for the program, as putting it in the --- standard library may someday return the location of the standard library jar instead --- of us -function determineDefaultSilverHome -IOVal ::= i::IO -{ - return error("NYI"); -} foreign { - -- This grabs the path to this jar (using Init.class as the thing to find the path to) - -- Then goes up two levels (HOME/jars/file.jar to HOME) and returns that. - -- If anything goes wrong, we crash. - "java" : return "new core.Pioval(%i%, common.Util.determineSilverHomePath(Init.class))"; -} - diff --git a/grammars/silver/driver/CompileFiles.sv b/grammars/silver/driver/CompileFiles.sv deleted file mode 100644 index 4e3990ce1..000000000 --- a/grammars/silver/driver/CompileFiles.sv +++ /dev/null @@ -1,35 +0,0 @@ -grammar silver:driver; - -{-- - - Parses a list of files. - - @param svParser The parser to use to contruct Roots - - @param gpath The path where we found the grammar. Ends in a slash/ - - @param files The list of .sv files to read. - - @param ioin The io token - - @return An ioval wrapping the list of parse results and parse errors. - -} -function compileFiles -IOVal> ::= svParser::SVParser gpath::String files::[String] ioin::IO -{ - local file :: String = head(files); - - -- Print the path we're reading, and read the file. - local text :: IOVal = - readFile(gpath ++ file, ioin); - - -- This is where a .sv file actually gets parsed: - local r :: ParseResult = svParser(text.iovalue, file); - - -- Continue parsing the rest of the files. - local recurse :: IOVal> = compileFiles(svParser, gpath, tail(files), text.io); - - return if null(files) then ioval(ioin, pair([], [])) - -- Using [] in this case because there seems to be no end to io token demanding issues: - else case r of - | parseSucceeded(rtree, _) -> - ioval(recurse.io, pair(rtree :: recurse.iovalue.fst, recurse.iovalue.snd)) - | parseFailed(errval, _) -> - ioval(recurse.io, pair(recurse.iovalue.fst, errval :: recurse.iovalue.snd)) - end; -} - diff --git a/grammars/silver/driver/CompileGrammar.sv b/grammars/silver/driver/CompileGrammar.sv deleted file mode 100644 index 6bef4c68a..000000000 --- a/grammars/silver/driver/CompileGrammar.sv +++ /dev/null @@ -1,148 +0,0 @@ -grammar silver:driver; - -{-- - - Hunts down a grammar and obtains its symbols, either by building or from an interface file. - -} -function compileGrammar -IOVal> ::= - svParser::SVParser - benv::BuildEnv - grammarName::String - clean::Boolean - ioin::IO -{ - local gramPath :: String = grammarToPath(grammarName); - - -- IO Step 1: Look for the grammar's source files - local grammarLocation :: IOVal> = findGrammarLocation(gramPath, benv.grammarPath, ioin); - - -- IO Step 2: List those files, and obtain their newest modification time - local files :: IOVal<[String]> = listSilverFiles(grammarLocation.iovalue.fromJust, grammarLocation.io); - local grammarTime :: IOVal = fileTimes(grammarLocation.iovalue.fromJust, files.iovalue, files.io); - - -- IO Step 3: Let's look for a valid interface file - local ifaceCompile :: IOVal> = - if clean then - -- We just skip this search if it's a clean build - ioval(grammarTime.io, nothing()) - else - compileInterface(grammarName, benv.silverHostGen, grammarTime.iovalue, grammarTime.io); - - -- IO Step 4: Build the grammar, and say so - local pr :: IO = - print("Compiling " ++ grammarName ++ "\n\t[" ++ grammarLocation.iovalue.fromJust ++ "]\n\t[" ++ renderFileNames(files.iovalue, 0) ++ "]\n", ifaceCompile.io); - - local gramCompile :: IOVal> = - compileFiles(svParser, grammarLocation.iovalue.fromJust, files.iovalue, pr); - - local rs :: RootSpec = - if null(gramCompile.iovalue.snd) then - grammarRootSpec(foldRoot(gramCompile.iovalue.fst), grammarName, grammarLocation.iovalue.fromJust, grammarTime.iovalue, benv.silverGen) - else - errorRootSpec(gramCompile.iovalue.snd, grammarName, grammarLocation.iovalue.fromJust, grammarTime.iovalue, benv.silverGen); - - return - if !grammarLocation.iovalue.isJust then - -- No grammar found! - ioval(grammarLocation.io, nothing()) - else if null(files.iovalue) then - -- Grammar had no files! - ioval(files.io, nothing()) - else if ifaceCompile.iovalue.isJust then - -- Found a valid interface file! Stop short, and return that - ifaceCompile - else - -- Return the compiled grammar - ioval(gramCompile.io, just(rs)); -} - -function foldRoot -Grammar ::= l::[Root] -{ - return foldr(consGrammar, nilGrammar(), l); -} - -{-- - - Determined whether a file name should be considered a Silver source file. - -} -function isValidSilverFile -Boolean ::= f::String -{ - return endsWith(".sv", f) && !startsWith(".", f); -} -function listSilverFiles -IOVal<[String]> ::= dir::String ioin::IO -{ - local files :: IOVal<[String]> = listContents(dir, ioin); - - return ioval(files.io, filter(isValidSilverFile, files.iovalue)); -} - -{-- - - Determines the maximum modification time of all files in a directory. - - Including the directory itself, to detect file deletions. - -} -function fileTimes -IOVal ::= dir::String is::[String] i::IO -{ - local ft :: IOVal = fileTime(dir ++ head(is), i); - local rest :: IOVal = fileTimes(dir, tail(is), ft.io); - - return if null(is) - then fileTime(dir, i) -- check the directory itself. Catches deleted files. - else if ft.iovalue > rest.iovalue - then ioval(rest.io, ft.iovalue) - else rest; -} - --- A crude approximation of line wrapping -function renderFileNames -String ::= files::[String] depth::Integer -{ - return - if null(files) then "" else - if depth >= 7 then "\n\t " ++ renderFileNames(files, 0) else - head(files) ++ - if null(tail(files)) then "" else " " ++ renderFileNames(tail(files), depth + 1); -} - -{-- - - Takes a grammar name (already converted to a path) and searches the grammar - - path for the first directory that matches. - -} -function findGrammarLocation -IOVal> ::= path::String searchPaths::[String] iIn::IO -{ - local exists :: IOVal> = - findGrammarInLocation(path, head(searchPaths), iIn); - - return - if null(searchPaths) then ioval(iIn, nothing()) - else if exists.iovalue.isJust then exists - else findGrammarLocation(path, tail(searchPaths), exists.io); -} - -{-- - - Looks to see if the grammar can be found in 'inPath' - - Tries (in order) for edu:umn:cs - - edu/umn/cs/ - - edu.umn/cs/ - - edu.umn.cs/ - -} -function findGrammarInLocation -IOVal> ::= gram::String inPath::String iIn::IO -{ - -- Find the first / in the grammar name (turned path) we're looking for. - local idx :: Integer = indexOf("/", gram); - - -- Replace the first / with a . - local nextGram :: String = substring(0, idx, gram) ++ "." ++ substring(idx + 1, length(gram), gram); - - local exists :: IOVal = isDirectory(inPath ++ gram, iIn); - - return - if idx == -1 then ioval(iIn, nothing()) - else if exists.iovalue then ioval(exists.io, just(inPath ++ gram)) - else findGrammarInLocation(nextGram, inPath, exists.io); -} - diff --git a/grammars/silver/driver/CompileGrammars.sv b/grammars/silver/driver/CompileGrammars.sv deleted file mode 100644 index a2c312d3a..000000000 --- a/grammars/silver/driver/CompileGrammars.sv +++ /dev/null @@ -1,41 +0,0 @@ -grammar silver:driver; - -{-- - - Eat the stream `need` and produce the output stream of (maybe, if found) `RootSpec`s. - - - - @param benv The compiler configuration, including search paths - - @param need A **stream** of grammars to compile. - - @param clean If true, ignore interface files entirely. - -} -function compileGrammars -IOVal<[Maybe]> ::= - svParser::SVParser - benv::BuildEnv - need::[String] - clean::Boolean - ioin::IO -{ - local grammarName :: String = head(need); - - -- Build the first gramamr in the need list. - local now :: IOVal> = - compileGrammar(svParser, benv, grammarName, clean, ioin); - - -- Recurse for the rest of the grammars needed. - local recurse :: IOVal<[Maybe]> = - compileGrammars(svParser, benv, tail(need), clean, now.io); - - return - if null(need) then - ioval(ioin, []) - else - ioval(recurse.io, unsafeTrace(now.iovalue, now.io) :: recurse.iovalue); - -- A short note about that unsafeTrace: - -- Unfortunately, Silver lacks any way to indicate strictness in the types, and - -- as a consequence, writing 'now.iovalue :: ...' means we can construct this - -- list without actually forcing the IO action intended by 'now', since we - -- never need to evaluate 'now' at all! - -- So we use unsafeTrace to first eval the IO action (on now.io) and then return - -- the 'now.iovalue' thunk. -} - diff --git a/grammars/silver/driver/CompileInterface.sv b/grammars/silver/driver/CompileInterface.sv deleted file mode 100644 index f3656c9b9..000000000 --- a/grammars/silver/driver/CompileInterface.sv +++ /dev/null @@ -1,74 +0,0 @@ -grammar silver:driver; - -import silver:reflect; - -{-- - - Find an interface file, if it exists, and it's valid (parsable and modification time is newer). - - - - @param grammarName The grammar we're looking for an interface file for - - @param silverHostGen The search path to look for interface files within - - @param grammarTime The newest modification time of the source files, to compare against - -} -function compileInterface -IOVal> ::= grammarName::String silverHostGen::[String] grammarTime::Integer ioin::IO -{ - local gramPath :: String = grammarToPath(grammarName); - - -- IO Step 1: Find the interface file, if any - local gen :: IOVal> = findInterfaceLocation(gramPath, silverHostGen, ioin); - - local file :: String = gen.iovalue.fromJust ++ "src/" ++ gramPath ++ "Silver.svi"; - - -- IO Step 2: See if it's new enough - local modTime :: IOVal = fileTime(file, gen.io); - - -- IO Step 3: Let's say so, and parse it - local pr :: IO = print("Found " ++ grammarName ++ "\n\t[" ++ file ++ "]\n", modTime.io); - local text :: IOVal = readFile(file, pr); - - local ir :: Either = deserialize("Silver.svi", text.iovalue); - - -- IO Step 4: Perhaps complain it failed to parse - local pr2 :: IO = - case ir of - | right(i) -> - if !null(i.interfaceErrors) - then - print("\n\tErrors unpacking interface file:\n " ++ implode("\n ", i.interfaceErrors) ++ - "\n\tRecovering by parsing grammar....\n", text.io) - else text.io - | left(msg) -> - print("\n\tFailed to deserialize interface file!\n" ++ msg ++ - "\n\tRecovering by parsing grammar....\n", text.io) - end; - - local rs :: RootSpec = interfaceRootSpec(ir.fromRight, modTime.iovalue, gen.iovalue.fromJust); - - return - if !gen.iovalue.isJust then - -- Didn't find one. Stop short, return nothing. - ioval(gen.io, nothing()) - else if modTime.iovalue <= grammarTime then - -- Interface file is too old, stop short, return nothing. - ioval(modTime.io, nothing()) - else if ir.isLeft || !null(ir.fromRight.interfaceErrors) then - -- Deserialization failed, return nothing. - ioval(pr2, nothing()) - else ioval(pr2, just(rs)); -} - -{-- - - Takes a grammar name (already converted to a path) and searches for Silver.svi - -} -function findInterfaceLocation -IOVal> ::= gramPath::String searchPaths::[String] ioin::IO -{ - local inPath :: String = head(searchPaths); - local exists :: IOVal = isFile(inPath ++ "src/" ++ gramPath ++ "Silver.svi", ioin); - - return - if null(searchPaths) then ioval(ioin, nothing()) - else if exists.iovalue then ioval(exists.io, just(inPath)) - else findInterfaceLocation(gramPath, tail(searchPaths), exists.io); -} - diff --git a/grammars/silver/driver/DocConfig.sv b/grammars/silver/driver/DocConfig.sv deleted file mode 100644 index 2eb626ea0..000000000 --- a/grammars/silver/driver/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:driver; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Driver\nmenu_title: Driver\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/driver/GrammarAction.sv b/grammars/silver/driver/GrammarAction.sv deleted file mode 100644 index 5bbc7d434..000000000 --- a/grammars/silver/driver/GrammarAction.sv +++ /dev/null @@ -1,120 +0,0 @@ -grammar silver:driver; - -{- - - A short guide to error codes: - - Negative = configuration/installation error - - 1-19 = command line/start up error - - 20+ = Normal use error (errors in spec) - - 127 = "abnormal" success (e.g. printed version string, quit now) - - 0 = success of course - -} - -{- Orders: - - 0: parsing errors (also flow graph emitting) - - 1: binding errors - - 3: interfaces and classes - - 4: doc - - 5: copper_mda - - 6: buildxml - - 7: impide - -} - -aspect production compilation -top::Compilation ::= g::Grammars r::Grammars buildGrammar::String benv::BuildEnv -{ - top.postOps <- [printAllParsingErrors(g.grammarList ++ r.grammarList)]; - top.postOps <- if top.config.noBindingChecking then [] else - [printAllBindingErrors(g.grammarList ++ r.grammarList)]; - top.postOps <- [touchIfaces(r.grammarList, benv.silverGen)]; -} - -abstract production touchIfaces -top::DriverAction ::= r::[Decorated RootSpec] genPath::String -{ - top.io = touchFiles(map(sviPath(_, genPath), r), top.ioIn); - top.code = 0; - top.order = 3; -} -function sviPath -String ::= r::Decorated RootSpec genPath::String -{ - return genPath ++ "src/" ++ grammarToPath(r.declaredName) ++ "Silver.svi"; -} - -abstract production printAllBindingErrors -top::DriverAction ::= specs::[Decorated RootSpec] -{ - -- Force printing of status before doing error checks - top.code = unsafeTrace(forward.code, forward.ioIn); - -- For anyone encountering this hack for the first time, - -- IO-token passing can force linearity of actions, but - -- interleaves pure computation in annoying ways. - -- Without the above, all the error checking work gets done - -- (to compute return code) before something tries to do IO, - -- so we wouldn't print first. - - forwards to printAllBindingErrorsHelp(specs) - with { - ioIn = print("Checking For Errors.\n", top.ioIn); - }; -} - -abstract production printAllBindingErrorsHelp -top::DriverAction ::= specs::[Decorated RootSpec] -{ - local errs :: [Pair] = head(specs).grammarErrors; - - local i :: IO = - if null(errs) - then top.ioIn - else print("Errors for " ++ head(specs).declaredName ++ "\n" ++ sflatMap(renderMessages(head(specs).grammarSource, _), errs) ++ "\n", top.ioIn); - - local recurse :: DriverAction = printAllBindingErrorsHelp(tail(specs)); - recurse.ioIn = i; - - top.io = if null(specs) then top.ioIn else recurse.io; - - top.code = - if null(specs) || (!grammarContainsErrors(errs, head(specs).config.warnError) && recurse.code == 0) - then 0 - else 20; - - top.order = 1; -} - -abstract production printAllParsingErrors -top::DriverAction ::= specs::[Decorated RootSpec] -{ - local errs :: [Pair] = head(specs).parsingErrors; - - local i :: IO = - if null(errs) - then top.ioIn - else print("Errors for " ++ head(specs).declaredName ++ "\n" ++ sflatMap(renderMessages(head(specs).grammarSource, _), errs) ++ "\n", top.ioIn); - - local recurse :: DriverAction = printAllParsingErrors(tail(specs)); - recurse.ioIn = i; - - top.io = if null(specs) then top.ioIn else recurse.io; - - top.code = - if null(specs) || (null(errs) && recurse.code == 0) - then 0 - else 21; - - top.order = 0; -} - -function renderMessages -String ::= grammarSource::String msg::Pair -{ - return " [" ++ grammarSource ++ msg.fst ++ "]\n" ++ messagesToString(msg.snd) ++ "\n"; -} - -function grammarContainsErrors -Boolean ::= es::[Pair] werr::Boolean -{ - return if null(es) then false - else containsErrors(head(es).snd, werr) || grammarContainsErrors(tail(es), werr); -} - diff --git a/grammars/silver/driver/util/Compilation.sv b/grammars/silver/driver/util/Compilation.sv deleted file mode 100644 index ef6078440..000000000 --- a/grammars/silver/driver/util/Compilation.sv +++ /dev/null @@ -1,96 +0,0 @@ -grammar silver:driver:util; - -import silver:definition:core only jarName; - -nonterminal Compilation with config, postOps, grammarList, recheckGrammars, allGrammars; - -flowtype postOps {config} on Compilation; - -synthesized attribute postOps :: [DriverAction] with ++; -synthesized attribute grammarList :: [Decorated RootSpec]; -monoid attribute recheckGrammars :: [String] with [], ++; --- This is used on the outside, e.g. the ide functions. -synthesized attribute allGrammars :: [Decorated RootSpec]; - -{-- - - This abstractly represents a compilation. - - Note, in particular, that this does not *DO* any IO at all itself. - - - - However, it does expect some "flow": - - * 'g' should be examined, and then 'top.recheckGrammars' used to provide 'r' - - - - @param g A list of grammar initially read in - - @param r A list of grammars that we re-compiled, due to dirtiness in 'g' - - @param buildGrammar The initial grammar requested built - - @param benv The build configuration - -} -abstract production compilation -top::Compilation ::= g::Grammars r::Grammars buildGrammar::String benv::BuildEnv -{ - -- the list of rootspecs coming out of g - top.grammarList = g.grammarList; - -- the list of grammars that should be re-checked - top.recheckGrammars := g.recheckGrammars; - - g.compiledGrammars = directBuildTree(map(grammarPairing, g.grammarList)); - -- However, we are then forced to use the interface files that we are going to - -- recheck in the .compiledGrammars for the recheck. - -- That means they don't see "themselves" but their previous interface file. - r.compiledGrammars = g.compiledGrammars; - -- This *should* be okay, because the information should be identical in both. - - -- This determines what is actually needed in this build. - -- For example, it excludes "options" and conditional builds that aren't - -- actually used / triggered. - production grammarsDependedUpon :: [String] = - expandAllDeps([buildGrammar], [], g.compiledGrammars); - - -- Ditto the above, but rootspecs - production grammarsRelevant :: [Decorated RootSpec] = - keepGrammars(grammarsDependedUpon, g.grammarList); - - -- JUST the grammars read from source, that are relevant, ignoring rechecked grammars - production grammarsToTranslate :: [Decorated RootSpec] = - keepGrammars(grammarsDependedUpon, g.translateGrammars); - - top.allGrammars = g.grammarList ++ r.grammarList; - - top.postOps := []; -} - -nonterminal Grammars with config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, grammarList, recheckGrammars, translateGrammars, jarName; - -propagate translateGrammars, recheckGrammars, jarName on Grammars; - -abstract production consGrammars -top::Grammars ::= h::RootSpec t::Grammars -{ - top.grammarList = h :: t.grammarList; -} - -abstract production nilGrammars -top::Grammars ::= -{ - top.grammarList = []; -} - -{-- - - Returns a pair, suitable for building an environment - -} -function grammarPairing -Pair ::= r::Decorated RootSpec -{ - return pair(r.declaredName, r); -} - -{-- - - Keep only a selected set of grammars. - - @param keep The set of grammars to keep - - @param d The list of grammars to filter - -} -function keepGrammars -[Decorated RootSpec] ::= keep::[String] d::[Decorated RootSpec] -{ - return if null(d) then [] else (if contains(head(d).declaredName, keep) then [head(d)] else []) ++ keepGrammars(keep, tail(d)); -} - diff --git a/grammars/silver/driver/util/FlowTypes.sv b/grammars/silver/driver/util/FlowTypes.sv deleted file mode 100644 index a2e4be5a2..000000000 --- a/grammars/silver/driver/util/FlowTypes.sv +++ /dev/null @@ -1,59 +0,0 @@ -grammar silver:driver:util; - -import silver:definition:flow:driver; -import silver:definition:flow:ast; -import silver:definition:flow:env; -import silver:util:raw:treemap as rtm; -import silver:util:raw:graph as g; - --- Hide all the flow type computation over here - -aspect production compilation -top::Compilation ::= g::Grammars r::Grammars buildGrammar::String benv::BuildEnv -{ - -- aggregate all flow def information - local allFlowDefs :: FlowDefs = foldr(consFlow, nilFlow(), flatMap((.flowDefs), g.grammarList)); - local allFlowEnv :: Decorated FlowEnv = fromFlowDefs(allFlowDefs); - - -- Look up tree for production info - local prodTree :: EnvTree = directBuildTree(allFlowDefs.prodGraphContribs); - - -- We need to know about all attributes and occurences on nonterminals. - -- It's possible (likely) we could do better than using the overall env here. - local allRealDefs :: [Def] = flatMap((.defs), g.grammarList); - local allRealOccursDefs :: [DclInfo] = flatMap((.occursDefs), g.grammarList); - local allRealEnv :: Decorated Env = occursEnv(allRealOccursDefs, toEnv(allRealDefs)); - - -- List of all productions - local allProds :: [DclInfo] = foldr(consDefs, nilDefs(), allRealDefs).prodDclList; - local allNts :: [String] = nubBy(stringEq, map(getProdNt, allProds)); - - -- Construct production graphs. - production prodGraph :: [ProductionGraph] = - computeAllProductionGraphs(allProds, prodTree, allFlowEnv, allRealEnv) ++ - -- Add in phantom graphs - map(constructPhantomProductionGraph(_, allFlowEnv, allRealEnv), allNts); - - local initialFT :: EnvTree = - computeInitialFlowTypes(allFlowDefs); - - -- Now, solve for flow types!! - local flowTypes1 :: Pair<[ProductionGraph] EnvTree> = - fullySolveFlowTypes(prodGraph, initialFT); - - production flowTypes :: EnvTree = flowTypes1.snd; - production finalGraphs :: [ProductionGraph] = flowTypes1.fst; - production finalGraphEnv :: EnvTree = directBuildTree(map(prodGraphToEnv, finalGraphs)); - - g.productionFlowGraphs = finalGraphEnv; - g.grammarFlowTypes = flowTypes; - - r.productionFlowGraphs = finalGraphEnv; - r.grammarFlowTypes = flowTypes; -} - -function getProdNt -String ::= d::DclInfo -{ - return d.namedSignature.outputElement.typerep.typeName; -} diff --git a/grammars/silver/driver/util/GrammarAction.sv b/grammars/silver/driver/util/GrammarAction.sv deleted file mode 100644 index 10c1433da..000000000 --- a/grammars/silver/driver/util/GrammarAction.sv +++ /dev/null @@ -1,48 +0,0 @@ -grammar silver:driver:util; - -closed nonterminal DriverAction with ioIn, io, code, order; - -synthesized attribute code :: Integer; -synthesized attribute order :: Integer; -inherited attribute ioIn :: IO; - -abstract production wrapUnit -top::DriverAction ::= f::(IOVal ::= IO) order::Integer -{ - local call :: IOVal = f(top.ioIn); - top.io = call.io; - top.code = call.iovalue; - top.order = order; -} - -{-- - - Run units until a non-zero error code is encountered. - -} -function runAll -IOVal ::= l::[DriverAction] i::IO -{ - local now :: DriverAction = head(l); - now.ioIn = i; - - return if unsafeTrace(null(l), i) -- TODO: this is just to force strictness... - then ioval(i, 0) - else if now.code != 0 - then ioval(now.io, now.code) - else runAll(tail(l), now.io); -} - -{-- - - Sorts a list of Units by priority order. (small to large) - -} -function sortUnits -[DriverAction] ::= c1::[DriverAction] -{ - return sortBy(unitLTE, c1); -} -function unitLTE -Boolean ::= l::DriverAction r::DriverAction -{ - return l.order <= r.order; -} - - diff --git a/grammars/silver/driver/util/RootSpec.sv b/grammars/silver/driver/util/RootSpec.sv deleted file mode 100644 index 80aa1f600..000000000 --- a/grammars/silver/driver/util/RootSpec.sv +++ /dev/null @@ -1,341 +0,0 @@ -grammar silver:driver:util; - -import silver:reflect; -import silver:langutil only pp; -import silver:langutil:pp only show; - -import silver:definition:core only Grammar, grammarErrors, grammarName, importedDefs, importedOccursDefs, grammarDependencies, globalImports, Message, err; -import silver:definition:flow:env only flowEnv, flowDefs, fromFlowDefs; -import silver:definition:flow:ast only nilFlow, consFlow, FlowDef; - -import silver:definition:core only jarName; - -{-- - - A representation of a grammar, from an unknown source. TODO: rename GrammarSpec - -} -nonterminal RootSpec with - -- compiler-wide inherited attributes - config, compiledGrammars, productionFlowGraphs, grammarFlowTypes, - -- synthesized attributes - declaredName, moduleNames, exportedGrammars, optionalGrammars, condBuild, allGrammarDependencies, - defs, occursDefs, grammarErrors, grammarSource, grammarTime, interfaceTime, recheckGrammars, translateGrammars, - parsingErrors, jarName, generateLocation; - - -{-- - - Grammars that were read from source. - -} -monoid attribute translateGrammars :: [Decorated RootSpec] with [], ++; - -{-- - - Parse errors present in this grammar (only for errorRootSpec!) - -} -synthesized attribute parsingErrors :: [Pair]; - -{-- Where generated files are or should be created -} -synthesized attribute generateLocation :: String; - -{-- - - Create a RootSpec from a real grammar, a set of .sv files. - -} -abstract production grammarRootSpec -top::RootSpec ::= g::Grammar grammarName::String grammarSource::String grammarTime::Integer generateLocation::String -{ - g.grammarName = grammarName; - - -- Create the environments for this grammar - g.env = occursEnv(g.occursDefs, toEnv(g.defs)); - g.globalImports = - occursEnv( - if contains("core", g.moduleNames) || grammarName == "core" then g.importedOccursDefs - else g.importedOccursDefs ++ head(searchEnvTree("core", top.compiledGrammars)).occursDefs, - toEnv( - if contains("core", g.moduleNames) || grammarName == "core" then g.importedDefs - else g.importedDefs ++ head(searchEnvTree("core", top.compiledGrammars)).defs)); - - -- This grammar, its direct imports, and only transitively close over exports and TRIGGERED conditional imports. - -- i.e. these are the things that we really, truly depend upon. (in the sense that we get their symbols) - local actualDependencies :: [String] = - makeSet(computeDependencies(grammarName :: top.moduleNames, top.compiledGrammars)); - - -- Compute flow information for this grammar, (closing over imports and options, too:) - local depsPlusOptions :: [String] = - makeSet(completeDependencyClosure(actualDependencies, top.compiledGrammars)); - g.grammarDependencies = actualDependencies; - g.flowEnv = fromFlowDefs(foldr(consFlow, nilFlow(), gatherFlowEnv(depsPlusOptions, top.compiledGrammars))); - - -- Echo down global compiler info - g.config = top.config; - g.compiledGrammars = top.compiledGrammars; - - top.grammarSource = grammarSource; - top.grammarTime = grammarTime; - top.interfaceTime = grammarTime; - top.generateLocation = generateLocation; - top.recheckGrammars := []; - top.translateGrammars := [top]; - - top.declaredName = g.declaredName; - top.moduleNames := makeSet(g.moduleNames ++ ["core"]); -- Ensure the prelude is in the deps, always - top.exportedGrammars := g.exportedGrammars; - top.optionalGrammars := g.optionalGrammars; - top.condBuild := g.condBuild; - top.allGrammarDependencies := actualDependencies; - - top.defs := g.defs; - top.occursDefs := g.occursDefs; - top.grammarErrors = g.grammarErrors; - top.parsingErrors = []; - - top.jarName := g.jarName; -} - -{-- - - Create a RootSpec from an interface file, representing a grammar. - -} -abstract production interfaceRootSpec -top::RootSpec ::= i::InterfaceItems interfaceTime::Integer generateLocation::String -{ - top.grammarSource = i.maybeGrammarSource.fromJust; - top.grammarTime = i.maybeGrammarTime.fromJust; - top.interfaceTime = interfaceTime; - top.generateLocation = generateLocation; - - local ood :: Boolean = isOutOfDate(interfaceTime, top.allGrammarDependencies, top.compiledGrammars); - top.recheckGrammars := if ood then [i.maybeDeclaredName.fromJust] else []; - top.translateGrammars := []; - - top.declaredName = i.maybeDeclaredName.fromJust; - top.moduleNames := i.maybeModuleNames.fromJust; - top.exportedGrammars := i.maybeExportedGrammars.fromJust; - top.optionalGrammars := i.maybeOptionalGrammars.fromJust; - top.condBuild := i.maybeCondBuild.fromJust; - top.allGrammarDependencies := i.maybeAllGrammarDependencies.fromJust; - - top.defs := i.maybeDefs.fromJust; - top.occursDefs := i.maybeOccursDefs.fromJust; - top.grammarErrors = []; -- TODO: consider getting grammarName and comparing against declaredName? - top.parsingErrors = []; - - top.jarName := nothing(); -} - -{-- - - A RootSpec that represents a failure to parse (part) of a grammar. - -} -abstract production errorRootSpec -top::RootSpec ::= e::[ParseError] grammarName::String grammarSource::String grammarTime::Integer generateLocation::String -{ - top.grammarSource = grammarSource; - top.grammarTime = grammarTime; - top.interfaceTime = grammarTime; - top.generateLocation = generateLocation; - - top.recheckGrammars := []; - top.translateGrammars := []; - - top.declaredName = grammarName; - top.moduleNames := []; - top.exportedGrammars := []; - top.optionalGrammars := []; - top.condBuild := []; - top.allGrammarDependencies := []; - - top.defs := []; - top.occursDefs := []; - top.grammarErrors = []; - top.parsingErrors = map(parseErrorToMessage(grammarSource, _), e); - - top.jarName := nothing(); -} - -function parseErrorToMessage -Pair ::= grammarSource::String e::ParseError -{ - return case e of - | syntaxError(str, locat, _, _) -> - pair(locat.filename, - [err(locat, - "Syntax error:\n" ++ str)]) - | unknownParseError(str, file) -> - pair(file, - [err(loc(grammarSource ++ file, -1, -1, -1, -1, -1, -1), - "Unknown error while parsing:\n" ++ str)]) - end; -} - -monoid attribute maybeGrammarSource::Maybe with nothing(), orElse; -monoid attribute maybeGrammarTime::Maybe with nothing(), orElse; -monoid attribute maybeDeclaredName::Maybe with nothing(), orElse; -monoid attribute maybeModuleNames::Maybe<[String]> with nothing(), orElse; -monoid attribute maybeExportedGrammars::Maybe<[String]> with nothing(), orElse; -monoid attribute maybeOptionalGrammars::Maybe<[String]> with nothing(), orElse; -monoid attribute maybeCondBuild::Maybe<[[String]]> with nothing(), orElse; -monoid attribute maybeAllGrammarDependencies::Maybe<[String]> with nothing(), orElse; -monoid attribute maybeDefs::Maybe<[Def]> with nothing(), orElse; -monoid attribute maybeOccursDefs::Maybe<[DclInfo]> with nothing(), orElse; - -monoid attribute interfaceErrors::[String] with [], ++; - -{-- - - Representation of all properties of a grammar, to be serialized/deserialize to/from an interface - - file. - -} -nonterminal InterfaceItems with maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, maybeModuleNames, maybeExportedGrammars, maybeOptionalGrammars, maybeCondBuild, maybeAllGrammarDependencies, maybeDefs, maybeOccursDefs, interfaceErrors; - -propagate maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, maybeModuleNames, maybeExportedGrammars, maybeOptionalGrammars, maybeCondBuild, maybeAllGrammarDependencies, maybeDefs, maybeOccursDefs - on InterfaceItems; - -abstract production consInterfaceItem -top::InterfaceItems ::= h::InterfaceItem t::InterfaceItems -{ - top.interfaceErrors := []; - top.interfaceErrors <- if !top.maybeGrammarSource.isJust then ["Missing item grammarSource"] else []; - top.interfaceErrors <- if !top.maybeGrammarTime.isJust then ["Missing item grammarTime"] else []; - top.interfaceErrors <- if !top.maybeDeclaredName.isJust then ["Missing item declaredName"] else []; - top.interfaceErrors <- if !top.maybeModuleNames.isJust then ["Missing item moduleNames"] else []; - top.interfaceErrors <- if !top.maybeExportedGrammars.isJust then ["Missing item exportedGrammars"] else []; - top.interfaceErrors <- if !top.maybeOptionalGrammars.isJust then ["Missing item optionalGrammars"] else []; - top.interfaceErrors <- if !top.maybeCondBuild.isJust then ["Missing item condBuild"] else []; - top.interfaceErrors <- if !top.maybeAllGrammarDependencies.isJust then ["Missing item allGrammarDependencies"] else []; - top.interfaceErrors <- if !top.maybeDefs.isJust then ["Missing item defs"] else []; - top.interfaceErrors <- if !top.maybeOccursDefs.isJust then ["Missing item occursDefs"] else []; -} - -abstract production nilInterfaceItem -top::InterfaceItems ::= -{ - top.interfaceErrors := ["Missing all items"]; -} - -closed nonterminal InterfaceItem with maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, maybeModuleNames, maybeExportedGrammars, maybeOptionalGrammars, maybeCondBuild, maybeAllGrammarDependencies, maybeDefs, maybeOccursDefs; - -aspect default production -top::InterfaceItem ::= -{ - -- Empty values as defaults - propagate maybeGrammarSource, maybeGrammarTime, maybeDeclaredName, maybeModuleNames, maybeExportedGrammars, maybeOptionalGrammars, maybeCondBuild, maybeAllGrammarDependencies, maybeDefs, maybeOccursDefs; -} - -abstract production grammarSourceInterfaceItem -top::InterfaceItem ::= val::String -{ - top.maybeGrammarSource := just(val); -} - -abstract production grammarTimeInterfaceItem -top::InterfaceItem ::= val::Integer -{ - top.maybeGrammarTime := just(val); -} - -abstract production declaredNameInterfaceItem -top::InterfaceItem ::= val::String -{ - top.maybeDeclaredName := just(val); -} - -abstract production moduleNamesInterfaceItem -top::InterfaceItem ::= val::[String] -{ - top.maybeModuleNames := just(val); -} - -abstract production exportedGrammarsInterfaceItem -top::InterfaceItem ::= val::[String] -{ - top.maybeExportedGrammars := just(val); -} - -abstract production optionalGrammarsInterfaceItem -top::InterfaceItem ::= val::[String] -{ - top.maybeOptionalGrammars := just(val); -} - -abstract production condBuildInterfaceItem -top::InterfaceItem ::= val::[[String]] -{ - top.maybeCondBuild := just(val); -} - -abstract production allDepsInterfaceItem -top::InterfaceItem ::= val::[String] -{ - top.maybeAllGrammarDependencies := just(val); -} - -abstract production defsInterfaceItem -top::InterfaceItem ::= val::[Def] -{ - top.maybeDefs := just(val); -} - -abstract production occursDefsInterfaceItem -top::InterfaceItem ::= val::[DclInfo] -{ - top.maybeOccursDefs := just(val); -} - -{-- - - How RootSpecs are turned into interface files shouldn't change - - depending on what the source it, so we give this function externally - - to the productions, instead of as an attribute. - -} -function unparseRootSpec -String ::= r::Decorated RootSpec -{ - production attribute interfaceItems :: [InterfaceItem] with ++; - interfaceItems := [ - grammarSourceInterfaceItem(r.grammarSource), - grammarTimeInterfaceItem(r.grammarTime), - declaredNameInterfaceItem(r.declaredName), - moduleNamesInterfaceItem(r.moduleNames), - exportedGrammarsInterfaceItem(r.exportedGrammars), - optionalGrammarsInterfaceItem(r.optionalGrammars), - condBuildInterfaceItem(r.condBuild), - allDepsInterfaceItem(r.allGrammarDependencies), - defsInterfaceItem(r.defs), - occursDefsInterfaceItem(r.occursDefs) - ]; - - return - case serialize(foldr(consInterfaceItem, nilInterfaceItem(), interfaceItems)) of - | left(msg) -> error("Fatal internal error generating interface file: \n" ++ show(80, reflect(foldr(consInterfaceItem, nilInterfaceItem(), interfaceItems)).pp) ++ "\n" ++ msg) - | right(txt) -> txt - end; -} - -{-- - - All grammar names mentioned by this root spec (not transitive!) - -} -function mentionedGrammars -[String] ::= r::Decorated RootSpec -{ - return makeSet(r.moduleNames ++ concat(r.condBuild) ++ r.optionalGrammars); -} - -function gatherFlowEnv -[FlowDef] ::= deps::[String] e::EnvTree -{ - return if null(deps) then [] - else case searchEnvTree(head(deps), e) of - | r :: _ -> r.flowDefs ++ gatherFlowEnv(tail(deps), e) - | [] -> gatherFlowEnv(tail(deps), e) - end; -} - --- We're comparing INTERFACE TIME against GRAMMAR TIME, just to emphasize what's going on here... -function isOutOfDate -Boolean ::= mine::Integer l::[String] e::EnvTree -{ - local n :: [Decorated RootSpec] = searchEnvTree(head(l), e); - - return if null(l) then - false - else if null(n) || mine >= head(n).grammarTime then - isOutOfDate(mine, tail(l), e) - else - true; -} - diff --git a/grammars/silver/driver/util/Util.sv b/grammars/silver/driver/util/Util.sv deleted file mode 100644 index 75a982816..000000000 --- a/grammars/silver/driver/util/Util.sv +++ /dev/null @@ -1,14 +0,0 @@ -grammar silver:driver:util; - -imports silver:definition:env; -imports silver:util only contains, rem, makeSet, containsAny; - -{-- - - Turns a grammar name into a path, including trailing slash. - -} -function grammarToPath -String ::= g::String -{ - return substitute(":", "/", g) ++ "/"; -} - diff --git a/grammars/silver/extension/DocConfig.sv b/grammars/silver/extension/DocConfig.sv deleted file mode 100644 index 7fe6837ed..000000000 --- a/grammars/silver/extension/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Extensions\nmenu_title: Extensions\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/astconstruction/Syntax.sv b/grammars/silver/extension/astconstruction/Syntax.sv deleted file mode 100644 index 6c9c76f71..000000000 --- a/grammars/silver/extension/astconstruction/Syntax.sv +++ /dev/null @@ -1,83 +0,0 @@ -grammar silver:extension:astconstruction; - -imports silver:langutil:pp; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type:syntax; -imports silver:extension:list; -imports silver:extension:patternmatching; - -exports silver:reflect:concretesyntax; - -concrete production quoteAST -top::Expr ::= 'AST' '{' ast::AST_c '}' -layout {silver:reflect:concretesyntax:WhiteSpace} -{ - top.unparse = s"AST {${ast.unparse}}"; - forwards to translate(top.location, reflect(ast.ast)); -} - -concrete production quoteASTPattern -top::Pattern ::= 'AST' '{' ast::AST_c '}' -layout {silver:reflect:concretesyntax:WhiteSpace} -{ - top.unparse = s"AST {${ast.unparse}}"; - forwards to translatePattern(top.location, reflect(ast.ast)); -} - -concrete production antiquoteAST_c -top::AST_c ::= '$' '{' e::Expr '}' -layout {silver:definition:core:WhiteSpace} -{ - top.unparse = s"$${${e.unparse}}"; - top.ast = antiquoteAST(e); - top.errors := []; -} - -concrete production varAST_c -top::AST_c ::= n::QName_t -{ - top.unparse = n.lexeme; - top.ast = antiquotePatternAST(varPattern(name(n.lexeme, n.location), location=top.location)); - top.errors := - if indexOf(":", n.lexeme) != -1 - then [err(n.location, "Pattern variable name must be unqualified")] - else []; -} - -concrete production wildAST_c -top::AST_c ::= '_' -{ - top.unparse = "_"; - top.ast = antiquotePatternAST(wildcPattern('_', location=top.location)); - top.errors := []; -} - -abstract production antiquoteAST -top::AST ::= e::Expr -{ - top.translation = - errorExpr( - [err(top.givenLocation, "${} should only occur inside AST { } expression")], - location=top.givenLocation); - top.patternTranslation = - errorPattern( - [err(top.givenLocation, "${} should only occur inside AST { } expression")], - location=top.givenLocation); - forwards to error("forward shouldn't be needed here"); -} - -abstract production antiquotePatternAST -top::AST ::= p::Pattern -{ - top.translation = - errorExpr( - [err(top.givenLocation, "Variable and wildcard patterns should only occur inside AST { } pattern")], - location=top.givenLocation); - top.patternTranslation = - errorPattern( - [err(top.givenLocation, "Variable and wildcard patterns should only occur inside AST { } pattern")], - location=top.givenLocation); - forwards to error("forward shouldn't be needed here"); -} diff --git a/grammars/silver/extension/astconstruction/Terminals.sv b/grammars/silver/extension/astconstruction/Terminals.sv deleted file mode 100644 index 6107efa36..000000000 --- a/grammars/silver/extension/astconstruction/Terminals.sv +++ /dev/null @@ -1,12 +0,0 @@ -grammar silver:extension:astconstruction; - -marking terminal AST_t 'AST' lexer classes {KEYWORD}; - -temp_imp_ide_font font_escape color(160, 32, 240) bold italic; -lexer class Escape font=font_escape; - -terminal EscapeAST_t '$' lexer classes {Escape}; - -disambiguate AST_t, IdUpper_t { - pluck AST_t; -} diff --git a/grammars/silver/extension/astconstruction/Translation.sv b/grammars/silver/extension/astconstruction/Translation.sv deleted file mode 100644 index d283ae830..000000000 --- a/grammars/silver/extension/astconstruction/Translation.sv +++ /dev/null @@ -1,13 +0,0 @@ -grammar silver:extension:astconstruction; - -imports silver:reflect; -imports silver:metatranslation; - -aspect production nonterminalAST -top::AST ::= prodName::String children::ASTs annotations::NamedASTs -{ - directAntiquoteProductions <- - ["silver:extension:astconstruction:antiquoteAST"]; - patternAntiquoteProductions <- - ["silver:extension:astconstruction:antiquotePatternAST"]; -} diff --git a/grammars/silver/extension/autoattr/DclInfo.sv b/grammars/silver/extension/autoattr/DclInfo.sv deleted file mode 100644 index b60d4d545..000000000 --- a/grammars/silver/extension/autoattr/DclInfo.sv +++ /dev/null @@ -1,54 +0,0 @@ -grammar silver:extension:autoattr; - -synthesized attribute propagateDispatcher :: (ProductionStmt ::= Decorated QName Location) occurs on DclInfo; - -synthesized attribute emptyVal::Expr occurs on DclInfo; - -aspect default production -top::DclInfo ::= -{ - top.propagateDispatcher = propagateError(_, location=_); - top.emptyVal = error("Internal compiler error: must be defined for all monoid attribute declarations"); -} - -abstract production functorDcl -top::DclInfo ::= sg::String sl::Location fn::String tyVar::TyVar -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = varType(tyVar); - top.dclBoundVars = [tyVar]; - top.isSynthesized = true; - - top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); - top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations - top.attributionDispatcher = functorAttributionDcl(_, _, _, _, location=_); - top.propagateDispatcher = propagateFunctor(_, location=_); -} - -abstract production monoidDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type empty::Expr append::Operation -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isSynthesized = true; - top.emptyVal = empty; - top.operation = append; - - top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); - top.attrDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, attr.name ++ " is a monoid collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); - top.attrBaseDefDispatcher = synBaseColAttributeDef(_, _, _, location=_); - top.attrAppendDefDispatcher = synAppendColAttributeDef(_, _, _, location=_); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); - top.propagateDispatcher = propagateMonoid(_, location=_); -} diff --git a/grammars/silver/extension/autoattr/Functor.sv b/grammars/silver/extension/autoattr/Functor.sv deleted file mode 100644 index 2b0a9a02c..000000000 --- a/grammars/silver/extension/autoattr/Functor.sv +++ /dev/null @@ -1,126 +0,0 @@ -grammar silver:extension:autoattr; - -concrete production functorAttributeDcl -top::AGDcl ::= 'functor' 'attribute' a::Name ';' -{ - top.unparse = "functor attribute " ++ a.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; - - forwards to - defsAGDcl( - [attrDef(defaultEnvItem(functorDcl(top.grammarName, a.location, fName, freshTyVar())))], - location=top.location); -} - -abstract production functorAttributionDcl -top::AGDcl ::= at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - top.unparse = "attribute " ++ at.unparse ++ attl.unparse ++ " occurs on " ++ nt.unparse ++ nttl.unparse ++ ";"; - forwards to - defaultAttributionDcl( - at, - if length(attl.types) > 0 - then attl - else - botlSome( - '<', - typeListSingle( - nominalTypeExpr(nt.qNameType, nttl, location=top.location), - location=top.location), - '>', location=top.location), - nt, nttl, - location=top.location); -} - -{-- - - Propagate a functor attribute on the enclosing production - - @param attr The name of the attribute to propagate - -} -abstract production propagateFunctor -top::ProductionStmt ::= attr::Decorated QName -{ - top.unparse = s"propagate ${attr.unparse};"; - - -- No explicit errors, for now. The only conceivable issue is the attribute not - -- occuring on the LHS but this should be caught by the forward errors. - - -- Generate the arguments for the constructor - local inputs :: [Expr] = - map(makeArg(top.location, top.env, attr, _), top.frame.signature.inputElements); - local annotations :: [Pair] = - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements); - - -- Construct an attribute def and call with the generated arguments - forwards to - attributeDef( - concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), - '.', - qNameAttrOccur(new(attr), location=top.location), - '=', - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - inputs, - annotations), - ';', - location=top.location); -} - -{-- - - Generates the expression we should use for an argument - - @param loc The parent location to use in construction - - @param env The environment - - @param attrName The name of the attribute being propagated - - @param input The NamedSignatureElement being propagated - - @return Either this the child, or accessing `attrName` on the child - -} -function makeArg -Expr ::= loc::Location env::Decorated Env attrName::Decorated QName input::NamedSignatureElement -{ - local at::QName = qName(loc, input.elementName); - at.env = env; - - -- Check if the attribute occurs on the first child - local attrOccursOnHead :: Boolean = - !null(getOccursDcl(attrName.lookupAttribute.dcl.fullName, input.typerep.typeName, env)); - local validTypeHead :: Boolean = input.typerep.isDecorable; - - return - if validTypeHead && attrOccursOnHead - then access( - baseExpr(at, location=loc), '.', - qNameAttrOccur(new(attrName), location=loc), - location=loc) - else baseExpr(at, location=loc); -} - -{-- - - Generates the list of AnnoExprs used in calling the constructor - - @param loc The parent location to use in construction - - @param baseName The name of the parent from the signature - - @param input The NamedSignatureElement for an annotation - - @return A list of AnnoExprs to be used to build the named arguments - -} -function makeAnnoArg -Pair ::= loc::Location baseName::String input::NamedSignatureElement -{ - -- TODO: This is a hacky way of getting the base name, not sure if correct - -- trouble is the annotations are listed as fullnames, but have to be supplied as shortnames. weird. - local annoName :: String = last(explode(":", input.elementName)); - - return - pair(annoName, - access( - baseExpr(qName(loc, baseName), location=loc), '.', - qNameAttrOccur(qName(loc, annoName), location=loc), - location=loc)); -} diff --git a/grammars/silver/extension/autoattr/Monoid.sv b/grammars/silver/extension/autoattr/Monoid.sv deleted file mode 100644 index 3ca28d880..000000000 --- a/grammars/silver/extension/autoattr/Monoid.sv +++ /dev/null @@ -1,121 +0,0 @@ -grammar silver:extension:autoattr; - -concrete production monoidAttributeDcl -top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' e::Expr ',' q::NameOrBOperator ';' -{ - top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ e.unparse ++ ", " ++ q.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - q.operatorForType = te.typerep; - - -- TODO: We want to define our own defs here but can't forward to defsAGDcl because collections define different translation. - -- Not sure about the best way to refactor this. - top.defs := - [attrDef(defaultEnvItem(monoidDcl(top.grammarName, a.location, fName, tl.freeVariables, te.typerep, e, q.operation)))]; - - top.errors <- e.errors; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; - - local errCheck1 :: TypeCheck = check(e.typerep, te.typerep); - top.errors <- - if errCheck1.typeerror - then [err(e.location, "Monoid attribute " ++ fName ++ " of type " ++ errCheck1.rightpp ++ " has empty value specified with type " ++ errCheck1.leftpp)] - else []; - - e.downSubst = emptySubst(); - errCheck1.downSubst = e.upSubst; - - errCheck1.finalSubst = errCheck1.upSubst; - e.finalSubst = errCheck1.upSubst; - - forwards to - collectionAttributeDclSyn( - 'synthesized', 'attribute', a, tl, '::', te, 'with', q, ';', - location=top.location); -} - -synthesized attribute appendProd :: (Expr ::= Expr Expr Location) occurs on Operation; - -aspect production functionOperation -top::Operation ::= s::String -{ - top.appendProd = \ e1::Expr e2::Expr l::Location -> mkStrFunctionInvocation(l, s, [e1, e2]); -} -aspect production productionOperation -top::Operation ::= s::String -{ - top.appendProd = \ e1::Expr e2::Expr l::Location -> mkStrFunctionInvocation(l, s, [e1, e2]); -} -aspect production plusPlusOperationString -top::Operation ::= -{ - top.appendProd = plusPlus(_, '++', _, location=_); -} -aspect production plusPlusOperationList -top::Operation ::= -{ - top.appendProd = plusPlus(_, '++', _, location=_); -} -aspect production borOperation -top::Operation ::= -{ - top.appendProd = or(_, '||', _, location=_); -} -aspect production bandOperation -top::Operation ::= -{ - top.appendProd = and(_, '&&', _, location=_); -} - -{-- - - Propagate a monoid attribute on the enclosing production - - @param attr The name of the attribute to propagate - -} -abstract production propagateMonoid -top::ProductionStmt ::= attr::Decorated QName -{ - top.unparse = s"propagate ${attr.unparse};"; - - -- No explicit errors, for now. The only conceivable issue is the attribute not - -- occuring on the LHS but this should be caught by the forward errors. - - local attrFullName::String = attr.lookupAttribute.dcl.fullName; - local inputsWithAttr::[NamedSignatureElement] = - filter( - \ input::NamedSignatureElement -> - input.typerep.isDecorable && - !null(getOccursDcl(attrFullName, input.typerep.typeName, top.env)), - top.frame.signature.inputElements); - local res :: Expr = - if null(inputsWithAttr) - then attr.lookupAttribute.dcl.emptyVal - else - foldr1( - attr.lookupAttribute.dcl.operation.appendProd(_, _, top.location), - map( - \ i::NamedSignatureElement -> - access( - baseExpr(qName(top.location, i.elementName), location=top.location), - '.', - qNameAttrOccur(new(attr), location=top.location), - location=top.location), - inputsWithAttr)); - - -- Construct an attribute def and call with the generated arguments - forwards to - attrContainsBase( - concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), - '.', - qNameAttrOccur(new(attr), location=top.location), - ':=', res, ';', location=top.location); -} diff --git a/grammars/silver/extension/autoattr/Project.sv b/grammars/silver/extension/autoattr/Project.sv deleted file mode 100644 index 70624d0a8..000000000 --- a/grammars/silver/extension/autoattr/Project.sv +++ /dev/null @@ -1,9 +0,0 @@ -grammar silver:extension:autoattr; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:modification:collection; - -exports silver:extension:autoattr:convenience; diff --git a/grammars/silver/extension/autoattr/Terminals.sv b/grammars/silver/extension/autoattr/Terminals.sv deleted file mode 100644 index 03433e4f7..000000000 --- a/grammars/silver/extension/autoattr/Terminals.sv +++ /dev/null @@ -1,7 +0,0 @@ -grammar silver:extension:autoattr; - -terminal Propagate_kwd 'propagate' lexer classes {KEYWORD,RESERVED}; -terminal Excluding_kwd 'excluding' lexer classes {KEYWORD}; - -terminal Functor_kwd 'functor' lexer classes {KEYWORD,RESERVED}; -terminal Monoid_kwd 'monoid' lexer classes {KEYWORD,RESERVED}; diff --git a/grammars/silver/extension/autoattr/convenience/Convenience.sv b/grammars/silver/extension/autoattr/convenience/Convenience.sv deleted file mode 100644 index c62431a10..000000000 --- a/grammars/silver/extension/autoattr/convenience/Convenience.sv +++ /dev/null @@ -1,32 +0,0 @@ -grammar silver:extension:autoattr:convenience; - -import silver:extension:autoattr; -import silver:extension:convenience; -import silver:modification:collection; -import silver:definition:core; -import silver:definition:concrete_syntax; -import silver:definition:type:syntax; -import silver:definition:type; -import silver:definition:env; - -concrete production functorAttributeDclMultiple -top::AGDcl ::= 'functor' 'attribute' a::Name 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "functor attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; - forwards to - appendAGDcl( - functorAttributeDcl($1, $2, a, $7, location=a.location), - makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), - location=top.location); -} - -concrete production monoidAttributeDclMultiple -top::AGDcl ::= 'monoid' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' e::Expr ',' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "monoid attribute " ++ a.unparse ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ e.unparse ++ ", " ++ q.unparse ++ " occurs on " ++ qs.unparse ++ ";"; - forwards to - appendAGDcl( - monoidAttributeDcl($1, $2, a, tl, $5, te, $7, e, $9, q, $14, location=a.location), - makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), - location=top.location); -} diff --git a/grammars/silver/extension/constructparser/DocConfig.sv b/grammars/silver/extension/constructparser/DocConfig.sv deleted file mode 100644 index f36cd029c..000000000 --- a/grammars/silver/extension/constructparser/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension:constructparser; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Construct Parser\nmenu_title: Construct Parser\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/convenience/Convenience.sv b/grammars/silver/extension/convenience/Convenience.sv deleted file mode 100644 index 3586cfa50..000000000 --- a/grammars/silver/extension/convenience/Convenience.sv +++ /dev/null @@ -1,131 +0,0 @@ -grammar silver:extension:convenience; - -imports silver:definition:env; -imports silver:definition:core; -imports silver:definition:concrete_syntax; -imports silver:definition:type; -imports silver:definition:type:syntax; -import silver:modification:collection; - --- Multiple attribute occurs on statements -concrete production multipleAttributionDclsManyMany -top::AGDcl ::= 'attribute' a::QNames2 'occurs' 'on' nts::QNames2 ';' -{ - top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, a.qnames, nts.qnames); -} -concrete production multipleAttributionDclsSingleMany -top::AGDcl ::= 'attribute' a::QName tl::BracketedOptTypeExprs 'occurs' 'on' nts::QNames2 ';' -{ - top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, [qNameWithTL(a, tl)], nts.qnames); -} -concrete production multipleAttributionDclsManySingle -top::AGDcl ::= 'attribute' a::QNames2 'occurs' 'on' nts::QNameWithTL ';' -{ - top.unparse = "attribute " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, a.qnames, [nts]); -} - --- Multiple annotation occurs on statements -concrete production multipleAnnotationDclsManyMany -top::AGDcl ::= 'annotation' a::QNames2 'occurs' 'on' nts::QNames2 ';' -{ - top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, a.qnames, nts.qnames); -} -concrete production multipleAnnotationDclsSingleMany -top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs 'occurs' 'on' nts::QNames2 ';' -{ - top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, [qNameWithTL(a, tl)], nts.qnames); -} -concrete production multipleAnnotationDclsManySingle -top::AGDcl ::= 'annotation' a::QNames2 'occurs' 'on' nts::QNameWithTL ';' -{ - top.unparse = "annotation " ++ a.unparse ++ " occurs on " ++ nts.unparse ++ " ;" ; - forwards to makeOccursDcls(top.location, a.qnames, [nts]); -} - - -concrete production nonterminalWithDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers 'with' attrs::QNames ';' -{ - top.unparse = "nonterminal " ++ id.unparse ++ tl.unparse ++ " " ++ nm.unparse ++ " with " ++ attrs.unparse ++ " ;"; - forwards to appendAGDcl( - nonterminalDcl(cl, $2, id, tl, nm, $8, location=top.location), - makeOccursDcls(top.location, attrs.qnames, [qNameWithTL(qNameId(id, location=id.location), tl)]), - location=top.location); -} - - -concrete production attributeDclInhMultiple -top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; - forwards to appendAGDcl( - attributeDclInh($1, $2, a, tl, $5, te, $10, location=top.location), - makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), - location=top.location); -} - -concrete production attributeDclSynMultiple -top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; - forwards to appendAGDcl( - attributeDclSyn($1, $2, a, tl, $5, te, $10, location=top.location), - makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), - location=top.location); -} - -concrete production collectionAttributeDclInhMultiple -top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; - forwards to appendAGDcl( - collectionAttributeDclInh($1, $2, a, tl, $5, te, $7, q, $12, location=top.location), - makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), - location=top.location); -} - -concrete production collectionAttributeDclSynMultiple -top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; - forwards to appendAGDcl( - collectionAttributeDclSyn($1, $2, a, tl, $5, te, $7, q, $12, location=top.location), - makeOccursDclsHelp(top.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), - location=top.location); -} - - - - -{- TEMPORARILY(?) DISABLED this aren't commonly used anyhow - - - - -concrete production nonterminalWithDcl3 -top::AGDcl ::= 'nonterminal' id::Names2 ';' -{ - top.unparse = "nonterminal " ++ id.unparse ++ " ;" ; - forwards to makeNTDcls($1.line, $1.column, id.ids) ; -} -concrete production attributeDclInhMultiple1 -top::AGDcl ::= 'inherited' 'attribute' a::Names2 '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "inherited attribute " ++ a.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; - forwards to appendAGDcl(makeInhDcls($1.line, $1.column, te, a.ids), makeOccursDcls($1.line, $1.column, qualifyNames(a.ids), qs.qnames)); -} -concrete production attributeDclSynMultiple1 -top::AGDcl ::= 'synthesized' 'attribute' a::Names2 '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "synthesized attribute " ++ a.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; - forwards to appendAGDcl(makeSynDcls($1.line, $1.column, te, a.ids), makeOccursDcls($1.line, $1.column, qualifyNames(a.ids), qs.qnames)); -} - - - --} diff --git a/grammars/silver/extension/deprecation/DocConfig.sv b/grammars/silver/extension/deprecation/DocConfig.sv deleted file mode 100644 index d09b9b3a4..000000000 --- a/grammars/silver/extension/deprecation/DocConfig.sv +++ /dev/null @@ -1,5 +0,0 @@ -grammar silver:extension:deprecation; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Deprecation\nmenu_title: Deprecation\nmenu_weight: 100\n---" -@} diff --git a/grammars/silver/extension/deprecation/TypeExpr.sv b/grammars/silver/extension/deprecation/TypeExpr.sv deleted file mode 100644 index 1e23a1378..000000000 --- a/grammars/silver/extension/deprecation/TypeExpr.sv +++ /dev/null @@ -1,20 +0,0 @@ -grammar silver:extension:deprecation; - -import silver:definition:type:syntax; - -terminal Function_tkwd 'Function' lexer classes {TYPE,RESERVED}; -terminal Production_tkwd 'Production' lexer classes {TYPE,RESERVED}; - -concrete production prodTypeExpr -top::TypeExpr ::= 'Production' '(' sig::Signature ')' -{ - top.errors <- [wrn(top.location, "'Production' keyword in types is no longer necessary. Remove it.")]; - forwards to funTypeExpr($2, sig, $4, location=top.location); -} -concrete production funTypeLegacy -top::TypeExpr ::= 'Function' '(' sig::Signature ')' -{ - top.errors <- [wrn(top.location, "'Function' keyword in types is no longer necessary. Remove it.")]; - forwards to funTypeExpr($2, sig, $4, location=top.location); -} - diff --git a/grammars/silver/extension/doc/Project.sv b/grammars/silver/extension/doc/Project.sv deleted file mode 100644 index 4ff2a4cda..000000000 --- a/grammars/silver/extension/doc/Project.sv +++ /dev/null @@ -1,9 +0,0 @@ -grammar silver:extension:doc; - -exports silver:extension:doc:core; -exports silver:extension:doc:driver; - -{@config - no-doc:"true" -@} - diff --git a/grammars/silver/extension/doc/core/Annotation.sv b/grammars/silver/extension/doc/core/Annotation.sv deleted file mode 100644 index 6868092ca..000000000 --- a/grammars/silver/extension/doc/core/Annotation.sv +++ /dev/null @@ -1,26 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production annotationDcl -top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.docs := [ bodilessDclCommentItem("annotation", a.name ++ tl.unparse, te.unparse, - a.location.filename) ]; -} - -concrete production docAnnotationDcl -top::AGDcl ::= comment::DclComment 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.docs := [ dclCommentItem("annotation ", a.name ++ tl.unparse, te.unparse, - a.location.filename, comment) ]; - - forwards to annotationDcl('annotation', a, tl, '::', te, ';', location=top.location); -} - -concrete production noDocAnnotationDcl -top::AGDcl ::= noDoc::NoDclComment_t 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - top.docs := []; - - forwards to annotationDcl('annotation', a, tl, '::', te, ';', location=top.location); -} - diff --git a/grammars/silver/extension/doc/core/AspectDcl.sv b/grammars/silver/extension/doc/core/AspectDcl.sv deleted file mode 100644 index 6bb9a7490..000000000 --- a/grammars/silver/extension/doc/core/AspectDcl.sv +++ /dev/null @@ -1,47 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.docs := [ bodilessDclCommentItem("aspect production", id.name, ns.unparse, - id.location.filename)]; -} - -concrete production docAspectProductionDcl -top::AGDcl ::= comment::DclComment 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.docs := [ dclCommentItem("aspect production", id.name, ns.unparse, - id.location.filename, comment)]; - - forwards to aspectProductionDcl('aspect', 'production', id, ns, body, location=top.location); -} - -concrete production noDocAspectProductionDcl -top::AGDcl ::= noDoc::NoDclComment_t 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.docs := []; - - forwards to aspectProductionDcl('aspect', 'production', id, ns, body, location=top.location); -} - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.docs := [bodilessDclCommentItem("aspect function", id.name, ns.unparse, id.location.filename)]; -} - -concrete production docAspectFunctionDcl -top::AGDcl ::= comment::DclComment 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.docs := [dclCommentItem("aspect function", id.name, ns.unparse, id.location.filename, comment)]; - - forwards to aspectFunctionDcl('aspect', 'function', id, ns, body, location=top.location); -} - -concrete production noDocAspectFunctionDcl -top::AGDcl ::= noDoc::NoDclComment_t 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.docs := []; - - forwards to aspectFunctionDcl('aspect', 'function', id, ns, body, location=top.location); -} diff --git a/grammars/silver/extension/doc/core/CommentItem.sv b/grammars/silver/extension/doc/core/CommentItem.sv deleted file mode 100644 index a13603557..000000000 --- a/grammars/silver/extension/doc/core/CommentItem.sv +++ /dev/null @@ -1,5 +0,0 @@ -grammar silver:extension:doc:core; - -synthesized attribute body :: String; -synthesized attribute file :: String; -nonterminal CommentItem with body, file; diff --git a/grammars/silver/extension/doc/core/DclComment.sv b/grammars/silver/extension/doc/core/DclComment.sv deleted file mode 100644 index 8bbb5db06..000000000 --- a/grammars/silver/extension/doc/core/DclComment.sv +++ /dev/null @@ -1,76 +0,0 @@ -grammar silver:extension:doc:core; - -nonterminal DclComment layout {} with body, env, location; -nonterminal DclCommentComponent with body, env, location; -nonterminal DclCommentComponents with body, env, location; - -attribute docEnv occurs on DclComment, DclCommentComponent, DclCommentComponents; - -{- -Used by the parser to parse documentation -comments out of Silver code --} - -concrete production dclComment -top::DclComment ::= '{@comment' components::DclCommentComponents '@}' -{ - top.body = components.body; -} - -concrete production consCommentComps -top::DclCommentComponents ::= h::DclCommentComponent t::DclCommentComponents -{ - top.body = h.body ++ t.body; -} - -concrete production nilCommentComps -top::DclCommentComponents ::= -{ - top.body = ""; -} - -concrete production componentLink -top::DclCommentComponent ::= '@link' '[' id::QName ']' -{ - local dclInfo::DocDclInfo = head(treeLookup(id.lookupValue.fullName, top.docEnv)); - top.body = "[" ++ dclInfo.id ++ "](" ++ dclInfo.path ++ ")"; -} - -concrete production componentText -top::DclCommentComponent ::= t::CommentText_t -{ - top.body = t.lexeme; -} - -concrete production componentWhiteSpace -top::DclCommentComponent ::= w::WhiteSpace -{ - top.body = w.lexeme; -} - -{- -Used by other productions to construct -items for the list of CommentItems that -AGDcl is given --} - -abstract production dclCommentItem -top::CommentItem ::= modifiers::String name::String signature::String file::String body::Decorated DclComment -{ - top.body = body.body; - top.file = file; -} - -abstract production bodilessDclCommentItem -top::CommentItem ::= modifiers::String name::String signature::String file::String -{ - top.body = ""; - top.file = file; -} - --- TODO: Find a way to indent doc information instead of quoting in markdown --- TODO: Add document comments for parser declarations. --- TODO: Add document comments for type declarations. - - - diff --git a/grammars/silver/extension/doc/core/DocConfig.sv b/grammars/silver/extension/doc/core/DocConfig.sv deleted file mode 100644 index 25fc6ab50..000000000 --- a/grammars/silver/extension/doc/core/DocConfig.sv +++ /dev/null @@ -1,87 +0,0 @@ -grammar silver:extension:doc:core; - -synthesized attribute header :: String; -synthesized attribute splitFiles :: String; -synthesized attribute noDoc :: Boolean; -synthesized attribute warnings :: String; -nonterminal DocConfigs with header, splitFiles, noDoc, warnings; -nonterminal DocConfig with header, splitFiles, noDoc; - -concrete production config -top::AGDcl ::= '{@config' items::DocConfigs '@}' -{ - top.docs := []; - top.docsHeader = items.header; - top.docsSplit = items.splitFiles; - top.docsNoDoc = items.noDoc; - forwards to emptyAGDcl(location=top.location); -} - -concrete production consConfigs -top::DocConfigs ::= c::DocConfig rest::DocConfigs -{ - local headerWarnings :: String = - if c.header != "" && rest.header != "" - then "Multiple header definitions in documentation configuration." - else ""; - - local splitFilesWarnings :: String = - if c.header != "" && rest.header != "" - then "Multpile split-files definitions in documentation configuration." - else ""; - - top.header = case c.header, rest.header of - | "", h -> h - | h, _ -> h - end; - - top.splitFiles = case c.splitFiles, rest.splitFiles of - | "", s -> s - | s, _ -> s - end; - - top.noDoc = c.noDoc || rest.noDoc; - top.warnings = headerWarnings ++ splitFilesWarnings ++ rest.warnings; -} - -concrete production nilConfigs -top::DocConfigs ::= -{ - top.header = ""; - top.splitFiles = ""; - top.noDoc = false; -} - -concrete production headerConfig -top::DocConfig ::= 'header' ':' value::ConfigValue_t -{ - top.header = cleanDocValue(value.lexeme); - top.splitFiles = ""; - top.noDoc = false; -} - -concrete production splitFilesConfig -top::DocConfig ::= 'split-files' ':' value::ConfigValue_t -{ - top.header = ""; - top.splitFiles = cleanDocValue(value.lexeme); - top.noDoc = false; -} - -concrete production noDocConfig -top::DocConfig ::= 'no-doc' ':' value::ConfigValue_t -{ - top.header = ""; - top.splitFiles = ""; - top.noDoc = if "true" == value.lexeme - then true - else false; -} - -function cleanDocValue -String ::= s::String -{ - return substitute("\\n", "\n", substitute("\"", "", substitute("\\t", "\t", s))); -} - - diff --git a/grammars/silver/extension/doc/core/Environment.sv b/grammars/silver/extension/doc/core/Environment.sv deleted file mode 100644 index 6d30d86cb..000000000 --- a/grammars/silver/extension/doc/core/Environment.sv +++ /dev/null @@ -1,39 +0,0 @@ -grammar silver:extension:doc:core; - -nonterminal DocDclInfo with id, file, path; - -synthesized attribute id :: String; -synthesized attribute path :: String; - -abstract production functionDocDclInfo -top::DocDclInfo ::= id::String file::String -{ - top.id = id; - top.file = file; - top.path = ""; -} - -abstract production functionDocDclInfoP -top::DocDclInfo ::= id::String file::String path::String -{ - top.id = id; - top.file = file; - top.path = path; -} - -abstract production productionDocDclInfo -top::DocDclInfo ::= id::String file::String -{ - top.id = id; - top.file = file; - top.path = ""; -} - -abstract production productionDocDclInfoP -top::DocDclInfo ::= id::String file::String path::String -{ - top.id = id; - top.file = file; - top.path = path; -} - diff --git a/grammars/silver/extension/doc/core/Expr.sv b/grammars/silver/extension/doc/core/Expr.sv deleted file mode 100644 index e05385063..000000000 --- a/grammars/silver/extension/doc/core/Expr.sv +++ /dev/null @@ -1,234 +0,0 @@ -grammar silver:extension:doc:core; - ---attribute doc occurs on Expr, Exprs; -- TODO: Implement? - -aspect production errorReference -top::Expr ::= msg::[Message] q::Decorated QName -{ -} - -aspect production childReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production lhsReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production localReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production productionReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production functionReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production forwardReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production globalValueReference -top::Expr ::= q::Decorated QName -{ -} - -aspect production errorApplication -top::Expr ::= e::Decorated Expr es::AppExprs annos::AnnoAppExprs -{ -} - -aspect production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ -} - -aspect production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ -} - -aspect production attributeSection -top::Expr ::= '(' '.' q::QName ')' -{ -} - -aspect production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - -aspect production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ -} - -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - -aspect production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - -aspect production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ -} - - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ -} - -aspect production trueConst -top::Expr ::='true' -{ -} - -aspect production falseConst -top::Expr ::= 'false' -{ -} - -aspect production and -top::Expr ::= e1::Expr '&&' e2::Expr -{ -} - -aspect production or -top::Expr ::= e1::Expr '||' e2::Expr -{ -} - -aspect production not -top::Expr ::= '!' e::Expr -{ -} - -aspect production gt -top::Expr ::= e1::Expr '>' e2::Expr -{ -} - -aspect production lt -top::Expr ::= e1::Expr '<' e2::Expr -{ -} - -aspect production gteq -top::Expr ::= e1::Expr '>=' e2::Expr -{ -} - -aspect production lteq -top::Expr ::= e1::Expr '<=' e2::Expr -{ -} - -aspect production eqeq -top::Expr ::= e1::Expr '==' e2::Expr -{ -} - -aspect production neq -top::Expr ::= e1::Expr '!=' e2::Expr -{ -} - -aspect production ifThenElse -top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr -{ -} - -aspect production intConst -top::Expr ::= i::Int_t -{ -} - -aspect production floatConst -top::Expr ::= f::Float_t -{ -} - -aspect production plus -top::Expr ::= e1::Expr '+' e2::Expr -{ -} -aspect production minus -top::Expr ::= e1::Expr '-' e2::Expr -{ -} -aspect production multiply -top::Expr ::= e1::Expr '*' e2::Expr -{ -} -aspect production divide -top::Expr ::= e1::Expr '/' e2::Expr -{ -} -aspect production modulus -top::Expr ::= e1::Expr '%' e2::Expr -{ -} -aspect production neg -top::Expr ::= '-' e::Expr -{ -} - -aspect production stringConst -top::Expr ::= s::String_t -{ -} - -aspect production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ -} - -aspect production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ -} - -aspect production exprsEmpty -top::Exprs ::= -{ -} - -aspect production exprsSingle -top::Exprs ::= e::Expr -{ -} - -aspect production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ -} - -aspect production exprRef -top::Expr ::= e::Decorated Expr -{ -} \ No newline at end of file diff --git a/grammars/silver/extension/doc/core/FunctionDcl.sv b/grammars/silver/extension/doc/core/FunctionDcl.sv deleted file mode 100644 index 7b5385607..000000000 --- a/grammars/silver/extension/doc/core/FunctionDcl.sv +++ /dev/null @@ -1,28 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.docs := [bodilessDclCommentItem("function", id.name, ns.unparse, id.location.filename)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, functionDocDclInfoP(id.name, id.location.filename, nameToPath(top.grammarName ++ ":" ++ id.name)))]; -} - -concrete production docFunctionDcl -top::AGDcl ::= comment::DclComment 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.docs := [dclCommentItem("function", id.name, ns.unparse, id.location.filename, comment)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, functionDocDclInfoP(id.name, id.location.filename, nameToPath(top.grammarName ++ ":" ++ id.name)))]; - - forwards to functionDcl('function', id, ns, body, location=top.location); -} - -concrete production noDocFunctionDcl -top::AGDcl ::= noDoc::NoDclComment_t 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.docs := []; - top.docDcls := []; - forwards to functionDcl('function', id, ns, body, location=top.location); -} - diff --git a/grammars/silver/extension/doc/core/GlobalDcl.sv b/grammars/silver/extension/doc/core/GlobalDcl.sv deleted file mode 100644 index 77421553a..000000000 --- a/grammars/silver/extension/doc/core/GlobalDcl.sv +++ /dev/null @@ -1,23 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production globalValueDclConcrete -top::AGDcl ::= 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - top.docs := [bodilessDclCommentItem("global", id.name, t.unparse, id.location.filename)]; -} - -concrete production docGlobalValueDclConcrete -top::AGDcl ::= comment::DclComment 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - top.docs := [dclCommentItem("global", id.name, t.unparse, id.location.filename, comment)]; - - forwards to globalValueDclConcrete('global', id, '::', t, '=', e, ';', location=top.location); -} - -concrete production noDocGlobalValueDclConcrete -top::AGDcl ::= noDoc::NoDclComment_t 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - top.docs := []; - - forwards to globalValueDclConcrete('global', id, '::', t, '=', e, ';', location=top.location); -} diff --git a/grammars/silver/extension/doc/core/NonTerminalDcl.sv b/grammars/silver/extension/doc/core/NonTerminalDcl.sv deleted file mode 100644 index e876c1ba8..000000000 --- a/grammars/silver/extension/doc/core/NonTerminalDcl.sv +++ /dev/null @@ -1,45 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production nonterminalDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - top.docs := [bodilessDclCommentItem("nonterminal", id.name ++ tl.unparse ++ " " ++ nm.unparse, "", id.location.filename)]; -} - -concrete production docNonterminalDcl -top::AGDcl ::= comment::DclComment cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - top.docs := [dclCommentItem("nonterminal", id.name ++ tl.unparse ++ " " ++ nm.unparse, "", id.location.filename, comment)]; - - forwards to nonterminalDcl(cl, 'nonterminal', id, tl, nm, ';', location=top.location); -} - -concrete production noDocNonterminalDcl -top::AGDcl ::= noDoc::NoDclComment_t cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - top.docs := []; - - forwards to nonterminalDcl(cl, 'nonterminal', id, tl, nm, ';', location=top.location); -} - -aspect production nonterminalWithDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers 'with' attrs::QNames ';' -{ - top.docs := [bodilessDclCommentItem("nonterminal", id.name ++ tl.unparse ++ " " ++ nm.unparse, "", id.location.filename)]; -} - -concrete production docNonterminalWithDcl -top::AGDcl ::= comment::DclComment cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers 'with' attrs::QNames ';' -{ - top.docs := [dclCommentItem("nonterminal", id.name ++ tl.unparse, "", id.location.filename, comment)]; - - forwards to nonterminalWithDcl(cl, 'nonterminal', id, tl, nm, 'with', attrs, ';', location=top.location); -} - -concrete production noDocNonterminalWithDcl -top::AGDcl ::= noDoc::NoDclComment_t cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers 'with' attrs::QNames ';' -{ - top.docs := []; - - forwards to nonterminalWithDcl(cl, 'nonterminal', id, tl, nm, 'with', attrs, ';', location=top.location); -} diff --git a/grammars/silver/extension/doc/core/OccursDcl.sv b/grammars/silver/extension/doc/core/OccursDcl.sv deleted file mode 100644 index b45755551..000000000 --- a/grammars/silver/extension/doc/core/OccursDcl.sv +++ /dev/null @@ -1,23 +0,0 @@ -grammar silver:extension:doc:core; - -aspect production attributionDcl -top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - top.docs := [bodilessDclCommentItem("attribute", at.name, "", at.location.filename)]; -} - -concrete production docAttributionDcl -top::AGDcl ::= comment::DclComment 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - top.docs := [dclCommentItem("attribute", at.name, "", at.location.filename, comment)]; - - forwards to attributionDcl('attribute', at, attl, 'occurs', 'on', nt, nttl, ';', location=top.location); -} - -concrete production noDocAttributionDcl -top::AGDcl ::= noDoc::NoDclComment_t 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - top.docs := []; - - forwards to attributionDcl('attribute', at, attl, 'occurs', 'on', nt, nttl, ';', location=top.location); -} diff --git a/grammars/silver/extension/doc/core/Paths.sv b/grammars/silver/extension/doc/core/Paths.sv deleted file mode 100644 index 1938091ee..000000000 --- a/grammars/silver/extension/doc/core/Paths.sv +++ /dev/null @@ -1,20 +0,0 @@ -grammar silver:extension:doc:core; - -function nameToPath -String ::= name::String -{ - return nameToPathHelp(explode(":", name)); -} - -function nameToPathHelp -String ::= s::[String] -{ - return case s of - | [] -> "" - | x :: [] -> "#" ++ x - | x1 :: x2 :: [] -> x1 ++ "#" ++ x2 - | x :: xs -> x ++ "/" ++ nameToPathHelp(xs) - end; -} - - diff --git a/grammars/silver/extension/doc/core/ProductionDcl.sv b/grammars/silver/extension/doc/core/ProductionDcl.sv deleted file mode 100644 index 15f4a697e..000000000 --- a/grammars/silver/extension/doc/core/ProductionDcl.sv +++ /dev/null @@ -1,58 +0,0 @@ -grammar silver:extension:doc:core; - -import silver:definition:concrete_syntax; - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - top.docs := [bodilessDclCommentItem("abstract production", id.name, ns.unparse, id.location.filename)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, productionDocDclInfoP(id.name, id.location.filename, top.grammarName ++ ":" ++ id.name))]; -} - -concrete production docProductionDecl -top::AGDcl ::= comment::DclComment 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - top.docs := [dclCommentItem("abstract production", id.name, ns.unparse, id.location.filename, comment)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, productionDocDclInfoP(id.name, id.location.filename, top.grammarName ++ ":" ++ id.name))]; - - forwards to productionDcl('abstract', 'production', id, ns, body, location=top.location); -} - -concrete production noDocProductionDecl -top::AGDcl ::= noDoc::NoDclComment_t 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - top.docs := []; - top.docDcls := []; - - forwards to productionDcl('abstract', 'production', id, ns, body, location=top.location); -} - -aspect production concreteProductionDcl -top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody -{ - top.docs := [bodilessDclCommentItem("concrete production", id.name, ns.unparse, id.location.filename)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, productionDocDclInfoP(id.name, id.location.filename, top.grammarName ++ ":" ++ id.name))]; -} - -concrete production docConcreteProductionDcl -top::AGDcl ::= comment::DclComment 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody -{ - top.docs := [dclCommentItem("concrete production", id.name, ns.unparse, id.location.filename, comment)]; - - top.docDcls := [pair(top.grammarName ++ ":" ++ id.name, productionDocDclInfoP(id.name, id.location.filename, top.grammarName ++ ":" ++ id.name))]; - - forwards to concreteProductionDcl('concrete', 'production', id, ns, pm, body, location=top.location); -} - -concrete production noDocConcreteProductionDcl -top::AGDcl ::= noDoc::NoDclComment_t 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody -{ - top.docs := []; - top.docDcls := []; - - forwards to concreteProductionDcl('concrete', 'production', id, ns, pm, body, location=top.location); -} - diff --git a/grammars/silver/extension/doc/core/Project.sv b/grammars/silver/extension/doc/core/Project.sv deleted file mode 100644 index a6a62aa57..000000000 --- a/grammars/silver/extension/doc/core/Project.sv +++ /dev/null @@ -1,16 +0,0 @@ -grammar silver:extension:doc:core; - -imports silver:definition:core; -imports silver:definition:type:syntax; - -imports silver:definition:env; -imports silver:definition:type; - -imports silver:extension:convenience; - -imports silver:util:treemap; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Core\nmenu_title: Core\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/doc/core/Root.sv b/grammars/silver/extension/doc/core/Root.sv deleted file mode 100644 index b060b40ac..000000000 --- a/grammars/silver/extension/doc/core/Root.sv +++ /dev/null @@ -1,116 +0,0 @@ -grammar silver:extension:doc:core; - -import silver:driver; - -synthesized attribute genFiles :: [Pair] with ++; - --- Used for getting doc comments on AGDcls -synthesized attribute docs :: [CommentItem] with ++; -attribute docs occurs on Grammar, Root, AGDcls, AGDcl; - --- A string that goes at the top of each file -synthesized attribute docsHeader :: String; -attribute docsHeader occurs on Grammar, Root, AGDcls, AGDcl; - --- Set to "true" if each file in the grammar should have its own markdown file -synthesized attribute docsSplit :: String; -attribute docsSplit occurs on Grammar, Root, AGDcls, AGDcl; - --- Set to "true" if no documentation should be generated for this grammar -synthesized attribute docsNoDoc :: Boolean; -attribute docsNoDoc occurs on Grammar, Root, AGDcls, AGDcl; - --- Declarations of documented AGDcls -synthesized attribute docDcls :: [Pair] with ++; -attribute docDcls occurs on Grammar, Root, AGDcls, AGDcl; - --- Environment of all documented AGDcls -autocopy attribute docEnv :: TreeMap; -attribute docEnv occurs on Grammar, Root, AGDcls, AGDcl; - -aspect production root -top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls -{ - top.docs := ags.docs; - top.docsHeader = ags.docsHeader; - top.docsSplit = ags.docsSplit; - top.docsNoDoc = false; - top.docDcls := ags.docDcls; -} - -aspect production nilAGDcls -top::AGDcls ::= -{ - top.docs := []; - top.docsHeader = ""; - top.docsSplit = ""; - top.docsNoDoc = false; - top.docDcls := []; -} - -aspect production consAGDcls -top::AGDcls ::= h::AGDcl t::AGDcls -{ - top.docs := h.docs ++ t.docs; - top.docsHeader = if "" == h.docsHeader - then t.docsHeader - else h.docsHeader; - top.docsSplit = if "" == h.docsSplit - then t.docsSplit - else h.docsSplit; - top.docsNoDoc = h.docsNoDoc || t.docsNoDoc; - top.docDcls := h.docDcls ++ t.docDcls; -} - -aspect default production -top::AGDcl ::= -{ - top.docs := []; - top.docsHeader = ""; - top.docsSplit = ""; - top.docsNoDoc = false; - top.docDcls := []; -} - -aspect production appendAGDcl -top::AGDcl ::= h::AGDcl t::AGDcl -{ - top.docs := h.docs ++ t.docs; - top.docsHeader = if "" == h.docsHeader - then t.docsHeader - else h.docsHeader; - - top.docsSplit = if "" == h.docsSplit - then t.docsSplit - else h.docsSplit; - - top.docsNoDoc = h.docsNoDoc || t.docsNoDoc; - top.docDcls := h.docDcls ++ t.docDcls; -} - -aspect production nilGrammar -top::Grammar ::= -{ - top.docs := []; - top.docsHeader = ""; - top.docsSplit = ""; - top.docsNoDoc = false; - top.docDcls := []; -} - -aspect production consGrammar -top::Grammar ::= c1::Root c2::Grammar -{ - top.docs := c1.docs ++ c2.docs; - top.docsHeader = if "" == c1.docsHeader - then c2.docsHeader - else c1.docsHeader; - - top.docsSplit = if "" == c1.docsSplit - then c2.docsSplit - else c1.docsSplit; - - top.docsNoDoc = c1.docsNoDoc || c2.docsNoDoc; - top.docDcls := c1.docDcls ++ c2.docDcls; -} - diff --git a/grammars/silver/extension/doc/core/RootSpec.sv b/grammars/silver/extension/doc/core/RootSpec.sv deleted file mode 100644 index 338fef504..000000000 --- a/grammars/silver/extension/doc/core/RootSpec.sv +++ /dev/null @@ -1,127 +0,0 @@ -grammar silver:extension:doc:core; - -import silver:driver:util; - -attribute genFiles occurs on RootSpec; - -aspect production interfaceRootSpec -top::RootSpec ::= _ _ _ -{ - top.genFiles := []; -} - -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - top.genFiles := []; -} - -aspect production grammarRootSpec -top::RootSpec ::= g::Grammar _ _ _ _ -{ - top.genFiles := if g.docsNoDoc - then [] - else - if "true" == g.docsSplit - then toSplitFiles(g.docs, [], g.docsHeader) - else [toSingleFile(g.docs, g.docsHeader)]; - - g.docEnv = treeConvert(g.docDcls, treeNew(compareString)); -} - -function toSplitFiles -[Pair] ::= comments::[CommentItem] sortedComments::[Pair] header::String -{ - return - case comments of - | c :: rest -> toSplitFiles(rest, placeComment(c, sortedComments, header), header) - | [] -> pair("index.md", makeIndexFile(sortedComments, header)) :: sortedComments - end; -} - -function placeComment -[Pair] ::= comment::CommentItem sortedComments::[Pair] header::String -{ - local markdown::String = toMarkdown(comment); - return - case sortedComments of - | pair(filename, contents) :: rest -> - if filename == toMarkdownExtension(comment.file) - then pair(filename, contents ++ markdown) :: rest - else pair(filename, contents) :: placeComment(comment, rest, header) - | [] -> [pair(toMarkdownExtension(comment.file), header ++ markdown)] - end; -} - -function makeIndexFile -String ::= sortedComments::[Pair] header::String -{ - return - case sortedComments of - | pair(f, _) :: rest -> makeIndexFile(rest, header) ++ "\n" ++ makeLink(f) ++ "\n" - | [] -> header - end; -} - -function makeLink -String ::= mdFileName::String -{ - local txt::String = substitute( ".md", "", mdFileName ); - local lnk::String = substitute( ".md", ".html", mdFileName ); - return "[" ++ txt ++ "](" ++ lnk ++ ")" ; -} - - -function toSingleFile -Pair ::= comments::[CommentItem] header::String -{ - local commentMarkdown::String = toSingleMarkdown(comments); - return pair("index.md", header ++ commentMarkdown); -} - -function toSingleMarkdown -String ::= comments::[CommentItem] -{ - return case comments of - | c :: rest -> toMarkdown(c) ++ toSingleMarkdown(rest) - | [] -> "" - end; -} - -function toMarkdown -String ::= c::CommentItem -{ - return - case c of - | dclCommentItem(mod, name, sig, file, body)-> - let signature :: String = - if 0 == length(sig) - then "" - else "\n###### `" ++ sig ++ "`" - in - "\n\n#### _" ++ mod ++ - "_ `" ++ name ++ - "`" ++ signature ++ - "\n> " ++ body.body ++ - "\nIn file: " ++ file - end - | bodilessDclCommentItem(mod, name, sig, file) -> - let signature :: String = - if 0 == length(sig) - then "" - else "\n###### `" ++ sig ++ "`" - in - "\n\n#### _" ++ mod ++ - "_ `" ++ name ++ - "`" ++ signature ++ - "\nIn file: " ++ file - end - end; -} - -function toMarkdownExtension -String ::= filename::String -{ - return substitute(".sv", ".md", filename); -} - diff --git a/grammars/silver/extension/doc/core/Terminals.sv b/grammars/silver/extension/doc/core/Terminals.sv deleted file mode 100644 index e1a1b2f1d..000000000 --- a/grammars/silver/extension/doc/core/Terminals.sv +++ /dev/null @@ -1,36 +0,0 @@ -grammar silver:extension:doc:core; - -temp_imp_ide_font font_doc color(82, 100, 166) italic; -- TODO? -temp_imp_ide_font font_doc_kwd color(82, 100, 166) italic bold; -- TODO? -temp_imp_ide_font font_doc_id color(82, 100, 166); -- TODO? - -lexer class DOC extends Silver, font = font_doc; -lexer class DOC_KWD extends Silver, font = font_doc_kwd; -lexer class DOC_ID extends Silver, font = font_doc_id; - --- For comments on AGDcls -terminal CommentOpen_t '{@comment' lexer classes {DOC_KWD}; -terminal CommentLink_t '@link' lexer classes {DOC_KWD}; -terminal CommentId_t /[a-zA-Z0-9]+[a-zA-Z0-9:]*/ lexer classes {DOC_ID}; -terminal CommentOpenSquare_t '[' lexer classes {DOC}; -terminal CommentCloseSquare_t ']' lexer classes {DOC}; - --- For grammar level documentation configuration -terminal ConfigOpen_t '{@config' lexer classes {DOC_KWD}; -terminal ConfigHeader_t 'header' lexer classes {DOC_KWD}; -terminal ConfigSplitFiles_t 'split-files' lexer classes {DOC_KWD}; -terminal ConfigNoDoc_t 'no-doc' lexer classes {DOC_KWD}; -terminal ConfigValue_t /"[^"]*"/ lexer classes {DOC}; - --- For tutorials and large bodies of text ---terminal TutOpen_t '{@tutor' lexer classes {DOC}; - --- For comments on AGDcls -terminal NoDclComment_t /{@no\-doc(\ )*@}/ dominates {DOC_KWD}; - --- Closes a documentation block -terminal Close_t '@}' lexer classes {DOC_KWD}; - --- Captures text inside comments -terminal CommentText_t /[a-zA-Z0-9][^\@]*/ lexer classes {DOC}; - diff --git a/grammars/silver/extension/doc/driver/BuildProcess.sv b/grammars/silver/extension/doc/driver/BuildProcess.sv deleted file mode 100644 index 2c947ca6a..000000000 --- a/grammars/silver/extension/doc/driver/BuildProcess.sv +++ /dev/null @@ -1,118 +0,0 @@ -grammar silver:extension:doc:driver; - -import silver:extension:doc:core; - -import silver:driver; -import silver:definition:env; -import silver:definition:core; - -import silver:util; -import silver:util:cmdargs; - -synthesized attribute docGeneration :: Boolean occurs on CmdArgs; -synthesized attribute docOutOption :: [String] occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= _ -{ - top.docGeneration = false; - top.docOutOption = []; -} - -abstract production docFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.docGeneration = true; - forwards to rest; -} - -abstract production docOutFlag -top::CmdArgs ::= loc::String rest::CmdArgs -{ - top.docOutOption = loc :: rest.docOutOption; - forwards to rest; -} - -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--doc", flag(docFlag)), - pair("--doc-out", option(docOutFlag))]; - flagdescs <- ["\t--doc : build the documentation", - "\t--doc-out : output location for documentation"]; -} - -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - local outputLoc::String = - if null(top.config.docOutOption) - then benv.silverGen - else head(top.config.docOutOption) ++ "/"; -- TODO: check only one item for docOutOption is provided - top.postOps <- if top.config.docGeneration then - [genDoc(top.config, grammarsToTranslate, outputLoc)] - else []; -} - -abstract production genDoc -top::DriverAction ::= a::Decorated CmdArgs specs::[Decorated RootSpec] outputLoc::String -{ - local pr :: IO = print("Generating Documentation.\n", top.ioIn); - - top.io = writeAll(pr, a, specs, outputLoc); - top.code = 0; - top.order = 4; -} - -function writeAll -IO ::= i::IO a::Decorated CmdArgs l::[Decorated RootSpec] outputLoc::String -{ - local now :: IO = writeSpec(i, head(l), outputLoc); - local recurse :: IO = writeAll(now, a, tail(l), outputLoc); - - return if null(l) then i else recurse; -} - -function writeSpec -IO ::= i::IO r::Decorated RootSpec outputLoc::String -{ - local path :: String = outputLoc ++ "doc/" ++ grammarToPath(r.declaredName); - - local mkiotest :: IOVal = - isDirectory(path, i); - - local mkio :: IOVal = - if mkiotest.iovalue - then mkiotest - else mkdir(path, mkiotest.io); - - local pr :: IO = - if mkio.iovalue - then print("\t[" ++ r.declaredName ++ "]\n", mkio.io) - else exit(-5, print("\nUnrecoverable Error: Unable to create directory: " ++ path ++ "\n\n", mkio.io)); - - local rm :: IO = deleteStaleDocs(pr, outputLoc, r.declaredName); - - local wr :: IO = writeFiles(path, r.genFiles, rm); - - return wr; -} - -{-- - - Given a path (with terminating /) and list of (file names relative to that root, contents), - - write these out. - -} -function writeFiles -IO ::= path::String s::[Pair] i::IO -{ - return if null(s) then i else writeFile(path ++ head(s).fst, head(s).snd, writeFiles(path, tail(s), i)); -} - --- Copied from -function deleteStaleDocs -IO ::= iIn::IO outputLoc::String gram::String -{ - local docPath :: String = outputLoc ++ "doc/" ++ grammarToPath(gram); - - return deleteDirFiles(docPath, iIn).io; -} diff --git a/grammars/silver/extension/doc/extra/Main.sv b/grammars/silver/extension/doc/extra/Main.sv deleted file mode 100644 index 4916f1954..000000000 --- a/grammars/silver/extension/doc/extra/Main.sv +++ /dev/null @@ -1,83 +0,0 @@ -grammar silver:extension:doc:extra; - -{- This grammar is just a bunch of imports. It is run by the generate-documentation script, and just builds documentation. The jar it produces is just thrown away. -} - -import core; - ---import ide; - -import lib:errors; -import lib:extcore; -import lib:system; -import lib:xml; - -import silver:analysis; -import silver:analysis:typechecking:core; -import silver:analysis:warnings; -import silver:analysis:warnings:flow; -import silver:analysis:warnings:exporting; - -import silver:composed; -import silver:composed:Default; ---import silver:composed:extendedorigins; ---import silver:composed:idetest; - -import silver:definition; -import silver:definition:concrete_syntax; -import silver:definition:concrete_syntax:ast; -import silver:definition:core; -import silver:definition:env; -import silver:definition:flow:ast; -import silver:definition:flow:driver; -import silver:definition:flow:env; -import silver:definition:regex; -import silver:definition:type; -import silver:definition:type:syntax; - -import silver:driver; -import silver:driver:util; - -import silver:extension; --- Individual extensions built by silver:composed:Default - -import silver:host; - -import silver:langutil; -import silver:langutil:pp; - -import silver:modification; --- Individual modifications built by silver:composed:Default - -import silver:support:monto; - -import silver:testing; ---import silver:testing:bin; --Do we want this? - -import silver:translation:java; -import silver:translation:java:core; -import silver:translation:java:driver; -import silver:translation:java:type; - -import silver:util; -import silver:util:cmdargs; -import silver:util:command; -import silver:util:deque; -import silver:util:fixedmap; -import silver:util:treemap; -import silver:util:raw:graph; -import silver:util:raw:treemap; -import silver:util:raw:treeset; - -{@comment Dummy main function that does nothing @link[dummyFunction] @} -function main -IOVal ::= args::[String] ioIn::IO -{ - return ioval(ioIn, 0); -} - -function dummyFunction -Integer ::= -{ - return 1; -} - diff --git a/grammars/silver/extension/easyterminal/DocConfig.sv b/grammars/silver/extension/easyterminal/DocConfig.sv deleted file mode 100644 index 41dd70fe1..000000000 --- a/grammars/silver/extension/easyterminal/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension:easyterminal; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Easy Terminal\nmenu_title: Easy Terminal\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/easyterminal/Env.sv b/grammars/silver/extension/easyterminal/Env.sv deleted file mode 100644 index c54e4322b..000000000 --- a/grammars/silver/extension/easyterminal/Env.sv +++ /dev/null @@ -1,62 +0,0 @@ -grammar silver:extension:easyterminal; - -import silver:definition:env; -import silver:definition:regex; - --- TODO BUG FIXME: This looks up terminals via their regular expressions, but --- that confuses single quoted and non-single quoted regexs! --- i.e. 'hi' will happily match /hi/ and '(' will match /[\(]/, but this should --- probably not be the case! - --- The correct solution would be to only look up via their "single quotes name" --- which for '' terminals is the bit in '', while for // terminals, it can be specified if desired - -function getTerminalRegexDclAll -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnv(search, e.terminalTree); -} - - - -synthesized attribute terminalTree :: [EnvScope] occurs on Env; -- must be kept in sync with typeTree's type!! (whether its a [] or not) - -function filterAndConvertTermDcls -[Pair] ::= ei::EnvItem sofar::[Pair] -{ - return case ei.dcl of - | termDcl(_, _, fn, regex) -> pair(regex.regString, ei.dcl) :: sofar - | _ -> sofar - end; -} - -function buildTerminalTree -EnvTree ::= eis::[EnvItem] -{ - return directBuildTree(foldr(filterAndConvertTermDcls,[],eis)); -} - -aspect production i_emptyEnv -top::Env ::= -{ - top.terminalTree = [emptyEnvScope()]; -} - -aspect production i_appendEnv -top::Env ::= e1::Decorated Env e2::Decorated Env -{ - top.terminalTree = e1.terminalTree ++ e2.terminalTree; -} - -aspect production i_newScopeEnv -top::Env ::= d::Defs e::Decorated Env -{ - top.terminalTree = oneEnvScope(buildTerminalTree(d.typeList)) :: e.terminalTree; -} - -aspect production i_occursEnv -top::Env ::= _ e::Decorated Env -{ - top.terminalTree = e.terminalTree; -} - diff --git a/grammars/silver/extension/easyterminal/TerminalDcl.sv b/grammars/silver/extension/easyterminal/TerminalDcl.sv deleted file mode 100644 index e1bd06cd2..000000000 --- a/grammars/silver/extension/easyterminal/TerminalDcl.sv +++ /dev/null @@ -1,111 +0,0 @@ -grammar silver:extension:easyterminal; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:definition:type:syntax; -import silver:definition:concrete_syntax; -import silver:definition:regex only Regex, regString, regexLiteral; - -terminal Terminal_t /\'[^\'\r\n]*\'/ lexer classes {LITERAL}; - --- TODO: refactor this to actually create two separate terminal declarations, one regular regex, one single quote. - -- TODO: alternatively, we keep this as a 'RegExpr', but we introduce "added terminal modifiers" synthesized - -- attribute on RegExpr. This would preserve the marking token aspect of the extension here. --- TODO: Add a 'quoted name' terminal modifier to regular terminal declarations. - -- e.g. "terminal Attr /_?_?attribute_?_?/ quoted='__attribute__';" --- TODO: Make this declaration forward to regular terminal declaration, adding the "quoted" modifier. - --- TODO: We could probably remove single quoted terminal references from all the RHSs, and add them to the Type syntax - -- Standing in the way of doing this is the busted aspect syntax. We'd have to fix that first. See the forward with the TODO. - - -{-- Introduce single quoted terminal declarations -} -concrete production regExprEasyTerm -top::RegExpr ::= t::Terminal_t -{ - top.unparse = t.lexeme; - - top.terminalRegExprSpec = regexLiteral(substring(1, length(t.lexeme) - 1, t.lexeme)); - - forwards to regExpr('/', top.terminalRegExprSpec, '/', location=top.location); -} - -{-- Abstracts away looking up terminals in the environment -} -nonterminal EasyTerminalRef with config, location, grammarName, unparse, errors, typerep, easyString, env, dcls; - -{-- String literal between quotes. e.g. 'hi"' is hi" -} -synthesized attribute easyString :: String; - -concrete production easyTerminalRef -top::EasyTerminalRef ::= t::Terminal_t -{ - top.unparse = t.lexeme; - top.easyString = substring(1, length(t.lexeme) - 1, t.lexeme); - - -- TODO: This is necessary because the environment is still populated using the regex, so we have to look up the corresponding regex. - local regHack :: Regex = regexLiteral(top.easyString); - - top.dcls = getTerminalRegexDclAll(regHack.regString, top.env); - - top.errors := - if null(top.dcls) then - [err(t.location, "Could not find terminal declaration for " ++ t.lexeme )] - else if length(top.dcls) > 1 then - [err(t.location, "Found ambiguous possibilities for " ++ t.lexeme ++ "\n" ++ printPossibilities(top.dcls))] - else []; - - top.typerep = if null(top.dcls) then errorType() else head(top.dcls).typerep; -} - - -concrete production productionRhsElemEasyReg -top::ProductionRHSElem ::= id::Name '::' reg::EasyTerminalRef -{ - top.unparse = id.unparse ++ "::" ++ reg.unparse; - top.errors <- reg.errors; - - forwards to productionRHSElem(id, $2, typerepTypeExpr(reg.typerep, location=reg.location), location=top.location); -} - -concrete production productionRhsElemTypeEasyReg -top::ProductionRHSElem ::= reg::EasyTerminalRef -{ - top.unparse = reg.unparse; - top.errors <- reg.errors; - - forwards to productionRHSElemType(typerepTypeExpr(reg.typerep, location=top.location), location=top.location); -} - -concrete production aspectRHSElemEasyReg -top::AspectRHSElem ::= reg::EasyTerminalRef -{ - top.unparse = reg.unparse; - top.errors <- reg.errors; - - forwards to aspectRHSElemNone('_', location=reg.location); -- TODO This isn't checking if the type is right!! -} - -concrete production aspectRHSElemTypedEasyReg -top::AspectRHSElem ::= id::Name '::' reg::EasyTerminalRef -{ - top.unparse = id.unparse ++ " :: " ++ reg.unparse; - top.errors <- reg.errors; - - forwards to aspectRHSElemTyped(id, $2, typerepTypeExpr(reg.typerep, location=reg.location), location=top.location); -} - -{-- Introduce single quoted terminal literals in expressions -} -concrete production terminalExprReg -top::Expr ::= reg::EasyTerminalRef -{ - top.unparse = reg.unparse; - top.errors <- reg.errors; - - local escapedName :: String = escapeString(reg.easyString); - - forwards to terminalFunction('terminal', '(', - typerepTypeExpr(reg.typerep, location=reg.location), - ',', stringConst(terminal(String_t, "\"" ++ escapedName ++ "\""), location=reg.location), ')', location=top.location); -} - diff --git a/grammars/silver/extension/extendedorigins/AGDcls.sv b/grammars/silver/extension/extendedorigins/AGDcls.sv deleted file mode 100644 index 0054368a5..000000000 --- a/grammars/silver/extension/extendedorigins/AGDcls.sv +++ /dev/null @@ -1,625 +0,0 @@ - -imports silver:definition:core; -imports silver:extension:convenience; -imports silver:definition:type:syntax; - -terminal Origins_kwd 'origins' lexer classes {KEYWORD,RESERVED}; -terminal At_kwd '@' association = left, lexer classes {KEYWORD,RESERVED}; -terminal AtDot_kwd '@.' association = left, lexer classes {KEYWORD,RESERVED}; -terminal AtLParen_kwd '@(' association = left, lexer classes {KEYWORD,RESERVED}; - -concrete production originAGDcls -top::AGDcl ::= t::'origins' 'on' qlist::QNames ';' -{ - - forwards to - appendAGDcl( - appendAGDcl( - nonterminalDcl( -- nonterminal NTWrapper; - openNt(location=top.location), - 'nonterminal', - nameIdUpper( - terminal(IdUpper_t,"NTWrapper"), - location=top.location), - botlNone(location=top.location), - ';', - location=top.location), - - appendAGDcl( - annotationDcl( -- annotation origin::NTWrapper; - 'annotation', - qName( - top.location,"origin"), - botlNone(location=top.location), - '::', - nominalTypeExpr( - qNameTypeId(terminal(IdUpper_t,"NTWrapper"), - location=top.location), - botlNone(location=top.location), - location=top.location), - ';', - location=top.location), - annotationDcl( -- annotation new::NTWrapper; - 'annotation', - qName( - top.location,"isNew"), - botlNone(location=top.location), - '::', - booleanTypeExpr( - 'Boolean', - location=top.location), - ';', - location=top.location), - location=top.location), - location=top.location), - - appendAGDcl( - constructAnnotationDcls( - qlist, top.location), - - appendAGDcl( - attributeDclSyn( -- synthesized attribute origin_next:: NTWrapper; - 'synthesized', - 'attribute', - nameIdLower( - terminal(IdLower_t,"origin_next"), - location=top.location), - botlNone( - location=top.location), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,"NTWrapper"), - location=top.location), - botlNone( - location=top.location), - location=top.location), - ';', - location=top.location), - - appendAGDcl( - attributionDcl( -- attribute origin_next occurs on NTWrapper; - 'attribute', - qNameId( - nameIdLower( - terminal(IdLower_t,"origin_next"), - location=top.location), - location=top.location), - botlNone( - location=top.location), - 'occurs', 'on', - qNameId( - nameIdUpper( - terminal(IdUpper_t,"NTWrapper"), - location=top.location), - location=top.location), - botlNone( - location=top.location), - ';', - location=top.location), - - appendAGDcl( - constructProductions(qlist.qnames, top.location), -- abstract production ntw... - - productionDcl( -- abstract production ntwBottom - 'abstract', - 'production', - nameIdLower( - terminal(IdLower_t, "ntwBottom"), - location=top.location), - productionSignature( -- top::NTWrapper ::= - productionLHS( -- top::NTWrapper - nameIdLower( - terminal(IdLower_t,"top"), - location=top.location), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,"NTWrapper"), - location=top.location), - botlNone(location=top.location), - location=top.location), - location=top.location), - '::=', - productionRHSNil(location=top.location), - location=top.location), - productionBody( -- {} - '{', productionStmtsNil(location=top.location), '}', - location=top.location), - location=top.location), - location=top.location), - location=top.location), - location=top.location), - location=top.location), location=top.location); -} - -function constructAnnotationDcls -AGDcl ::= qlist::QNames l::Location -{ - return - case qlist of - qNamesSingle(qNameWithTL(q,_)) -> - appendAGDcl( - annotateDcl( - 'annotation', - qName( - l,"origin"), - botlNone(location=l), - 'occurs', 'on', - q, - botlNone(location=l), - ';', - location=l), - annotateDcl( - 'annotation', - qName( - l,"isNew"), - botlNone(location=l), - 'occurs', 'on', - q, - botlNone(location=l), - ';', - location=l), - location=l) - - | qNamesCons(qNameWithTL(q,_),_,qnames) -> - appendAGDcl( - appendAGDcl( - annotateDcl( - 'annotation', - qName( - l,"origin"), - botlNone(location=l), - 'occurs', 'on', - q, - botlNone(location=l), - ';', - location=l), - annotateDcl( - 'annotation', - qName( - l,"isNew"), - botlNone(location=l), - 'occurs', 'on', - q, - botlNone(location=l), - ';', - location=l), - location=l), - constructAnnotationDcls(qnames, l), - location=l) - end; -} - -function constructQNames -QNames2 ::= input::[QNameWithTL] -{ - return - if null(input) then error("Need nonterminals for origin!") - else if null(tail(input)) then error("Need nonterminals for origin!") - else if null(tail(tail(input))) then qNames2Two(head(input), ',', head(tail(input))) - else qNames2Cons(head(input), ',', constructQNames(tail(input))); -} - -function constructProductions -AGDcl ::= input::[QNameWithTL] t::Location -{ - return - if null(input) then error("Need nonterminals for origin!") - else if null(tail(input)) then error("Need nonterminals for origin!") - else if null(tail(tail(input))) then - appendAGDcl( - productionDcl( -- abstract production ntw... top::NTWrapper ::= nt::... {} - 'abstract', - 'production', - nameIdLower( - terminal(IdLower_t, "ntw" ++ head(input).qnwtQN.name), - location=t), - productionSignature( -- top::NTWrapper ::= nt::... - productionLHS( -- top::NTWrapper - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,"NTWrapper"), - location=t), - botlNone(location=t), - location=t), - location=t), - '::=', - productionRHSCons( -- nt::... - productionRHSElem( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,head(input).qnwtQN.name), --TODO - location=t), - botlNone(location=t), - location=t), - location=t), - productionRHSNil(location=t), - location=t), - location=t), - productionBody( -- { top.origin_next = nt.origin; } - '{', - productionStmtsSnoc( - productionStmtsNil(location=t), - attributeDef( -- top.origin_next = nt.origin; - concreteDefLHS( - qNameId( - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin_next"), - location=t), - location=t), - location=t), - '=', - access( - baseExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin"), - location=t), - location=t), - location=t), - location=t), - ';', - location=t), - location=t), - '}', - location=t), - location=t), - -{- -abstract production ntw___ -top::NTWrapper ::= nt::___ -{ - top.origin_next = nt.origin; -} --} - productionDcl( - 'abstract', - 'production', - nameIdLower( - terminal(IdLower_t,"ntw" ++ head(tail(input)).qnwtQN.name), --TODO - location=t), - productionSignature( -- top::NTWrapper ::= nt::... - productionLHS( -- top::NTWrapper - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,"NTWrapper"), - location=t), - botlNone(location=t), - location=t), - location=t), - '::=', - productionRHSCons( -- nt::... - productionRHSElem( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,head(tail(input)).qnwtQN.name), - location=t), - botlNone(location=t), - location=t), - location=t), - productionRHSNil(location=t), - location=t), - location=t), - productionBody( - '{', - productionStmtsSnoc( - productionStmtsNil(location=t), - attributeDef( -- top.origin_next = nt.origin; - concreteDefLHS( - qNameId( - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin_next"), - location=t), - location=t), - location=t), - '=', - access( - baseExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin"), - location=t), - location=t), - location=t), - location=t), - ';', - location=t), - location=t), - '}', - location=t), - location=t), - location=t) - else appendAGDcl( -{- -abstract production ntw___ -top::NTWrapper ::= nt::___ -{ - top.origin_next = ntw.origin; -} --} - productionDcl( -- abstract production ntw___ - 'abstract', - 'production', - nameIdLower( - terminal(IdLower_t,"ntw" ++ head(input).qnwtQN.name), - location=t), - productionSignature( -- top::NTWrapper ::= nt::... - productionLHS( -- top::NTWrapper - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,"NTWrapper"), - location=t), - botlNone(location=t), - location=t), - location=t), - '::=', - productionRHSCons( -- nt::... - productionRHSElem( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - '::', - nominalTypeExpr( - qNameTypeId( - terminal(IdUpper_t,head(tail(input)).qnwtQN.name), - location=t), - botlNone(location=t), - location=t), - location=t), - productionRHSNil(location=t), - location=t), - location=t), - productionBody( - '{', - productionStmtsSnoc( - productionStmtsNil(location=t), - attributeDef( -- top.origin_next = ntw.origin; - concreteDefLHS( - qNameId( - nameIdLower( - terminal(IdLower_t,"top"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin_next"), - location=t), - location=t), - location=t), - '=', - access( - baseExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"nt"), - location=t), - location=t), - location=t), - '.', - qNameAttrOccur( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin"), - location=t), - location=t), - location=t), - location=t), - ';', - location=t), - location=t), - '}', - location=t), - location=t), - constructProductions(tail(input),t), - location=t); -} - - -{- - - The following productions require that the origin is explicitly handed to the - - annotation by the user. This will be replaced by an inherited attribute which - - passes down the tree which the attribute is evaluated on. - -} - -autocopy attribute ori::Expr occurs on AGDcls,Expr,ProductionBody,ProductionStmts,ProductionStmt,Exprs,ForwardInhs,ForwardInh,ExprInh,AppExprs,AnnoExpr,AnnoAppExprs,AppExpr; - -synthesized attribute lhs::Expr occurs on ProductionSignature, ProductionLHS; - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - body.ori = ns.lhs; -} - -aspect production productionSignature -top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS -{ - top.lhs = lhs.lhs; -} - -aspect production productionLHS -top::ProductionLHS ::= id::Name '::' t::TypeExpr -{ - top.lhs = applicationExpr( - baseExpr( - qNameId( - case t of - | nominalTypeExpr(qNameTypeId(id),_) -> - nameIdLower( - terminal(IdLower_t,"ntw"++id.lexeme), - location=top.location) - | _ -> error("Origin does not support this type yet") - end, - location=top.location), - location=top.location), - '(', - oneAppExprs( - presentAppExpr( - baseExpr( - qNameId( - id, - location=top.location), - location=top.location), - location=top.location), - location=top.location), - ')',location=top.location); -} - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - body.ori = - applicationExpr( - baseExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"ntwBottom"), - location=top.location), - location=top.location), - location=top.location), - '(', - emptyAppExprs( - location=top.location), - ')', - location=top.location); -} - -concrete production oChild -top::Expr ::= '@' q::QName -{ - forwards to baseExpr(q, location=top.location); -} - -concrete production oAttribute -top::Expr ::= e::QName '@.' q::QNameAttrOccur -{ - forwards to access( - baseExpr( - e, - location=top.location), - '.', - q, - location=top.location); -} - -concrete production oApplication -top::Expr ::= e::Expr '@(' es::AppExprs ',' anns::AnnoAppExprs ')' -{ - forwards to - application( - e,'(',es,',', - snocAnnoAppExprs( - snocAnnoAppExprs( - anns, - ',', - annoExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"isNew"), - location=top.location), - location=top.location), - '=', - presentAppExpr( - trueConst( - 'true', - location=top.location), - location=top.location), - location=top.location), - location=top.location), - ',', - annoExpr( - qNameId( - nameIdLower( - terminal(IdLower_t,"origin"), - location=top.location), - location=top.location), - '=', - presentAppExpr( - top.ori, - location=top.location), - location=top.location), - location=top.location), - ')', - location=top.location); -} - -concrete production oApplicationAnno -top::Expr ::= e::Expr '@(' anns::AnnoAppExprs ')' -{ - forwards to - oApplication(e, $2, emptyAppExprs(location=$2.location), ',', anns, - $4, location=top.location); -} - -concrete production oApplicationExpr -top::Expr ::= e::Expr '@(' es::AppExprs ')' -{ - forwards to - oApplication(e, $2, es, ',', emptyAnnoAppExprs(location=$4.location), - $4, location=top.location); -} - -concrete production oApplicationEmpty -top::Expr ::= e::Expr '@(' ')' -{ - forwards to - oApplication(e, $2, emptyAppExprs(location=$2.location), ',', - emptyAnnoAppExprs(location=$2.location), - $3, location=top.location); -} diff --git a/grammars/silver/extension/list/List.sv b/grammars/silver/extension/list/List.sv deleted file mode 100644 index a3a9d4575..000000000 --- a/grammars/silver/extension/list/List.sv +++ /dev/null @@ -1,91 +0,0 @@ -grammar silver:extension:list; - -import silver:definition:type:syntax; - -terminal LSqr_t '[' ; -terminal RSqr_t ']' ; - --- The TYPE -------------------------------------------------------------------- -concrete production listTypeExpr -top::TypeExpr ::= '[' te::TypeExpr ']' -{ - top.unparse = "[" ++ te.unparse ++ "]"; - - top.typerep = listType(te.typerep); - - forwards to refTypeExpr('Decorated', - nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "core:List"), location=top.location), - botlSome('<', typeListSingle(te, location=te.location), '>', location=top.location), location=top.location), location=top.location); -} - --- The expressions ------------------------------------------------------------- - -concrete production emptyList -top::Expr ::= '[' ']' -{ - top.unparse = "[]"; - - forwards to mkStrFunctionInvocation(top.location, "core:nil", []); -} - --- TODO: BUG: '::' is HasType_t. We probably want to have a different --- terminal here, with different precedence! - -concrete production consListOp -top::Expr ::= h::Expr '::' t::Expr -{ - top.unparse = "(" ++ h.unparse ++ " :: " ++ t.unparse ++ ")" ; - - h.downSubst = top.downSubst; t.downSubst = top.downSubst; -- TODO BUG: don't know what this is needed... unparse apparently?? - - forwards to mkStrFunctionInvocation(top.location, "core:cons", [h, t]); -} - -concrete production fullList -top::Expr ::= '[' es::Exprs ']' -{ - top.unparse = "[ " ++ es.unparse ++ " ]"; - - es.downSubst = top.downSubst; -- TODO again, pretty printing garbage. - - forwards to es.listtrans; -} - -synthesized attribute listtrans :: Expr occurs on Exprs; - -aspect production exprsEmpty -top::Exprs ::= -{ - top.listtrans = emptyList('[',']', location=top.location); -} - -aspect production exprsSingle -top::Exprs ::= e::Expr -{ - top.listtrans = mkStrFunctionInvocation(e.location, "core:cons", [e, emptyList('[',']', location=top.location)]); -} - -aspect production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ - top.listtrans = mkStrFunctionInvocation(e1.location, "core:cons", [e1, e2.listtrans]); -} - --- Overloaded operators -------------------------------------------------------- - -abstract production listPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - forwards to mkStrFunctionInvocationDecorated(e1.location, "core:append", [e1,e2]); -} - -abstract production listLengthBouncer -top::Expr ::= e::Decorated Expr -{ - -- Because `e` will get wrapped in `exprRef`, it's errors will not appear in the - -- forward tree. - top.errors := forward.errors ++ e.errors; - - forwards to mkStrFunctionInvocationDecorated(e.location, "core:listLength", [e]); -} - diff --git a/grammars/silver/extension/list/Project.sv b/grammars/silver/extension/list/Project.sv deleted file mode 100644 index afada03a4..000000000 --- a/grammars/silver/extension/list/Project.sv +++ /dev/null @@ -1,11 +0,0 @@ -grammar silver:extension:list; - -imports silver:definition:type; -imports silver:definition:env; -imports silver:definition:core; - -exports silver:extension:list:java with silver:translation:java:type; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Lists\nmenu_title: Lists\nmenu_weight: 100\n---" -@} diff --git a/grammars/silver/extension/list/Type.sv b/grammars/silver/extension/list/Type.sv deleted file mode 100644 index 977fcd5aa..000000000 --- a/grammars/silver/extension/list/Type.sv +++ /dev/null @@ -1,29 +0,0 @@ -grammar silver:extension:list; - -abstract production listType -top::Type ::= el::Type -{ - top.freeVariables = el.freeVariables; -- TypeExp.sv - top.substituted = listType(el.substituted); - top.flatRenamed = listType(el.flatRenamed); - top.typepp = "[" ++ el.typepp ++ "]"; - - top.unify = - case top.unifyWith of - | listType(fel) -> unify(el,fel) - | decoratedType(nonterminalType("core:List", ftes)) -> unify(el, head(ftes)) - | _ -> errorSubst("Tried to unify list with " ++ prettyType(top.unifyWith)) - end; - - -- Suppress its "nonterminal"ness - top.isDecorable = false; - top.isDecorated = false; - --top.accessHandler = errorAccessHandler; -- permit this, since we need it for default, non-specialized java version - top.lengthDispatcher = listLengthBouncer(_, location=_); - top.appendDispatcher = listPlusPlus(_, _, location=_); - - --top.transType -- for translation. - - forwards to decoratedType(nonterminalType("core:List", [el])); -} - diff --git a/grammars/silver/extension/list/java/Type.sv b/grammars/silver/extension/list/java/Type.sv deleted file mode 100644 index f77159ce0..000000000 --- a/grammars/silver/extension/list/java/Type.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar silver:extension:list:java; - -import silver:extension:list; -import silver:definition:type; -import silver:definition:env; -import silver:definition:core; -import silver:translation:java:type; -import silver:translation:java:core; -- for the nil hack - -aspect production listType -top::Type ::= el::Type -{ - top.transType = "common.ConsCell"; - top.transTypeRep = s"new common.ListTypeRep(${el.transTypeRep})"; - top.transFreshTypeRep = s"new common.ListTypeRep(${el.transFreshTypeRep})"; -} - - --- A wonderous hack -aspect production emptyList -top::Expr ::= '[' ']' -{ - -- Technically, this is interfering, however it's hardly the worst violation - -- in the Silver compiler, and it's not too bad: we do the same thing, just - -- slightly more efficiently. - -- For the time being, this is easier than (and as effective as) amending - -- function calls with special optimization cases. - - -- This makes `[]` act like a constant literal (e.g. a number) instead of - -- a function call. - top.translation = "(common.ConsCell)common.ConsCell.nil"; - top.lazyTranslation = top.translation; -} diff --git a/grammars/silver/extension/monad/AbstractSyntax.sv b/grammars/silver/extension/monad/AbstractSyntax.sv deleted file mode 100644 index bfdfc155b..000000000 --- a/grammars/silver/extension/monad/AbstractSyntax.sv +++ /dev/null @@ -1,43 +0,0 @@ - -abstract production bindExpr -top::Expr ::= n::Name t::TypeExpr e::Expr rest::Expr bindFn::QName -{ - {-e.downSubst = top.downSubst; - rest.downSubst = e.upSubst; - forward.downSubst = rest.upSubst;-} - - local cont :: Expr = - lambdap( - productionRHSCons( - productionRHSElem( - n, '::', t, location=top.location), - productionRHSNil(location=top.location), - location=top.location), - rest, - location=top.location); - - forwards to - mkFunctionInvocation(top.location, baseExpr(bindFn, location=top.location), [e, cont]); -} - -abstract production returnExpr -top::Expr ::= e::Expr isFinalVal::Boolean returnFn::QName -{ - -- TODO: This is interfering... - top.errors <- - if !isFinalVal - then [wrn(top.location, "Return is not final value, and does not have type Unit")] - else []; - - forwards to - mkFunctionInvocation(top.location, baseExpr(returnFn, location=top.location), [e]); -} - -abstract production returnUnitExpr -top::Expr ::= returnFn::QName -{ - forwards to - returnExpr( - mkStrFunctionInvocation(top.location, "core:unit", []), - true, returnFn, location=top.location); -} diff --git a/grammars/silver/extension/monad/ConcreteSyntax.sv b/grammars/silver/extension/monad/ConcreteSyntax.sv deleted file mode 100644 index a55de6f90..000000000 --- a/grammars/silver/extension/monad/ConcreteSyntax.sv +++ /dev/null @@ -1,130 +0,0 @@ - -concrete production do_c -top::Expr ::= 'do' '(' bindFn::QName ',' returnFn::QName ')' '{' body::DoBodyStmts '}' -{ - top.unparse = s"do (${bindFn.unparse}, ${returnFn.unparse}) {${body.unparse}}"; - - local bindLookup::Decorated QNameLookup = bindFn.lookupValue; - local returnLookup::Decorated QNameLookup = returnFn.lookupValue; - - -- TODO: Raise errors once if bind/return have the wrong types - -- We may need typeclasses for this to be possible? - local localErrors::[Message] = bindLookup.errors ++ returnLookup.errors; - - body.bindFn = bindFn; - body.returnFn = returnFn; - body.isFinalVal = true; - - forwards to - if !null(localErrors) - then errorExpr(localErrors, location=top.location) - else body.transform; -} - -autocopy attribute bindFn::QName; -autocopy attribute returnFn::QName; - -autocopy attribute isFinalVal::Boolean; - -synthesized attribute transform::Expr; - -nonterminal DoBodyStmts with location, unparse, transform, bindFn, returnFn, isFinalVal; - -concrete production bindExprDoBodyStmts -top::DoBodyStmts ::= n::MName '::' t::TypeExpr '<-' e::Expr ';' rest::DoBodyStmts -{ - top.unparse = s"${n.unparse}::${t.unparse} <- ${e.unparse}; ${rest.unparse}"; - top.transform = - bindExpr(nameFromMName(n), t, e, rest.transform, top.bindFn, location=top.location); -} - -concrete production letExprDoBodyStmts -top::DoBodyStmts ::= n::MName '::' t::TypeExpr '=' e::Expr ';' rest::DoBodyStmts -{ - top.unparse = s"${n.unparse}::${t.unparse} = ${e.unparse}; ${rest.unparse}"; - - -- TODO: move this to AbstractSyntax for consistancy? - top.transform = - letp( - assignExpr(nameFromMName(n), '::', t, '=', e, location=top.location), - rest.transform, - location=top.location); -} - -concrete production consDoBodyStmt -top::DoBodyStmts ::= h::DoBodyStmt t::DoBodyStmts -{ - top.unparse = s"${h.unparse} ${t.unparse}"; - forwards to - bindExprDoBodyStmts( - mName("_", top.location), '::', - typerepTypeExpr(freshType(), location=top.location), - '<-', h.transform, ';', t, - location=top.location); - - h.isFinalVal = false; -} - -concrete production oneDoBodyStmt -top::DoBodyStmts ::= h::DoBodyStmt -{ - top.unparse = s"${h.unparse}"; - top.transform = h.transform; -} - -nonterminal DoBodyStmt with location, unparse, transform, bindFn, returnFn, isFinalVal; - -concrete production doBodyBlock -top::DoBodyStmt ::= '{' body::DoBodyStmts '}' -{ - top.unparse = s"{${body.unparse}}"; - top.transform = body.transform; -} - -concrete production exprDoBody -top::DoBodyStmt ::= e::Expr ';' -{ - top.unparse = s"${e.unparse};"; - --top.errors := e.errors; - top.transform = e; -} - -concrete production returnDoBody -top::DoBodyStmt ::= 'return' e::Expr ';' -{ - top.unparse = s"return ${e.unparse};"; - top.transform = returnExpr(e, top.isFinalVal, top.returnFn, location=top.location); -} - --- This could forward to returnDoBody, but not doing that so raising a warning on non-unit return --- is simpler -abstract production returnUnitDoBody -top::DoBodyStmt ::= -{ - top.unparse = s"return unit();"; - top.transform = returnUnitExpr(top.returnFn, location=top.location); -} - -concrete production condDoBody -top::DoBodyStmt ::= 'if' cond::Expr 'then' th::DoBodyStmt NoElse_t -{ - top.unparse = s"if ${cond.unparse} then ${th.unparse}"; - forwards to - condDoBodyElse( - 'if', cond, - 'then', th, - 'else', returnUnitDoBody(location=top.location), - location=top.location); -} - -concrete production condDoBodyElse -top::DoBodyStmt ::= 'if' cond::Expr 'then' th::DoBodyStmt 'else' el::DoBodyStmt -{ - top.unparse = s"if ${cond.unparse} then ${th.unparse} else ${el.unparse}"; - top.transform = - ifThenElse( - 'if', cond, - 'then', th.transform, - 'else', el.transform, - location=top.location); -} \ No newline at end of file diff --git a/grammars/silver/extension/monad/Name.sv b/grammars/silver/extension/monad/Name.sv deleted file mode 100644 index d0d75f947..000000000 --- a/grammars/silver/extension/monad/Name.sv +++ /dev/null @@ -1,37 +0,0 @@ --- Exact copy of Name, but productions have higher precedence. --- This is needed to avoid a parse conflict with '::' as both the type and cons operator --- Expr -> QName -> Name which can be followed by ::. This is a shift-reduce conflict. --- Using MName instead creates a reduce/reduce conflict with QName/Name which is resolved by setting --- the production precedence. - -nonterminal MName with config, grammarName, location, unparse, name; - -synthesized attribute name :: String; - -concrete production mNameIdLower -top::MName ::= id::IdLower_t -precedence=2 -{ - top.name = id.lexeme; - top.unparse = id.lexeme; -} -concrete production mNameIdUpper -top::MName ::= id::IdUpper_t -precedence=2 -{ - top.name = id.lexeme; - top.unparse = id.lexeme; -} - -function nameFromMName -Name ::= n::MName -{ - return name(n.name, n.location); -} - -function mName -MName ::= n::String l::Location -{ - return mNameIdLower(terminal(IdLower_t, n, l), location=l); -} - diff --git a/grammars/silver/extension/monad/Project.sv b/grammars/silver/extension/monad/Project.sv deleted file mode 100644 index 79b93cac8..000000000 --- a/grammars/silver/extension/monad/Project.sv +++ /dev/null @@ -1,13 +0,0 @@ -grammar silver:extension:monad; - -imports silver:definition:core; -imports silver:definition:type:syntax; - -imports silver:definition:env; -imports silver:definition:type; - -imports silver:extension:convenience; -imports silver:modification:lambda_fn; -imports silver:modification:let_fix; - ---import silver:util; \ No newline at end of file diff --git a/grammars/silver/extension/monad/Terminals.sv b/grammars/silver/extension/monad/Terminals.sv deleted file mode 100644 index 4e0ed586f..000000000 --- a/grammars/silver/extension/monad/Terminals.sv +++ /dev/null @@ -1,12 +0,0 @@ - -marking terminal Do_kwd 'do' lexer classes {KEYWORD,RESERVED}; - -terminal LArrow_t '<-' lexer classes {SPECOP}; ---terminal DoubleColon_t '::' lexer classes {SPECOP}; - --- Not currently used, but reserving these just in case ---terminal Monad_kwd 'monad' lexer classes {KEYWORD,RESERVED}; -terminal Sequence_t '>>' precedence = 2, association = left; -terminal Bind_kwd '>>=' precedence = 2, association = left; - -terminal NoElse_t '' precedence = 0; \ No newline at end of file diff --git a/grammars/silver/extension/patternmatching/Case.sv b/grammars/silver/extension/patternmatching/Case.sv deleted file mode 100644 index 69401e8c6..000000000 --- a/grammars/silver/extension/patternmatching/Case.sv +++ /dev/null @@ -1,556 +0,0 @@ -grammar silver:extension:patternmatching; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:modification:primitivepattern; - -import silver:definition:type:syntax only typerepTypeExpr; -import silver:modification:let_fix; - -terminal Case_kwd 'case' lexer classes {KEYWORD,RESERVED}; -terminal Of_kwd 'of' lexer classes {KEYWORD,RESERVED}; -terminal Arrow_kwd '->' lexer classes {SPECOP}; -terminal Vbar_kwd '|' lexer classes {SPECOP}; -terminal Opt_Vbar_t /\|?/ lexer classes {SPECOP}; -- optional Coq-style vbar. -terminal When_kwd 'when' lexer classes {KEYWORD,RESERVED}; -terminal Matches_kwd 'matches' lexer classes {KEYWORD}; - --- MR | ... -nonterminal MRuleList with location, config, unparse, env, frame, errors, matchRuleList, matchRulePatternSize; - --- Turns MRuleList (of MatchRules) into [AbstractMatchRule] -synthesized attribute matchRuleList :: [AbstractMatchRule]; --- Notification of the number of expressions being matched upon -autocopy attribute matchRulePatternSize :: Integer; - --- P -> E -nonterminal MatchRule with location, config, unparse, env, frame, errors, matchRuleList, matchRulePatternSize; -nonterminal AbstractMatchRule with location, unparse, headPattern, isVarMatchRule, expandHeadPattern; - --- The head pattern of a match rule -synthesized attribute headPattern :: Decorated Pattern; --- Whether the head pattern of a match rule is a variable binder or not -synthesized attribute isVarMatchRule :: Boolean; --- Turns A(B, C), D into B, C, D in the patterns list, with a list of named patterns to include. -synthesized attribute expandHeadPattern :: (AbstractMatchRule ::= [String]); - --- P , ... -nonterminal PatternList with location, config, unparse, patternList, env, frame, errors, patternVars, patternVarEnv; - --- Turns PatternList into [Pattern] -synthesized attribute patternList :: [Decorated Pattern]; - - -{- NOTE ON ERRORS: #HACK2012 - - - - All of the real error checking should be done in PrimitiveMatch.sv on the - - more primitive form of pattern matching. BUT, there are a few - - kinds of errors that the pattern matching compiler will OBSCURE - - and so we must check for them here. - - - - ANY error on MRuleList, MatchRule, PatternList, or Pattern should - - be accompanied by a comment explaining why it's there, and not on - - primitive match. - -} - - -concrete production caseExpr_c -top::Expr ::= 'case' es::Exprs 'of' Opt_Vbar_t ml::MRuleList 'end' -{ - top.unparse = "case " ++ es.unparse ++ " of " ++ ml.unparse ++ " end"; - - ml.matchRulePatternSize = length(es.rawExprs); - top.errors <- ml.errors; - - -- TODO: this is the only use of .rawExprs. FIXME - -- introduce the failure case here. - forwards to - caseExpr(es.rawExprs, ml.matchRuleList, - mkStrFunctionInvocation(top.location, "core:error", - [stringConst(terminal(String_t, - "\"Error: pattern match failed at " ++ top.grammarName ++ " " ++ top.location.unparse ++ "\\n\""), location=top.location)]), - freshType(), location=top.location); -} - -abstract production caseExpr -top::Expr ::= es::[Expr] ml::[AbstractMatchRule] failExpr::Expr retType::Type -{ - top.unparse = - "(case " ++ implode(", ", map((.unparse), es)) ++ " of " ++ - implode(" | ", map((.unparse), ml)) ++ " | _ -> " ++ failExpr.unparse ++ - " end :: " ++ prettyType(retType) ++ ")"; - - -- 4 cases: no patterns left, all constructors, all variables, or mixed con/var. - -- errors cases: more patterns no scrutinees, more scrutinees no patterns, no scrutinees multiple rules - forwards to - case ml of - | matchRule([], c, e) :: _ -> buildMatchWhenConditionals(ml, failExpr) -- valid or error case - -- No match rules, only possible through abstract syntax - | [] -> Silver_Expr { let res :: $TypeExpr{typerepTypeExpr(retType, location=top.location)} = $Expr{failExpr} in res end } - | _ -> if null(es) then failExpr -- error case - else if null(varRules) then allConCase - else if null(prodRules) then allVarCase - else mixedCase - end; - -- TODO: BUG: we're using the left of patterns in the first match rule as a guide here - -- which means we run into serious problems if not all match rules agree on the length - -- of the pattern list. We don't report some errors related to not having enough - -- variable binders - - top.errors <- - case ml of - -- are there multiple match rules, with no patterns/conditions left in them to distinguish between them? - | matchRule([], _, e) :: _ :: _ -> - if areUselessPatterns(ml) - then [err(top.location, "Pattern has overlapping cases!")] - else [] - | _ -> [] - end; - --- top.errors <- unsafeTrace([], --- print(top.unparse ++ "\n\n", unsafeIO())); - - local partMRs :: Pair<[AbstractMatchRule] [AbstractMatchRule]> = - partition((.isVarMatchRule), ml); - local varRules :: [AbstractMatchRule] = partMRs.fst; - local prodRules :: [AbstractMatchRule] = partMRs.snd; - - {-- - - All constructors? Then do a real primitive match. - -} - local freshCurrName :: String = "__curr_match_" ++ toString(genInt()); - local freshCurrNameRef :: Expr = - baseExpr(qName(top.location, freshCurrName), location=top.location); - local allConCase :: Expr = - -- Annoyingly, this now needs to be a let in case of annotation patterns. - makeLet(top.location, - freshCurrName, freshType(), head(es), - matchPrimitive( - freshCurrNameRef, - typerepTypeExpr(retType, location=top.location), - foldPrimPatterns( - map(allConCaseTransform(freshCurrNameRef, tail(es), failExpr, retType, _), - groupMRules(prodRules))), - failExpr, location=top.location)); - - {-- - - All variables? Just push a let binding inside each branch. - -} - local allVarCase :: Expr = - caseExpr(tail(es), - map(bindHeadPattern(head(es), freshType(){-whatever the first expression's type is?-}, _), - ml), - failExpr, retType, location=top.location); - -- A quick note about that freshType() hack: putting it here means there's ONE fresh type - -- generated, puching it inside 'bindHeadPattern' would generate multiple fresh types. - -- So don't try that! - - {-- - - Mixed con/var? Partition into segments and build nested case expressions - - The whole segment partitioning is done in a function rather than grabbing the initial segment - and forwarding to do the rest of the segments (another workable option) for efficiency - -} - local mixedCase :: Expr = buildMixedCaseMatches(es, ml, failExpr, retType, top.location); -} - - ---Get the initial segment of the match rules which all have the same ---pattern type (constructor or var) and the rest of the rules -function initialSegmentPatternType -Pair<[AbstractMatchRule] [AbstractMatchRule]> ::= lst::[AbstractMatchRule] -{ - return case lst of - --this probably shouldn't be called with an empty list, but catch it anyway - | [] -> pair([], []) - | [mr] -> pair([mr], []) - | mr1::mr2::rest -> - if mr1.isVarMatchRule == mr2.isVarMatchRule - then --both have the same type of pattern - let sub::Pair<[AbstractMatchRule] [AbstractMatchRule]> = initialSegmentPatternType(mr2::rest) - in pair(mr1::sub.fst, sub.snd) end - else --the first has a different type of pattern than the second - pair([mr1], mr2::rest) - end; -} - -{- - Build the correct match expression when we are mixing constructor - and variable patterns for the first match. We do this by - partitioning the list into segments of only constructor or variable - patterns in order, then putting each segment into its own match. --} -function buildMixedCaseMatches -Expr ::= es::[Expr] ml::[AbstractMatchRule] failExpr::Expr retType::Type loc::Location -{ - local freshFailName :: String = "__fail_" ++ toString(genInt()); - return if null(ml) - then failExpr - else let segments::Pair<[AbstractMatchRule] [AbstractMatchRule]> = - initialSegmentPatternType(ml) - in - makeLet(loc, freshFailName, retType, - buildMixedCaseMatches(es, segments.snd, failExpr, retType, loc), - caseExpr(es, segments.fst, baseExpr(qName(loc, freshFailName), location=loc), - retType, location=loc)) - end; -} - - - - ---Match Rules -concrete production mRuleList_one -top::MRuleList ::= m::MatchRule -{ - top.unparse = m.unparse; - top.errors := m.errors; - - top.matchRuleList = m.matchRuleList; -} - -concrete production mRuleList_cons -top::MRuleList ::= h::MatchRule '|' t::MRuleList -{ - top.unparse = h.unparse ++ " | " ++ t.unparse; - top.errors := h.errors ++ t.errors; - - top.matchRuleList = h.matchRuleList ++ t.matchRuleList; -} - -concrete production matchRule_c -top::MatchRule ::= pt::PatternList '->' e::Expr -{ - top.unparse = pt.unparse ++ " -> " ++ e.unparse; - top.errors := pt.errors; -- e.errors is examined later, after transformation. - - top.errors <- - if length(pt.patternList) == top.matchRulePatternSize then [] - else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; - - pt.patternVarEnv = []; - - top.matchRuleList = [matchRule(pt.patternList, nothing(), e, location=top.location)]; -} - -concrete production matchRuleWhen_c -top::MatchRule ::= pt::PatternList 'when' cond::Expr '->' e::Expr -{ - top.unparse = pt.unparse ++ " when " ++ cond.unparse ++ " -> " ++ e.unparse; - top.errors := pt.errors; -- e.errors is examined later, after transformation, as is cond.errors - - top.errors <- - if length(pt.patternList) == top.matchRulePatternSize then [] - else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; - - pt.patternVarEnv = []; - - top.matchRuleList = [matchRule(pt.patternList, just(pair(cond, nothing())), e, location=top.location)]; -} - -concrete production matchRuleWhenMatches_c -top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern '->' e::Expr -{ - top.unparse = pt.unparse ++ " when " ++ cond.unparse ++ " matches " ++ p.unparse ++ " -> " ++ e.unparse; - top.errors := pt.errors; -- e.errors is examined later, after transformation, as is cond.errors - - top.errors <- - if length(pt.patternList) == top.matchRulePatternSize then [] - else [err(pt.location, "case expression matching against " ++ toString(top.matchRulePatternSize) ++ " values, but this rule has " ++ toString(length(pt.patternList)) ++ " patterns")]; - - pt.patternVarEnv = []; - p.patternVarEnv = pt.patternVars; - - top.matchRuleList = [matchRule(pt.patternList, just(pair(cond, just(p))), e, location=top.location)]; -} - -abstract production matchRule -top::AbstractMatchRule ::= pl::[Decorated Pattern] cond::Maybe>> e::Expr -{ - top.unparse = - implode(", ", map((.unparse), pl)) ++ - case cond of - | just(pair(c, just(p))) -> " when " ++ c.unparse ++ " matches " ++ p.unparse - | just(pair(c, nothing())) -> " when " ++ c.unparse - | nothing() -> "" - end ++ - " -> " ++ e.unparse; - top.headPattern = head(pl); - -- If pl is null, and we're consulted, then we're missing patterns, pretend they're _ - top.isVarMatchRule = null(pl) || head(pl).patternIsVariable; - -- For this, we safely know that pl is not null: - top.expandHeadPattern = - \ named::[String] -> - matchRule( - head(pl).patternSubPatternList ++ - map( - \ n::String -> - fromMaybe( - decorate wildcPattern('_', location=top.location) - with { config=head(pl).config; env=head(pl).env; patternVarEnv = []; }, - lookupBy(stringEq, n, head(pl).patternNamedSubPatternList)), - named) ++ - tail(pl), - cond, e, location=top.location); -} - -concrete production patternList_one -top::PatternList ::= p::Pattern -{ - top.unparse = p.unparse; - top.errors := p.errors; - - top.patternVars = p.patternVars; - p.patternVarEnv = top.patternVarEnv; - - top.patternList = [p]; -} -concrete production patternList_snoc -top::PatternList ::= ps::PatternList ',' p::Pattern -{ - top.unparse = ps.unparse ++ ", " ++ p.unparse; - - forwards to appendPatternList(ps, patternList_one(p, location=p.location)); -} -abstract production patternList_more -top::PatternList ::= p::Pattern ',' ps1::PatternList -{ - top.unparse = p.unparse ++ ", " ++ ps1.unparse; - top.errors := p.errors ++ ps1.errors; - - top.patternVars = p.patternVars ++ ps1.patternVars; - ps1.patternVarEnv = p.patternVarEnv ++ p.patternVars; - - top.patternList = p :: ps1.patternList; -} - --- lol, dangling comma bug TODO -concrete production patternList_nil -top::PatternList ::= -{ - top.unparse = ""; - top.errors := []; - - top.patternVars = []; - top.patternList = []; -} - ----------------------------------------------------- --- Added Functions ----------------------------------------------------- -function appendPatternList -PatternList ::= p1::PatternList p2::PatternList -{ - return - case p1 of - | patternList_more(h, _, t) -> - patternList_more(h, ',', appendPatternList(t, p2), location=p1.location) - | patternList_one(h) -> - patternList_more(h, ',', p2, location=p1.location) - | patternList_nil() -> p2 - end; -} - -function patternListVars -Name ::= p::Decorated Pattern -{ - local n :: String = - case p of - | varPattern(pvn) -> "__sv_pv_" ++ toString(genInt()) ++ "_" ++ pvn.name - | h -> "__sv_tmp_pv_" ++ toString(genInt()) - end; - return name(n, p.location); -} -function convStringsToVarBinders -VarBinders ::= s::[Name] l::Location -{ - return if null(s) then nilVarBinder(location=l) - else if null(tail(s)) then oneVarBinder(varVarBinder(head(s), location=head(s).location), location=l) - else consVarBinder(varVarBinder(head(s), location=head(s).location), ',', convStringsToVarBinders(tail(s), l), location=l); -} -function exprFromName -Expr ::= n::Name -{ - return baseExpr(qNameId(n, location=n.location), location=n.location); -} - -{-- - - Takes a set of matchrules that all match against the SAME CONSTRUCTOR and pushes - - a complex case-expr within a primitive pattern that matches this constructor. - - - - @param currExpr (The current expression to match against in the overall complex case-expr) - - @param restExprs (The remaining expressions to match against in the overall complex case-expr) - - @param failCase (The failure expression) - - @param retType (The return type of the overall case-expr, and thus this) - - @param mrs (Match rules that all share the same head-pattern) - - - - @return A primitive pattern matching the constructor, with the overall case-expr pushed down into it - -} -function allConCaseTransform -PrimPattern ::= currExpr::Expr restExprs::[Expr] failCase::Expr retType::Type mrs::[AbstractMatchRule] -{ - -- TODO: potential source of buggy error messages. We're using head(mrs) as the source of - -- authority for the length of pattern variables to match against. But each match rule may - -- actually have a different length (and .expandHeadPattern just applies whatever is there) - -- This is an erroneous condition, but it means we transform into a maybe-more erroneous condition. - local names :: [Name] = map(patternListVars, head(mrs).headPattern.patternSubPatternList); - - local subcase :: Expr = - caseExpr( - map(exprFromName, names) ++ annoAccesses ++ restExprs, - map(\ mr::AbstractMatchRule -> mr.expandHeadPattern(annos), mrs), - failCase, retType, location=head(mrs).location); - -- TODO: head(mrs).location is probably not the correct thing to use here?? (generally) - - local annos :: [String] = - nubBy(stringEq, map(fst, flatMap((.patternNamedSubPatternList), map((.headPattern), mrs)))); - local annoAccesses :: [Expr] = - map(\ n::String -> access(currExpr, '.', qNameAttrOccur(qName(l, n), location=l), location=l), annos); - - -- Maybe this one is more reasonable? We need to test examples and see what happens... - local l :: Location = head(mrs).headPattern.location; - - return - case head(mrs).headPattern of - | prodAppPattern_named(qn,_,_,_,_,_) -> - prodPattern(qn, '(', convStringsToVarBinders(names, l), ')', '->', subcase, location=l) - | intPattern(it) -> integerPattern(it, '->', subcase, location=l) - | fltPattern(it) -> floatPattern(it, '->', subcase, location=l) - | strPattern(it) -> stringPattern(it, '->', subcase, location=l) - | truePattern(_) -> booleanPattern("true", '->', subcase, location=l) - | falsePattern(_) -> booleanPattern("false", '->', subcase, location=l) - | nilListPattern(_,_) -> nilPattern(subcase, location=l) - | consListPattern(h,_,t) -> conslstPattern(head(names), head(tail(names)), subcase, location=l) - end; -} - -function foldPrimPatterns -PrimPatterns ::= l::[PrimPattern] -{ - return if null(tail(l)) then onePattern(head(l), location=head(l).location) - else consPattern(head(l), '|', foldPrimPatterns(tail(l)), location=head(l).location); -} - -{-- - - Remove the first pattern from the rule, and put a let binding of it into - - the expression. - - - - Would like to make this an attribute instead of a function, but - - (a) we don't have lambdas yet, and the attr would need to be a function value - - (b) we don't have a nice way of applying to all element of a list of functions - - e.g. right now we 'map(this(x, y, _), list)' - -} -function bindHeadPattern -AbstractMatchRule ::= headExpr::Expr headType::Type absRule::AbstractMatchRule -{ - -- If it's '_' we do nothing, otherwise, bind away! - return case absRule of - | matchRule(headPat :: restPat, cond, e) -> - case headPat.patternVariableName of - | just(pvn) -> - matchRule( - restPat, - case cond of - | just(pair(c, p)) -> just(pair(makeLet(absRule.location, pvn, headType, headExpr, c), p)) - | nothing() -> nothing() - end, - makeLet(absRule.location, pvn, headType, headExpr, e), - location=absRule.location) - | nothing() -> matchRule(restPat, cond, e, location=absRule.location) - end - end; -} - -function makeLet -Expr ::= l::Location s::String t::Type e::Expr o::Expr -{ - return letp( - assignExpr( - name(s, l), '::', typerepTypeExpr(t, location=l), '=', e, location=l), - o, location=l); -} - -function ensureDecoratedExpr -Expr ::= e::Decorated Expr -{ - local et :: Type = performSubstitution(e.typerep, e.upSubst); - - return if et.isDecorable - then decorateExprWithEmpty('decorate', exprRef(e, location=e.location), 'with', '{', '}', location=e.location) - else exprRef(e, location=e.location); -} - -function mruleEqForGrouping -Boolean ::= a::AbstractMatchRule b::AbstractMatchRule -{ - return a.headPattern.patternSortKey == b.headPattern.patternSortKey; -} -function mruleLTEForSorting -Boolean ::= a::AbstractMatchRule b::AbstractMatchRule -{ - return a.headPattern.patternSortKey <= b.headPattern.patternSortKey; -} -{-- - - Given a list of match rules, examine the "head pattern" of each. - - Sort and group by the key of this head pattern. - - - - i.e. [cons, nil, cons] becomes [[cons, cons], [nil]] (where 'cons' is the key of the head pattern) - -} -function groupMRules -[[AbstractMatchRule]] ::= l::[AbstractMatchRule] -{ - return groupBy(mruleEqForGrouping, sortBy(mruleLTEForSorting, l)); -} - -{-- - - Given a list of match rules, which are presumed to match empty - - patterns (this is not checked), turn them into nested - - conditionals. - -} -function buildMatchWhenConditionals -Expr ::= ml::[AbstractMatchRule] failExpr::Expr -{ - return - case ml of - | matchRule(_, just(pair(c, nothing())), e) :: tl -> - Silver_Expr { - if $Expr{c} - then $Expr{e} - else $Expr{buildMatchWhenConditionals(tl, failExpr)} - } - | matchRule(_, just(pair(c, just(p))), e) :: tl -> - Silver_Expr { - case $Expr{c} of - | $Pattern{p} -> $Expr{e} - | _ -> $Expr{buildMatchWhenConditionals(tl, failExpr)} - end - } - | matchRule(_, nothing(), e) :: tl -> e - | [] -> failExpr - end; -} - -{-- - - Check whether there are patterns that overlap in a list of match - - rules which are presumed to match empty patterns (this is not - - checked here). - - - - An answer of true definitively means there are useless patterns. - - An answer of false means there may be, but it would require - - analysis of the conditions on the patterns to determine whether - - they are actually useless. We do not do that. - -} -function areUselessPatterns -Boolean ::= ml::[AbstractMatchRule] -{ - return - case ml of - | matchRule(_, just(_), _) :: tl -> - areUselessPatterns(tl) - | matchRule(_, nothing(), _) :: _ :: _ -> true - | matchRule(_, nothing(), _) :: [] -> false - | [] -> false - end; -} - - diff --git a/grammars/silver/extension/patternmatching/DocConfig.sv b/grammars/silver/extension/patternmatching/DocConfig.sv deleted file mode 100644 index 30157179d..000000000 --- a/grammars/silver/extension/patternmatching/DocConfig.sv +++ /dev/null @@ -1,5 +0,0 @@ -grammar silver:extension:patternmatching; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Pattern Matching\nmenu_title: Pattern Matching\nmenu_weight: 100\n---" -@} diff --git a/grammars/silver/extension/patternmatching/PatternTypes.sv b/grammars/silver/extension/patternmatching/PatternTypes.sv deleted file mode 100644 index 6bb0fe288..000000000 --- a/grammars/silver/extension/patternmatching/PatternTypes.sv +++ /dev/null @@ -1,324 +0,0 @@ -grammar silver:extension:patternmatching; - -import silver:extension:list only LSqr_t, RSqr_t; - -{-- - - The forms of syntactic patterns that are permissible in (nested) case expresssions. - -} -nonterminal Pattern with location, config, unparse, env, frame, errors, patternVars, patternVarEnv, patternIsVariable, patternVariableName, patternSubPatternList, patternNamedSubPatternList, patternSortKey; - -{-- - - The names of all var patterns in the pattern. - -} -synthesized attribute patternVars :: [String]; -{-- - - The names of all var patterns seen so far. - -} -autocopy attribute patternVarEnv :: [String]; -{-- - - False if it actually matches anything specific, true if it's a variable/wildcard. - -} -synthesized attribute patternIsVariable :: Boolean; -{-- - - The name of the variable, if any (e.g. wildcards!) - -} -synthesized attribute patternVariableName :: Maybe; -{-- - - Each positional child pattern below this one. - -} -synthesized attribute patternSubPatternList :: [Decorated Pattern]; -{-- - - Each named child pattern below this one. - -} -synthesized attribute patternNamedSubPatternList :: [Pair]; -{-- - - The sort (and grouping) key of the pattern. - - "~var" if patternIsVariable is true. (TODO: actually, we should call it undefined! It's not used.) - - fullname if it's a production. - - otherwise, it is type-depedent, but same values should be the same! - -} -synthesized attribute patternSortKey :: String; - - --- These are the "canonical" patterns: - -{-- - - Match on a production, extracting children as patterns, too. - - The production name may be qualified. - - TODO: if not qualified filter down to productions on scruntinee type - -} -concrete production prodAppPattern_named -top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' -{ - top.unparse = prod.unparse ++ "(" ++ ps.unparse ++ ")"; - top.errors := ps.errors ++ nps.errors; - - local parms :: Integer = length(prod.lookupValue.typerep.inputTypes); - - top.errors <- - if null(prod.lookupValue.dcls) || length(ps.patternList) == parms then [] - else [err(prod.location, prod.name ++ " has " ++ toString(parms) ++ " parameters but " ++ toString(length(ps.patternList)) ++ " patterns were provided")]; - - top.patternVars = ps.patternVars ++ nps.patternVars; - nps.patternVarEnv = ps.patternVarEnv ++ ps.patternVars; - - top.patternIsVariable = false; - top.patternVariableName = nothing(); - top.patternSubPatternList = ps.patternList; - top.patternNamedSubPatternList = nps.namedPatternList; - top.patternSortKey = prod.lookupValue.fullName; -} - -concrete production prodAppPattern -top::Pattern ::= prod::QName '(' ps::PatternList ')' -{ - forwards to prodAppPattern_named(prod, '(', ps, ',', namedPatternList_nil(location=top.location), ')', location=top.location); -} - -concrete production propAppPattern_onlyNamed -top::Pattern ::= prod::QName '(' nps::NamedPatternList ')' -{ - forwards to prodAppPattern_named(prod, '(', patternList_nil(location=top.location), ',', nps, ')', location=top.location); -} - -{-- - - Match anything, and bind nothing. - -} -concrete production wildcPattern -top::Pattern ::= '_' -{ - top.unparse = "_"; - top.errors := []; - - top.patternVars = []; - top.patternIsVariable = true; - top.patternVariableName = nothing(); - top.patternSubPatternList = []; - top.patternSortKey = "~var"; -} - -{-- - - Match anything, bind it to a name. - - Note 1: must be lower case, to avoid newbie confusions "case (e::a) of Expr -> ..." - - Note 2: must not shadow a production name, to avoid "forgot parens" confusion - - (e.g. "case maybe of nothing -> ..." vs "nothing()") - -} -concrete production varPattern -top::Pattern ::= v::Name -{ - top.unparse = v.name; - top.errors := []; - top.errors <- - if containsBy(stringEq, v.name, top.patternVarEnv) - then [err(v.location, "Duplicate pattern variable " ++ v.name)] - else []; - top.errors <- - if isUpper(substring(0,1,v.name)) - then [err(v.location, "Pattern variable names start with a lower case letter")] - else []; - top.errors <- - case getValueDcl(v.name, top.env) of - | prodDcl(_,_,_,_) :: _ -> - [err(v.location, "Pattern variables should not share the name of a production. (Potential confusion between '" ++ v.name ++ "' and '" ++ v.name ++ "()')")] - | _ -> [] - end; - - top.patternVars = [v.name]; - top.patternIsVariable = true; - top.patternVariableName = just(v.name); - top.patternSubPatternList = []; - top.patternSortKey = "~var"; -} - -{-- - - For other extensions to pattern matching. Basically acts like a wildcard. - -} -abstract production errorPattern -top::Pattern ::= msg::[Message] -{ - top.unparse = s"{-${messagesToString(msg)}-}"; - top.errors := msg; - - top.patternVars = []; - top.patternIsVariable = true; - top.patternVariableName = nothing(); - top.patternSubPatternList = []; - top.patternSortKey = "error"; -} - -aspect default production -top::Pattern ::= -{ - -- All other patterns should never set these to anything else, so let's default them. - top.patternIsVariable = false; - top.patternVariableName = nothing(); - top.patternNamedSubPatternList = []; -} - --------------------------------------------------------------------------------- - --- Below are the non-canonical patterns, i.e. those for other types - -concrete production intPattern -top::Pattern ::= num::Int_t -{ - top.unparse = num.lexeme; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = num.lexeme; -} - -concrete production fltPattern -top::Pattern ::= num::Float_t -{ - top.unparse = num.lexeme; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = num.lexeme; -} - -concrete production strPattern -top::Pattern ::= str::String_t -{ - top.unparse = str.lexeme; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = str.lexeme; -} - -concrete production truePattern -top::Pattern ::= 'true' -{ - top.unparse = "true"; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = "true"; -} - -concrete production falsePattern -top::Pattern ::= 'false' -{ - top.unparse = "false"; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = "false"; -} - -abstract production nilListPattern -top::Pattern ::= '[' ']' -{ - top.unparse = "[]"; - top.errors := []; - - top.patternVars = []; - top.patternSubPatternList = []; - top.patternSortKey = "core:nil"; -} - -concrete production consListPattern -top::Pattern ::= hp::Pattern '::' tp::Pattern -{ - top.unparse = hp.unparse ++ "::" ++ tp.unparse; - top.errors := hp.errors ++ tp.errors; - - top.patternVars = hp.patternVars ++ tp.patternVars; - hp.patternVarEnv = top.patternVarEnv; - tp.patternVarEnv = hp.patternVarEnv ++ hp.patternVars; - - top.patternSubPatternList = [hp, tp]; - top.patternSortKey = "core:cons"; -} - --- List literal patterns -concrete production listPattern -top::Pattern ::= '[' ps::PatternList ']' -{ - top.unparse = s"[${ps.unparse}]"; - forwards to ps.asListPattern; -} - -synthesized attribute asListPattern::Pattern occurs on PatternList; - -aspect production patternList_one -top::PatternList ::= p::Pattern -{ - top.asListPattern = - consListPattern(p, '::', nilListPattern('[', ']', location=top.location), location=top.location); -} -aspect production patternList_more -top::PatternList ::= p::Pattern ',' ps1::PatternList -{ - top.asListPattern = consListPattern(p, '::', ps1.asListPattern, location=top.location); -} -aspect production patternList_nil -top::PatternList ::= -{ - top.asListPattern = nilListPattern('[', ']', location=top.location); -} - -synthesized attribute namedPatternList::[Pair]; - -nonterminal NamedPatternList with location, config, unparse, env, errors, patternVars, patternVarEnv, namedPatternList; - -concrete production namedPatternList_one -top::NamedPatternList ::= p::NamedPattern -{ - top.unparse = p.unparse; - top.errors := p.errors; - - top.patternVars = p.patternVars; - top.namedPatternList = p.namedPatternList; -} -concrete production namedPatternList_more -top::NamedPatternList ::= p::NamedPattern ',' ps::NamedPatternList -{ - top.unparse = p.unparse ++ ", " ++ ps.unparse; - top.errors := p.errors ++ ps.errors; - - top.patternVars = p.patternVars ++ ps.patternVars; - ps.patternVarEnv = p.patternVarEnv ++ p.patternVars; - top.namedPatternList = p.namedPatternList ++ ps.namedPatternList; -} - --- Abstract only to avoid parse conflict -abstract production namedPatternList_nil -top::NamedPatternList ::= -{ - top.unparse = ""; - top.errors := []; - - top.patternVars = []; - top.namedPatternList = []; -} - -nonterminal NamedPattern with location, config, unparse, env, errors, patternVars, patternVarEnv, namedPatternList; - -concrete production namedPattern -top::NamedPattern ::= qn::QName '=' p::Pattern -{ - top.unparse = s"${qn.unparse}=${p.unparse}"; - top.errors := p.errors; - - -- TODO: Error checking for annotation patterns is a bit broken. - -- We can check that it is an annotation here, but any other - -- errors will show up in the generated code, potentially in the wrong - -- (or more than one) place. - top.errors <- - if qn.lookupAttribute.found && !qn.lookupAttribute.dcl.isAnnotation - then [err(qn.location, s"${qn.name} is not an annotation")] - else []; - - top.patternVars = p.patternVars; - top.namedPatternList = [pair(qn.lookupAttribute.fullName, p)]; -} - diff --git a/grammars/silver/extension/reflection/BuiltinFunctions.sv b/grammars/silver/extension/reflection/BuiltinFunctions.sv deleted file mode 100644 index e01df7c18..000000000 --- a/grammars/silver/extension/reflection/BuiltinFunctions.sv +++ /dev/null @@ -1,70 +0,0 @@ -grammar silver:extension:reflection; - -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:env; -imports silver:extension:patternmatching; -imports silver:modification:let_fix; - -terminal ReifyUnchecked_kwd 'reifyUnchecked' lexer classes {BUILTIN,RESERVED}; -terminal Deserialize_kwd 'deserialize' lexer classes {BUILTIN,RESERVED}; - --- Useful shorthand in cases when we want to immediately raise an error on failure -concrete production reifyUncheckedFunction -top::Expr ::= 'reifyUnchecked' '(' e::Expr ')' -{ - forwards to - Silver_Expr { - case reify($Expr{e}) of - | left(msg) -> error(msg) - | right(a) -> a - end - }; -} - -{-- - - This production deserializes a string to what it represents, in contrast to - - `reify` which "deserializes" an `AST` to what it represents, and in contrast to - - `deserializeAST` which deserializes a string to an `AST`. - - i.e. this function is `reify . deserializeAST` so to speak. - - - - This also has to be a built-in function because reification requires an - - explicit concrete type at its call site, which makes it difficult to write ordinary - - functions for. Something we might someday be able to solve with a typeclass? --} -concrete production deserializeFunction -top::Expr ::= 'deserialize' '(' fileName::Expr ',' text::Expr ')' -{ - top.unparse = s"deserialize(${fileName.unparse}, ${text.unparse})"; - - local errCheck1::TypeCheck = check(fileName.typerep, stringType()); - errCheck1.finalSubst = top.finalSubst; - local errCheck2::TypeCheck = check(text.typerep, stringType()); - errCheck2.finalSubst = top.finalSubst; - - fileName.downSubst = top.downSubst; - text.downSubst = fileName.upSubst; - errCheck1.downSubst = text.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - --top.upSubst = errCheck2.upSubst; - - local localErrors::[Message] = - (if errCheck1.typeerror - then [err(fileName.location, "First operand to 'deserialize(fileName, text)' must be a String, instead it is " ++ errCheck1.leftpp)] - else []) ++ - (if errCheck2.typeerror - then [err(text.location, "Second operand to 'deserialize(fileName, text)' must be a String, instead it is " ++ errCheck2.leftpp)] - else []); - - local fwrd::Expr = - Silver_Expr { - case deserializeAST($Expr {exprRef(fileName, location=builtin)}, $Expr {exprRef(text, location=builtin)}) of - | left(msg) -> left(msg) - | right(ast) -> reify(ast) - end - }; - - forwards to if !null(localErrors) then errorExpr(localErrors, location=top.location) else fwrd; -} - -global builtin::Location = builtinLoc("reflection"); diff --git a/grammars/silver/extension/rewriting/Expr.sv b/grammars/silver/extension/rewriting/Expr.sv deleted file mode 100644 index b916855fe..000000000 --- a/grammars/silver/extension/rewriting/Expr.sv +++ /dev/null @@ -1,778 +0,0 @@ -grammar silver:extension:rewriting; - --- Environment mapping variables that were defined on the rule RHS to Booleans indicating whether --- the variable was explicitly (i.e. not implicitly) decorated in the pattern. --- TODO: Lots of flow errors in this grammar because we are pretending this attribute is in the reference set -autocopy attribute boundVars::[Pair] occurs on Expr, Exprs, ExprInhs, ExprInh, AppExprs, AppExpr, AnnoAppExprs, AnnoExpr, AssignExpr, PrimPatterns, PrimPattern; - -attribute transform occurs on Expr; - -synthesized attribute decRuleExprs::[Pair] occurs on Expr, AssignExpr, PrimPatterns, PrimPattern; - -aspect default production -top::Expr ::= -{ - top.transform = - antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); - top.decRuleExprs = []; -- Only needed on things resulting from the translation of caseExpr -} - -aspect production lexicalLocalReference -top::Expr ::= q::Decorated QName _ _ -{ - -- In regular pattern matching nonterminal values are always effectively decorated, but we are - -- using the same typing behavior while matching on *undecorated* trees. So when a variable is - -- referenced as decorated we must seperately handle the cases of when it was already decorated - -- or was decorated implicitly (in which case we need to explicitly decorate it here.) The same - -- problem exists when dealing with implicit undecoration. - top.transform = - case lookupBy(stringEq, q.name, top.boundVars) of - | just(bindingIsDecorated) -> - -- The variable is bound in the rule - if finalType(top).isDecorated && !bindingIsDecorated - then - -- We want the decorated version, but the bound value is undecorated - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(top).decoratedType, location=builtin)} -> - $Expr{ - decorateExprWithEmpty( - 'decorate', Silver_Expr { e }, 'with', '{', '}', - location=top.location)}) - }), - consASTExpr(varASTExpr(q.name), nilASTExpr()), - nilNamedASTExpr()) - else if finalType(top).isDecorable && bindingIsDecorated - -- We want the undecorated version, but the bound value is decorated - then - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(decoratedType(finalType(top)), location=builtin)} -> new(e)) - }), - consASTExpr(varASTExpr(q.name), nilASTExpr()), - nilNamedASTExpr()) - -- Both (or neither) the bound value/desired type is a decorated nonterminal - just return the value - else varASTExpr(q.name) - | nothing() -> - -- The variable is bound in an enclosing let/match - -- Explicitly undecorate the variable, if appropriate for the final expected type - if q.lookupValue.typerep.isDecorable && !finalType(top).isDecorated - then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(new($Expr{top})) }) - else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }) - end; -} - -aspect production childReference -top::Expr ::= q::Decorated QName -{ - top.transform = - -- Explicitly undecorate the variable, if appropriate for the final expected type - if q.lookupValue.typerep.isDecorable && !finalType(top).isDecorated - then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(new($Expr{top})) }) - else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); -} - -aspect production lhsReference -top::Expr ::= q::Decorated QName -{ - top.transform = - -- Explicitly undecorate the variable, if appropriate for the final expected type - if q.lookupValue.typerep.isDecorable && !finalType(top).isDecorated - then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(new($Expr{top})) }) - else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); -} - -aspect production localReference -top::Expr ::= q::Decorated QName -{ - top.transform = - -- Explicitly undecorate the variable, if appropriate for the final expected type - if q.lookupValue.typerep.isDecorable && !finalType(top).isDecorated - then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(new($Expr{top})) }) - else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); -} - -aspect production forwardReference -top::Expr ::= q::Decorated QName -{ - top.transform = - -- Explicitly undecorate the variable, if appropriate for the final expected type - if q.lookupValue.typerep.isDecorable && !finalType(top).isDecorated - then antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr(new($Expr{top})) }) - else antiquoteASTExpr(Silver_Expr { silver:rewrite:anyASTExpr($Expr{top}) }); -} - -aspect production errorApplication -top::Expr ::= e::Decorated Expr es::AppExprs anns::AnnoAppExprs -{ - top.transform = applyASTExpr(e.transform, es.transform, anns.transform); -} - -aspect production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.transform = - case e of - | productionReference(q) -> prodCallASTExpr(q.lookupValue.fullName, es.transform, anns.transform) - | _ -> applyASTExpr(e.transform, es.transform, anns.transform) - end; -} - -aspect production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs anns::Decorated AnnoAppExprs -{ - top.transform = applyASTExpr(e.transform, es.transform, anns.transform); -} - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.forward) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - - -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - case e of - -- Special cases to avoid introducing a reference and causing flow errors. - | decorateExprWith(_, eUndec, _, _, inh, _) -> - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - $Expr{ - lambdap( - productionRHSCons( - productionRHSElem( - name("_e", builtin), '::', - typerepTypeExpr(finalType(eUndec), location=builtin), - location=builtin), - inh.lambdaParams, - location=builtin), - Silver_Expr { - $Expr{ - decorateExprWith( - 'decorate', baseExpr(qName(builtin, "_e"), location=builtin), - 'with', '{', inh.bodyExprInhTransform, '}', - location=builtin)}.$qName{q.name} - }, - location=builtin)}) - }), - consASTExpr(eUndec.transform, inh.transform), - nilNamedASTExpr()) - | lexicalLocalReference(qn, _, _) when - case lookupBy(stringEq, qn.name, top.boundVars) of - | just(bindingIsDecorated) -> !bindingIsDecorated - | nothing() -> false - end -> - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e).decoratedType, location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(varASTExpr(qn.name), nilASTExpr()), - nilNamedASTExpr()) - | _ -> - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()) - end; -} - -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> e.$qName{q.name}) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - $Expr{ - lambdap( - productionRHSCons( - productionRHSElem( - name("_e", builtin), '::', - typerepTypeExpr(finalType(e), location=builtin), - location=builtin), - inh.lambdaParams, - location=builtin), - decorateExprWith( - 'decorate', baseExpr(qName(builtin, "_e"), location=builtin), - 'with', '{', inh.bodyExprInhTransform, '}', - location=builtin), - location=builtin)}) - }), - consASTExpr(e.transform, inh.transform), - nilNamedASTExpr()); -} - -attribute transform occurs on ExprInhs; -synthesized attribute lambdaParams::ProductionRHS occurs on ExprInhs; -functor attribute bodyExprInhTransform occurs on ExprInhs, ExprInh; -propagate bodyExprInhTransform on ExprInhs; - -aspect production exprInhsEmpty -top::ExprInhs ::= -{ - top.transform = nilASTExpr(); - top.lambdaParams = productionRHSNil(location=builtin); -} - -aspect production exprInhsOne -top::ExprInhs ::= lhs::ExprInh -{ - top.transform = consASTExpr(lhs.transform, nilASTExpr()); - top.lambdaParams = - productionRHSCons(lhs.lambdaParam, productionRHSNil(location=builtin), location=builtin); -} - -aspect production exprInhsCons -top::ExprInhs ::= lhs::ExprInh inh::ExprInhs -{ - top.transform = consASTExpr(lhs.transform, inh.transform); - top.lambdaParams = productionRHSCons(lhs.lambdaParam, inh.lambdaParams, location=builtin); -} - -attribute transform occurs on ExprInh; -synthesized attribute lambdaParam::ProductionRHSElem occurs on ExprInh; - -aspect production exprInh -top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' -{ - top.transform = e.transform; - - local paramName::String = implode("_", explode(":", lhs.name)); - top.lambdaParam = - productionRHSElem( - name(paramName, builtin), '::', - typerepTypeExpr(finalType(e), location=builtin), - location=builtin); - top.bodyExprInhTransform = - exprInh( - lhs, '=', baseExpr(qName(builtin, paramName), location=builtin), ';', - location=builtin); -} - -aspect production trueConst -top::Expr ::= 'true' -{ - top.transform = booleanASTExpr(true); -} - -aspect production falseConst -top::Expr ::= 'false' -{ - top.transform = booleanASTExpr(false); -} - -aspect production and -top::Expr ::= e1::Expr '&&' e2::Expr -{ - top.transform = andASTExpr(e1.transform, e2.transform); -} - -aspect production or -top::Expr ::= e1::Expr '||' e2::Expr -{ - top.transform = orASTExpr(e1.transform, e2.transform); -} - -aspect production not -top::Expr ::= '!' e::Expr -{ - top.transform = notASTExpr(e.transform); -} - -aspect production gt -top::Expr ::= e1::Expr '>' e2::Expr -{ - top.transform = gtASTExpr(e1.transform, e2.transform); -} - -aspect production lt -top::Expr ::= e1::Expr '<' e2::Expr -{ - top.transform = ltASTExpr(e1.transform, e2.transform); -} - -aspect production gteq -top::Expr ::= e1::Expr '>=' e2::Expr -{ - top.transform = gteqASTExpr(e1.transform, e2.transform); -} - -aspect production lteq -top::Expr ::= e1::Expr '<=' e2::Expr -{ - top.transform = lteqASTExpr(e1.transform, e2.transform); -} - -aspect production eqeq -top::Expr ::= e1::Expr '==' e2::Expr -{ - top.transform = eqeqASTExpr(e1.transform, e2.transform); -} - -aspect production neq -top::Expr ::= e1::Expr '!=' e2::Expr -{ - top.transform = neqASTExpr(e1.transform, e2.transform); -} - -aspect production intConst -top::Expr ::= i::Int_t -{ - top.transform = integerASTExpr(toInteger(i.lexeme)); -} - -aspect production floatConst -top::Expr ::= f::Float_t -{ - top.transform = floatASTExpr(toFloat(f.lexeme)); -} - -aspect production plus -top::Expr ::= e1::Expr '+' e2::Expr -{ - top.transform = plusASTExpr(e1.transform, e2.transform); -} - -aspect production minus -top::Expr ::= e1::Expr '-' e2::Expr -{ - top.transform = minusASTExpr(e1.transform, e2.transform); -} - -aspect production multiply -top::Expr ::= e1::Expr '*' e2::Expr -{ - top.transform = multiplyASTExpr(e1.transform, e2.transform); -} - -aspect production divide -top::Expr ::= e1::Expr '/' e2::Expr -{ - top.transform = divideASTExpr(e1.transform, e2.transform); -} - -aspect production modulus -top::Expr ::= e1::Expr '%' e2::Expr -{ - top.transform = modulusASTExpr(e1.transform, e2.transform); -} - -aspect production neg -top::Expr ::= '-' e::Expr -{ - top.transform = negASTExpr(e.transform); -} - -aspect production stringConst -top::Expr ::= s::String_t -{ - top.transform = stringASTExpr(unescapeString(substring(1, length(s.lexeme) - 1, s.lexeme))); -} - -aspect production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.transform = appendASTExpr(e1.transform, e2.transform); -} - -aspect production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.transform = appendASTExpr(e1.transform, e2.transform); -} - -aspect production errorLength -top::Expr ::= e::Decorated Expr -{ - top.transform = lengthASTExpr(e.transform); -} - -aspect production stringLength -top::Expr ::= e::Decorated Expr -{ - top.transform = lengthASTExpr(e.transform); -} - -aspect production toIntegerFunction -top::Expr ::= 'toInteger' '(' e::Expr ')' -{ - top.transform = toIntegerASTExpr(e.transform); -} - -aspect production toBooleanFunction -top::Expr ::= 'toBoolean' '(' e::Expr ')' -{ - top.transform = toBooleanASTExpr(e.transform); -} - -aspect production toFloatFunction -top::Expr ::= 'toFloat' '(' e::Expr ')' -{ - top.transform = toFloatASTExpr(e.transform); -} - -aspect production toStringFunction -top::Expr ::= 'toString' '(' e::Expr ')' -{ - top.transform = toStringASTExpr(e.transform); -} - -aspect production newFunction -top::Expr ::= 'new' '(' e::Expr ')' -{ - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ e::$TypeExpr{typerepTypeExpr(finalType(e), location=builtin)} -> new(e)) - }), - consASTExpr(e.transform, nilASTExpr()), - nilNamedASTExpr()); -} - -aspect production terminalConstructor -top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' -{ - top.transform = terminalASTExpr(t.typerep.typeName, es.transform, el.transform); -} - -aspect production ifThenElse -top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr -{ - top.transform = ifThenElseASTExpr(e1.transform, e2.transform, e3.transform); - top.decRuleExprs = e1.decRuleExprs ++ e2.decRuleExprs ++ e3.decRuleExprs; -} - --- Extensions -aspect production emptyList -top::Expr ::= '[' ']' -{ - top.transform = nilListASTExpr(); -} - -aspect production consListOp -top::Expr ::= h::Expr '::' t::Expr -{ - top.transform = consListASTExpr(h.transform, t.transform); -} - -aspect production fullList -top::Expr ::= '[' es::Exprs ']' -{ - top.transform = listASTExpr(es.transform); -} - -aspect production listPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.transform = appendASTExpr(e1.transform, e2.transform); -} - -aspect production listLengthBouncer -top::Expr ::= e::Decorated Expr -{ - top.transform = lengthASTExpr(e.transform); -} - --- TODO: Awful hack to allow case to appear on rule RHS. --- This is interfering (should really be defined on primitive match) --- and only supports variables from the rule LHS appearing in the match expressions. -aspect production caseExpr_c -top::Expr ::= 'case' es::Exprs 'of' o::Opt_Vbar_t ml::MRuleList 'end' -{ - local decEs::Exprs = es; - decEs.downSubst = top.downSubst; - decEs.finalSubst = top.finalSubst; - decEs.frame = top.frame; - decEs.config = top.config; - decEs.compiledGrammars = top.compiledGrammars; - decEs.grammarName = top.grammarName; - decEs.env = top.env; - decEs.flowEnv = top.flowEnv; - decEs.boundVars = top.boundVars; - - top.transform = - applyASTExpr( - antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - $Expr{ - lambdap( - decEs.lambdaParams, - caseExpr_c( - 'case', decEs.lambdaParamRefs, 'of', - o, ml, 'end', - location=builtin), - location=builtin)}) - }), - decEs.transform, - nilNamedASTExpr()); -} - -aspect production rewriteExpr -top::Expr ::= 'rewriteWith' '(' s::Expr ',' e::Expr ')' -{ - -- More efficient implementation that avoids reifying e's value. - top.transform = rewriteASTExpr(s.transform, e.transform); -} - --- Modifications -aspect production letp -top::Expr ::= la::AssignExpr e::Expr -{ - top.transform = letASTExpr(la.transform, e.transform); - top.decRuleExprs = la.decRuleExprs ++ e.decRuleExprs; - - e.boundVars = top.boundVars ++ la.varBindings; -} - -attribute transform occurs on AssignExpr; -attribute varBindings occurs on AssignExpr; - -aspect production appendAssignExpr -top::AssignExpr ::= a1::AssignExpr a2::AssignExpr -{ - top.transform = appendNamedASTExprs(a1.transform, a2.transform); - top.varBindings = a1.varBindings ++ a2.varBindings; - top.decRuleExprs = a1.decRuleExprs ++ a2.decRuleExprs; -} - -aspect production assignExpr -top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr -{ - top.transform = - consNamedASTExpr(namedASTExpr(id.name, e.transform), nilNamedASTExpr()); - - -- If this is a generated pattern variable binding, figure out whether the corresponding - -- primitive pattern variable was implictly decorated. - local isDecorated::Boolean = - case e of - | lexicalLocalReference(qn, _, _) -> - fromMaybe(finalType(e).isDecorated, lookupBy(stringEq, qn.name, top.boundVars)) - | _ -> finalType(e).isDecorated - end; - top.varBindings = [pair(id.name, isDecorated)]; - top.decRuleExprs = e.decRuleExprs; -} - -aspect production matchPrimitiveReal -top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr -{ - top.decRuleExprs = e.decRuleExprs ++ pr.decRuleExprs ++ f.decRuleExprs; -} - --- TODO: Support for lambdas capturing rule LHS variables - --- Expr "collection" productions -attribute transform occurs on Exprs; -attribute lambdaParams occurs on Exprs; -synthesized attribute lambdaParamRefs::Exprs occurs on Exprs; - -aspect production exprsEmpty -top::Exprs ::= -{ - top.transform = nilASTExpr(); - top.lambdaParams = productionRHSNil(location=builtin); - top.lambdaParamRefs = exprsEmpty(location=builtin); -} -aspect production exprsSingle -top::Exprs ::= e::Expr -{ - top.transform = consASTExpr(e.transform, nilASTExpr()); - - local lambdaParamName::String = "__exprs_param_" ++ toString(genInt()); - top.lambdaParams = - productionRHSCons( - productionRHSElem( - name(lambdaParamName, builtin), '::', - typerepTypeExpr(finalType(e), location=builtin), - location=builtin), - productionRHSNil(location=builtin), - location=builtin); - top.lambdaParamRefs = - exprsSingle( - baseExpr(qName(builtin,lambdaParamName), location=builtin), - location=builtin); -} -aspect production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ - top.transform = consASTExpr(e1.transform, e2.transform); - - local lambdaParamName::String = "__exprs_param_" ++ toString(genInt()); - top.lambdaParams = - productionRHSCons( - productionRHSElem( - name(lambdaParamName, builtin), '::', - typerepTypeExpr(finalType(e1), location=builtin), - location=builtin), - e2.lambdaParams, - location=builtin); - top.lambdaParamRefs = - exprsCons( - baseExpr(qName(builtin, lambdaParamName), location=builtin), - ',', e2.lambdaParamRefs, - location=builtin); -} - -attribute transform occurs on AppExpr; - -aspect production missingAppExpr -top::AppExpr ::= '_' -{ - top.transform = missingArgASTExpr(); -} -aspect production presentAppExpr -top::AppExpr ::= e::Expr -{ - top.transform = e.transform; -} - -attribute transform occurs on AppExprs; - -aspect production snocAppExprs -top::AppExprs ::= es::AppExprs ',' e::AppExpr -{ - -- Inefficient, ugh. - top.transform = appendASTExprs(es.transform, consASTExpr(e.transform, nilASTExpr())); -} -aspect production oneAppExprs -top::AppExprs ::= e::AppExpr -{ - top.transform = consASTExpr(e.transform, nilASTExpr()); -} -aspect production emptyAppExprs -top::AppExprs ::= -{ - top.transform = nilASTExpr(); -} - -attribute transform occurs on AnnoExpr; - -aspect production annoExpr -top::AnnoExpr ::= qn::QName '=' e::AppExpr -{ - top.transform = namedASTExpr(qn.lookupAttribute.fullName, e.transform); -} - -attribute transform occurs on AnnoAppExprs; - -aspect production snocAnnoAppExprs -top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr -{ - -- Inefficient, ugh. - top.transform = appendNamedASTExprs(es.transform, consNamedASTExpr(e.transform, nilNamedASTExpr())); -} - -aspect production oneAnnoAppExprs -top::AnnoAppExprs ::= e::AnnoExpr -{ - top.transform = consNamedASTExpr(e.transform, nilNamedASTExpr()); -} - -aspect production emptyAnnoAppExprs -top::AnnoAppExprs ::= -{ - top.transform = nilNamedASTExpr(); -} - -aspect production exprRef -top::Expr ::= e::Decorated Expr -{ - top.transform = e.transform; -} \ No newline at end of file diff --git a/grammars/silver/extension/rewriting/Rewriting.sv b/grammars/silver/extension/rewriting/Rewriting.sv deleted file mode 100644 index 6a95f5e25..000000000 --- a/grammars/silver/extension/rewriting/Rewriting.sv +++ /dev/null @@ -1,335 +0,0 @@ -grammar silver:extension:rewriting; - -imports silver:rewrite; -imports silver:metatranslation; -imports silver:langutil:pp; -imports silver:util; - -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:definition:env; -imports silver:translation:java:core only finalType; -imports silver:extension:patternmatching; -imports silver:extension:reflection; -imports silver:extension:list; -imports silver:modification:primitivepattern; -imports silver:modification:lambda_fn; -imports silver:modification:let_fix; - -terminal RewriteWith_t 'rewriteWith' lexer classes {KEYWORD, RESERVED}; - -concrete production rewriteExpr -top::Expr ::= 'rewriteWith' '(' s::Expr ',' e::Expr ')' -{ - top.unparse = s"rewriteWith(${s.unparse}, ${e.unparse})"; - - local errCheckS::TypeCheck = check(s.typerep, nonterminalType("silver:rewrite:Strategy", [])); - errCheckS.finalSubst = top.finalSubst; - - local localErrors::[Message] = - s.errors ++ e.errors ++ - (if errCheckS.typeerror - then [err(top.location, "First argument to rewriteWith must be Strategy. Instead got " ++ errCheckS.leftpp)] - else []) ++ - (if null(getTypeDcl("silver:rewrite:Strategy", top.env)) - then [err(top.location, "Term rewriting requires import of silver:rewrite")] - else []); - - -- Can't use an error production here, unfourtunately, due to circular dependency issues. - top.errors := if !null(localErrors) then localErrors else forward.errors; - - -- TODO: Equation needed due to weirdness with lets auto-undecorating bindings. - -- See comments in definition of lexicalLocalReference (grammars/silver/modification/let_fix/Let.sv) - -- Actual syntax to exactly constrain the types of arbitrary expressions would be useful here. - top.typerep = nonterminalType("core:Maybe", [e.typerep]); - - s.downSubst = top.downSubst; - e.downSubst = s.upSubst; - errCheckS.downSubst = e.upSubst; - forward.downSubst = errCheckS.upSubst; - - forwards to - Silver_Expr { - case decorate $Expr{exprRef(s, location=builtin)} - with { - silver:rewrite:term = silver:reflect:reflect($Expr{exprRef(e, location=builtin)}); - }.silver:rewrite:result of - | just(a) -> - -- let needed to constrain the result type to be the same as e. - let res :: $TypeExpr{typerepTypeExpr(e.typerep, location=builtin)} = reifyUnchecked(a) - in just(res) - end - | nothing() -> nothing() - end - }; -} - --- Note that these being infix operators means that this wouldn't pass the MDA, --- despite being a Silver "extension". This could be fixed by refactoring the --- Silver Expr grammar into an "ETF" style with seperate operator nonterminals. -terminal Sequence_t '<*' precedence = 12, association = left; -- Same as * -terminal Choice_t '<+' precedence = 11, association = left; -- Same as + - -concrete production sequenceOperator -top::Expr ::= s1::Expr '<*' s2::Expr -{ - top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; - forwards to mkStrFunctionInvocation(top.location, "silver:rewrite:sequence", [s1, s2]); -} - -concrete production choiceOperator -top::Expr ::= s1::Expr '<+' s2::Expr -{ - top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; - forwards to mkStrFunctionInvocation(top.location, "silver:rewrite:choice", [s1, s2]); -} - - -terminal Traverse_t 'traverse' lexer classes {KEYWORD, RESERVED}; - -concrete production traverseProdExprAnno -top::Expr ::= 'traverse' n::QName '(' es::AppExprs ',' anns::AnnoAppExprs ')' -{ - top.unparse = s"traverse ${n.name}(${es.unparse}, ${anns.unparse})"; - - local numChildren::Integer = length(n.lookupValue.typerep.inputTypes); - local annotations::[String] = map((.argName), n.lookupValue.typerep.namedTypes); - es.appExprTypereps = repeat(nonterminalType("silver:rewrite:Strategy", []), numChildren); - es.appExprApplied = n.unparse; - anns.appExprApplied = n.unparse; - anns.funcAnnotations = - map(namedArgType(_, nonterminalType("silver:rewrite:Strategy", [])), annotations); - anns.remainingFuncAnnotations = anns.funcAnnotations; - - local localErrors::[Message] = - es.errors ++ anns.traverseErrors ++ - if null(getTypeDcl("silver:rewrite:Strategy", top.env)) - then [err(top.location, "Term rewriting requires import of silver:rewrite")] - else []; - - es.downSubst = top.downSubst; - anns.downSubst = es.upSubst; - forward.downSubst = es.downSubst; - - local transform::Strategy = - traversal(n.lookupValue.fullName, es.traverseTransform, anns.traverseTransform); - local fwrd::Expr = translate(builtin, reflect(new(transform))); - - forwards to if !null(localErrors) then errorExpr(localErrors, location=builtin) else fwrd; -} -concrete production traverseProdAnno -top::Expr ::= 'traverse' n::QName '(' anns::AnnoAppExprs ')' -{ - forwards to traverseProdExprAnno($1, n, $3, emptyAppExprs(location=$3.location), ',', anns, $5, location=top.location); -} -concrete production traverseProdExpr -top::Expr ::= 'traverse' n::QName '(' es::AppExprs ')' -{ - forwards to traverseProdExprAnno($1, n, $3, es, ',', emptyAnnoAppExprs(location=$4.location), $5, location=top.location); -} -concrete production traverseProdEmpty -top::Expr ::= 'traverse' n::QName '(' ')' -{ - forwards to traverseProdExprAnno($1, n, $3, emptyAppExprs(location=$3.location), ',', emptyAnnoAppExprs(location=$4.location), $4, location=top.location); -} - -abstract production traverseConsList -top::Expr ::= 'traverse' '(' h::AppExpr '::' t::AppExpr ')' -{ - top.unparse = s"traverse (${h.unparse} :: ${t.unparse})"; - - local transform::Strategy = consListCongruence(h.traverseTransform, t.traverseTransform); - forwards to translate(top.location, reflect(new(transform))); -} -concrete production traverseConsListFirstMissing -top::Expr ::= 'traverse' '(' h::'_' '::' t::AppExpr ')' -{ - forwards to traverseConsList($1, $2, missingAppExpr(h, location=h.location), $4, t, $6, location=top.location); -} -concrete production traverseConsListFirstPresent -top::Expr ::= 'traverse' '(' h::Expr '::' t::AppExpr ')' -{ - forwards to traverseConsList($1, $2, presentAppExpr(h, location=h.location), $4, t, $6, location=top.location); -} - -concrete production traverseNilList -top::Expr ::= 'traverse' '[' ']' -{ - top.unparse = s"traverse []"; - - local transform::Strategy = nilListCongruence(); - forwards to translate(top.location, reflect(new(transform))); -} - -concrete production traverseList -top::Expr ::= 'traverse' '[' es::AppExprs ']' -{ - top.unparse = s"traverse [${es.unparse}]"; - - local transform::Strategy = foldr(consListCongruence, nilListCongruence(), es.traverseTransform); - forwards to translate(top.location, reflect(new(transform))); -} - --- Compute our own errors on AnnoAppExprs, since we want to ignore missing annotations (like in patterns) -synthesized attribute traverseErrors::[Message] occurs on AnnoAppExprs, AnnoExpr; -synthesized attribute traverseTransform::a; -attribute traverseTransform occurs on AppExpr; -attribute traverseTransform> occurs on AnnoExpr; -attribute traverseTransform<[Strategy]> occurs on AppExprs; -attribute traverseTransform<[Pair]> occurs on AnnoAppExprs; - -aspect production missingAppExpr -top::AppExpr ::= '_' -{ - top.traverseTransform = id(); -} -aspect production presentAppExpr -top::AppExpr ::= e::Expr -{ - top.traverseTransform = antiquoteStrategy(e); -} - -aspect production snocAppExprs -top::AppExprs ::= es::AppExprs ',' e::AppExpr -{ - top.traverseTransform = es.traverseTransform ++ [e.traverseTransform]; -} -aspect production oneAppExprs -top::AppExprs ::= e::AppExpr -{ - top.traverseTransform = [e.traverseTransform]; -} -aspect production emptyAppExprs -top::AppExprs ::= -{ - top.traverseTransform = []; -} - -aspect production annoExpr -top::AnnoExpr ::= qn::QName '=' e::AppExpr -{ - top.traverseErrors = - e.errors ++ - if !extractNamedArg(qn.name, top.funcAnnotations).fst.isJust - then [err(qn.location, "Named parameter '" ++ qn.name ++ "' is not appropriate for '" ++ top.appExprApplied ++ "'")] - else []; - top.traverseTransform = pair(qn.lookupAttribute.fullName, e.traverseTransform); -} - -aspect production snocAnnoAppExprs -top::AnnoAppExprs ::= es::AnnoAppExprs ',' e::AnnoExpr -{ - top.traverseErrors = es.traverseErrors ++ e.traverseErrors; - top.traverseTransform = es.traverseTransform ++ [e.traverseTransform]; -} -aspect production oneAnnoAppExprs -top::AnnoAppExprs ::= e::AnnoExpr -{ - top.traverseErrors = e.traverseErrors; - top.traverseTransform = [e.traverseTransform]; -} -aspect production emptyAnnoAppExprs -top::AnnoAppExprs ::= -{ - top.traverseErrors = []; - top.traverseTransform = []; -} - -terminal Rule_t 'rule' lexer classes {KEYWORD, RESERVED}; - -concrete production ruleExpr -top::Expr ::= 'rule' 'on' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' -{ - top.unparse = "rule on " ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; - - -- Find the free type variables (i.e. lacking a definition) to add as skolem constants - local freeTyVars::[String] = - filter(\ tv::String -> null(getTypeDcl(tv, top.env)), makeSet(ty.lexicalTypeVariables)); - ty.env = newScopeEnv(addNewLexicalTyVars(top.grammarName, ty.location, freeTyVars), top.env); - - -- Pattern matching error checking (mostly) happens on what caseExpr forwards to, - -- so we need to decorate one of those here. - local checkExpr::Expr = - caseExpr( - [hackExprType(ty.typerep, location=builtin)], - ml.wrappedMatchRuleList, - errorExpr([], location=builtin), - ty.typerep, - location=builtin); - checkExpr.env = top.env; - checkExpr.flowEnv = top.flowEnv; - checkExpr.finalSubst = checkExpr.upSubst; -- Not top.finalSubst to avoid circularity - checkExpr.grammarName = top.grammarName; - checkExpr.frame = top.frame; - checkExpr.config = top.config; - checkExpr.compiledGrammars = top.compiledGrammars; - checkExpr.boundVars = []; - - ml.matchRulePatternSize = 1; - ml.ruleIndex = 0; - ml.decRuleExprsIn = checkExpr.decRuleExprs; - - local localErrors::[Message] = - ty.errors ++ ml.errors ++ checkExpr.errors ++ - if null(getTypeDcl("silver:rewrite:Strategy", top.env)) - then [err(top.location, "Term rewriting requires import of silver:rewrite")] - else []; - - -- Can't use an error production here, unfourtunately, due to circular dependency issues. - top.errors := if !null(localErrors) then localErrors else forward.errors; - - checkExpr.downSubst = top.downSubst; - forward.downSubst = checkExpr.upSubst; - - local finalRuleType::Type = - freshenType( - performSubstitution(ty.typerep, checkExpr.upSubst), - ty.typerep.freeVariables); - local transform::Strategy = - if ml.isPolymorphic - then requireType(antiquoteASTExpr( - Silver_Expr { - silver:rewrite:anyASTExpr( - \ $TypeExpr{typerepTypeExpr(finalRuleType, location=builtin)} -> unit()) - })) <* ml.transform - else ml.transform; - - local fwrd::Expr = translate(top.location, reflect(new(transform))); - - --forwards to unsafeTrace(fwrd, print(top.location.unparse ++ ": " ++ show(80, transform.pp) ++ "\n\n\n", unsafeIO())); - forwards to fwrd; -} - --- Hack dummy expr with a given type -abstract production hackExprType -top::Expr ::= t::Type -{ - top.typerep = t; - forwards to errorExpr([], location=builtin); -} - --- Strategy meta-translation -abstract production antiquoteASTExpr -top::ASTExpr ::= e::Expr -{ - top.pp = pp"antiquoteASTExpr {${text(e.unparse)}}"; - forwards to error("no forward"); -} - -abstract production antiquoteStrategy -top::Strategy ::= e::Expr -{ - top.pp = pp"antiquoteStrategy {${text(e.unparse)}}"; - forwards to error("no forward"); -} - -aspect production nonterminalAST -top::AST ::= prodName::String children::ASTs annotations::NamedASTs -{ - directAntiquoteProductions <- - ["silver:extension:rewriting:antiquoteASTExpr", - "silver:extension:rewriting:antiquoteStrategy"]; -} - -global builtin::Location = builtinLoc("rewriting"); diff --git a/grammars/silver/extension/silverconstruction/Syntax.sv b/grammars/silver/extension/silverconstruction/Syntax.sv deleted file mode 100644 index e7021dc71..000000000 --- a/grammars/silver/extension/silverconstruction/Syntax.sv +++ /dev/null @@ -1,130 +0,0 @@ -grammar silver:extension:silverconstruction; - -imports silver:langutil:pp; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type:syntax; -imports silver:extension:list; -imports silver:extension:patternmatching; - -concrete production quoteAGDcl -top::Expr ::= 'Silver_AGDcl' '{' ast::AGDcl '}' -{ - top.unparse = s"Silver_AGDcl {${ast.unparse}}"; - forwards to translate(top.location, reflect(new(ast))); -} - -concrete production quoteProductionStmt -top::Expr ::= 'Silver_ProductionStmt' '{' ast::ProductionStmt '}' -{ - top.unparse = s"Silver_ProductionStmt {${ast.unparse}}"; - forwards to translate(top.location, reflect(new(ast))); -} - -concrete production quoteExpr -top::Expr ::= 'Silver_Expr' '{' ast::Expr '}' -{ - top.unparse = s"Silver_Expr {${ast.unparse}}"; - forwards to translate(top.location, reflect(new(ast))); -} - -concrete production quoteExprInh -top::Expr ::= 'Silver_ExprInh' '{' ast::ExprInh '}' -{ - top.unparse = s"Silver_ExprInh {${ast.unparse}}"; - forwards to translate(top.location, reflect(new(ast))); -} - -concrete production quotePattern -top::Expr ::= 'Silver_Pattern' '{' ast::Pattern '}' -{ - top.unparse = s"Silver_Pattern {${ast.unparse}}"; - forwards to translate(top.location, reflect(new(ast))); -} - -concrete production antiquoteExpr -top::Expr ::= '$Expr' '{' e::Expr '}' -{ - top.unparse = s"$$Expr{${e.unparse}}"; - forwards to - errorExpr( - [err(top.location, "$Expr should not occur outside of quoted Silver literal")], - location=top.location); -} - -concrete production antiquoteExprInhs -top::ExprInhs ::= '$ExprInhs' '{' e::Expr '}' -{ - top.unparse = s"$$ExprInhs{${e.unparse}}"; - -- TODO: [err(top.location, "$ExprInhs should not occur outside of quoted Silver literal")] - forwards to exprInhsEmpty(location=top.location); -} - -concrete production antiquoteTypeExpr -top::TypeExpr ::= '$TypeExpr' '{' e::Expr '}' -{ - top.unparse = s"$$TypeExpr{${e.unparse}}"; - forwards to - errorTypeExpr( - [err(top.location, "$TypeExpr should not occur outside of quoted Silver literal")], - location=top.location); -} - -concrete production antiquotePattern -top::Pattern ::= '$Pattern' '{' e::Expr '}' -{ - top.unparse = s"$$Pattern{${e.unparse}}"; - forwards to - errorPattern( - [err(top.location, "$Pattern should not occur outside of quoted Silver literal")], - location=top.location); -} - -concrete production antiquoteQName -top::QName ::= '$QName' '{' e::Expr '}' -{ - top.unparse = s"$$QName{${e.unparse}}"; - forwards to - qNameError( - [err(top.location, "$QName should not occur outside of quoted Silver literal")], - location=top.location); -} - -concrete production antiquoteQNameAttrOccur -top::QNameAttrOccur ::= '$QNameAttrOccur' '{' e::Expr '}' -{ - top.unparse = s"$$QNameAttrOccur{${e.unparse}}"; - forwards to - qNameAttrOccur( - qNameError( - [err(top.location, "$QNameAttrOccur should not occur outside of quoted Silver literal")], - location=top.location), - location=top.location); -} - -concrete production antiquoteName -top::Name ::= '$Name' '{' e::Expr '}' -{ - top.unparse = s"$$Name{${e.unparse}}"; - -- TODO: [err(top.location, "$Name should not occur outside of quoted Silver literal")] - forwards to name("err", top.location); -} - -concrete production antiquote_qName -top::QName ::= '$qName' '{' e::Expr '}' -{ - top.unparse = s"$$qName{${e.unparse}}"; - forwards to - qNameError( - [err(top.location, "$qName should not occur outside of Silver_Expr")], - location=top.location); -} - -concrete production antiquote_name -top::Name ::= '$name' '{' e::Expr '}' -{ - top.unparse = s"$$name{${e.unparse}}"; - -- TODO: [err(top.location, "$Name should not occur outside of quoted Silver literal")] - forwards to name("err", top.location); -} diff --git a/grammars/silver/extension/silverconstruction/Terminals.sv b/grammars/silver/extension/silverconstruction/Terminals.sv deleted file mode 100644 index 2d8214b8c..000000000 --- a/grammars/silver/extension/silverconstruction/Terminals.sv +++ /dev/null @@ -1,20 +0,0 @@ -grammar silver:extension:silverconstruction; - -marking terminal SilverExpr_t 'Silver_Expr' lexer classes {KEYWORD, RESERVED}; -marking terminal SilverExprInh_t 'Silver_ExprInh' lexer classes {KEYWORD, RESERVED}; -marking terminal SilverPattern_t 'Silver_Pattern' lexer classes {KEYWORD, RESERVED}; -marking terminal SilverAGDcl_t 'Silver_AGDcl' lexer classes {KEYWORD, RESERVED}; -marking terminal SilverProductionStmt_t 'Silver_ProductionStmt' lexer classes {KEYWORD, RESERVED}; - -temp_imp_ide_font font_escape color(160, 32, 240) bold italic; -lexer class Antiquote font=font_escape; - -terminal AntiquoteExpr_t '$Expr' lexer classes {Antiquote}; -terminal AntiquoteExprInhs_t '$ExprInhs' lexer classes {Antiquote}; -terminal AntiquoteTypeExpr_t '$TypeExpr' lexer classes {Antiquote}; -terminal AntiquotePattern_t '$Pattern' lexer classes {Antiquote}; -terminal AntiquoteQName_t '$QName' lexer classes {Antiquote}; -terminal AntiquoteQNameAttrOccur_t '$QNameAttrOccur' lexer classes {Antiquote}; -terminal AntiquoteName_t '$Name' lexer classes {Antiquote}; -terminal Antiquote_qName_t '$qName' lexer classes {Antiquote}; -terminal Antiquote_name_t '$name' lexer classes {Antiquote}; diff --git a/grammars/silver/extension/silverconstruction/Translation.sv b/grammars/silver/extension/silverconstruction/Translation.sv deleted file mode 100644 index a82b53598..000000000 --- a/grammars/silver/extension/silverconstruction/Translation.sv +++ /dev/null @@ -1,53 +0,0 @@ -grammar silver:extension:silverconstruction; - -imports silver:reflect; -imports silver:metatranslation; - -aspect production nonterminalAST -top::AST ::= prodName::String children::ASTs annotations::NamedASTs -{ - directAntiquoteProductions <- - ["silver:extension:silverconstruction:antiquoteExpr", - "silver:extension:silverconstruction:antiquoteExprInhs", - "silver:extension:silverconstruction:antiquoteTypeExpr", - "silver:extension:silverconstruction:antiquotePattern", - "silver:extension:silverconstruction:antiquoteQName", - "silver:extension:silverconstruction:antiquoteQNameAttrOccur", - "silver:extension:silverconstruction:antiquoteName"]; - - -- "Indirect" antiquote productions - antiquoteTranslation <- - case prodName, children, annotations of - | "silver:extension:silverconstruction:antiquote_qName", - consAST(_, consAST(_, consAST(a, consAST(_, nilAST())))), - consNamedAST(namedAST("core:location", locAST), nilNamedAST()) -> - case reify(a) of - | right(e) -> - just( - mkFullFunctionInvocation( - givenLocation, - baseExpr(qName(givenLocation, "silver:metatranslation:makeQName"), location=givenLocation), - [e, locAST.translation], - [])) - | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") - end - | "silver:extension:silverconstruction:antiquote_qName", _, _ -> - error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") - | "silver:extension:silverconstruction:antiquote_name", - consAST(_, consAST(_, consAST(a, consAST(_, nilAST())))), - consNamedAST(namedAST("core:location", locAST), nilNamedAST()) -> - case reify(a) of - | right(e) -> - just( - mkFullFunctionInvocation( - givenLocation, - baseExpr(qName(givenLocation, "silver:metatranslation:makeName"), location=givenLocation), - [e, locAST.translation], - [])) - | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") - end - | "silver:extension:silverconstruction:antiquote_name", _, _ -> - error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") - | _, _, _ -> nothing() - end; -} diff --git a/grammars/silver/extension/strategyattr/ConcreteSyntax.sv b/grammars/silver/extension/strategyattr/ConcreteSyntax.sv deleted file mode 100644 index eae5b0ab4..000000000 --- a/grammars/silver/extension/strategyattr/ConcreteSyntax.sv +++ /dev/null @@ -1,241 +0,0 @@ -grammar silver:extension:strategyattr; - -inherited attribute givenGenName::String; - -concrete production partialStrategyAttributeDcl -top::AGDcl ::= 'partial' 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c ';' -{ - top.unparse = "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; - e.givenGenName = a.name; - forwards to strategyAttributeDcl(false, a, [], [], e.ast, location=top.location); -} - -concrete production totalStrategyAttributeDcl -top::AGDcl ::= 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c ';' -{ - top.unparse = "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; - e.givenGenName = a.name; - forwards to strategyAttributeDcl(true, a, [], [], e.ast, location=top.location); -} - -closed nonterminal StrategyExpr_c with location, givenGenName, unparse, ast; - -concrete productions top::StrategyExpr_c -| 'id' -{ - top.unparse = "id"; - top.ast = id(genName=top.givenGenName, location=top.location); -} -| 'fail' -{ - top.unparse = "fail"; - top.ast = fail(genName=top.givenGenName, location=top.location); -} -| s1::StrategyExpr_c '<*' s2::StrategyExpr_c -{ - top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; - top.ast = sequence(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_fst"; - s2.givenGenName = top.givenGenName ++ "_snd"; -} -| s1::StrategyExpr_c '<+' s2::StrategyExpr_c -{ - top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; - top.ast = choice(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_left"; - s2.givenGenName = top.givenGenName ++ "_right"; -} -| 'all' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"all(${s.unparse})"; - top.ast = allTraversal(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_all_arg"; -} -| 'some' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"some(${s.unparse})"; - top.ast = someTraversal(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_some_arg"; -} -| 'one' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"one(${s.unparse})"; - top.ast = oneTraversal(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_one_arg"; -} -| id::StrategyQName '(' s::StrategyExprs_c ')' -{ - top.unparse = s"${id.ast.unparse}(${s.unparse})"; - top.ast = prodTraversal(id.ast, s.ast, genName=top.givenGenName, location=top.location); - s.index = 1; - s.givenGenName = top.givenGenName ++ "_" ++ id.ast.name; -} -| 'rec' n::Name Arrow_t s::StrategyExpr_c -{ - top.unparse = s"rec ${n.name} -> (${s.unparse})"; - top.ast = recComb(n, s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName; -} -| 'rule' 'on' id::Name '::' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' -{ - top.unparse = "rule on " ++ id.unparse ++ "::" ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; - top.ast = rewriteRule(id, ty, ml, genName=top.givenGenName, location=top.location); -} -| 'rule' 'on' ty::TypeExpr 'of' Opt_Vbar_t ml::MRuleList 'end' -{ - top.unparse = "rule on " ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; - top.ast = rewriteRule(name("top", top.location), ty, ml, genName=top.givenGenName, location=top.location); -} -| id::StrategyQName -{ - top.unparse = id.ast.unparse; - top.ast = nameRef(id.ast, genName=top.givenGenName, location=top.location); -} -| '(' s::StrategyExpr_c ')' -{ - top.unparse = s"(${s.unparse})"; - top.ast = s.ast; - s.givenGenName = top.givenGenName; -} -| 'printTerm' -{ - top.unparse = s"printTerm"; - top.ast = printTerm(genName=top.givenGenName, location=top.location); -} -| 'try' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"try(${s.unparse})"; - top.ast = try(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_try_arg"; -} -| 'repeat' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"repeat(${s.unparse})"; - top.ast = repeatS(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_repeat_arg"; -} -| 'reduce' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"reduce(${s.unparse})"; - top.ast = reduce(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_reduce_arg"; -} -| 'bottomUp' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"bottomUp(${s.unparse})"; - top.ast = bottomUp(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_bottomUp_arg"; -} -| 'topDown' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"topDown(${s.unparse})"; - top.ast = topDown(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_topDown_arg"; -} -| 'downUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' -{ - top.unparse = s"downUp(${s1.unparse}, ${s2.unparse})"; - top.ast = downUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_downUp_arg1"; - s2.givenGenName = top.givenGenName ++ "_downUp_arg2"; -} -| 'allBottomUp' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"allBottomUp(${s.unparse})"; - top.ast = allBottomUp(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_allBottomUp_arg"; -} -| 'allTopDown' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"allTopDown(${s.unparse})"; - top.ast = allTopDown(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_allTopDown_arg"; -} -| 'allDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' -{ - top.unparse = s"allDownUp(${s1.unparse}, ${s2.unparse})"; - top.ast = allDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_allDownUp_arg1"; - s2.givenGenName = top.givenGenName ++ "_allDownUp_arg2"; -} -| 'someBottomUp' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"someBottomUp(${s.unparse})"; - top.ast = someBottomUp(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_someBottomUp_arg"; -} -| 'someTopDown' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"someTopDown(${s.unparse})"; - top.ast = someTopDown(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_someTopDown_arg"; -} -| 'someDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' -{ - top.unparse = s"someDownUp(${s1.unparse}, ${s2.unparse})"; - top.ast = someDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_someDownUp_arg1"; - s2.givenGenName = top.givenGenName ++ "_someDownUp_arg2"; -} -| 'onceBottomUp' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"onceBottomUp(${s.unparse})"; - top.ast = onceBottomUp(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_onceBottomUp_arg"; -} -| 'onceTopDown' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"onceTopDown(${s.unparse})"; - top.ast = onceTopDown(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_onceTopDown_arg"; -} -| 'onceDownUp' '(' s1::StrategyExpr_c ',' s2::StrategyExpr_c ')' -{ - top.unparse = s"onceDownUp(${s1.unparse}, ${s2.unparse})"; - top.ast = onceDownUp(s1.ast, s2.ast, genName=top.givenGenName, location=top.location); - s1.givenGenName = top.givenGenName ++ "_onceDownUp_arg1"; - s2.givenGenName = top.givenGenName ++ "_onceDownUp_arg2"; -} -| 'innermost' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"innermost(${s.unparse})"; - top.ast = innermost(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_innermost_arg"; -} -| 'outermost' '(' s::StrategyExpr_c ')' -{ - top.unparse = s"outermost(${s.unparse})"; - top.ast = outermost(s.ast, genName=top.givenGenName, location=top.location); - s.givenGenName = top.givenGenName ++ "_outermost_arg"; -} - -autocopy attribute index::Integer; - -nonterminal StrategyExprs_c with location, index, givenGenName, unparse, ast; -concrete productions top::StrategyExprs_c -| h::StrategyExpr_c ',' t::StrategyExprs_c -{ - top.unparse = h.unparse ++ ", " ++ t.unparse; - top.ast = consStrategyExpr(h.ast, t.ast); - h.givenGenName = top.givenGenName ++ "_arg" ++ toString(top.index); - t.givenGenName = top.givenGenName; - t.index = top.index + 1; -} -| h::StrategyExpr_c -{ - top.unparse = h.unparse; - top.ast = consStrategyExpr(h.ast, nilStrategyExpr()); - h.givenGenName = top.givenGenName ++ "_arg" ++ toString(top.index); -} -| -{ - top.unparse = ""; - top.ast = nilStrategyExpr(); -} - -nonterminal StrategyQName with location, ast; -concrete productions top::StrategyQName -(strategyQNameOne) | id::StrategyName_t -{ top.ast = qNameId(name(id.lexeme, id.location), location=top.location); } -(strategyQNameCons) | id::StrategyName_t ':' qn::StrategyQName -{ top.ast = qNameCons(name(id.lexeme, id.location), $2, qn.ast, location=top.location); } diff --git a/grammars/silver/extension/strategyattr/DclInfo.sv b/grammars/silver/extension/strategyattr/DclInfo.sv deleted file mode 100644 index 5113958e7..000000000 --- a/grammars/silver/extension/strategyattr/DclInfo.sv +++ /dev/null @@ -1,58 +0,0 @@ -grammar silver:extension:strategyattr; - -synthesized attribute isStrategy::Boolean occurs on DclInfo; -attribute isTotal occurs on DclInfo; -synthesized attribute containsErrors::Boolean occurs on DclInfo; -synthesized attribute liftedStrategyNames::[String] occurs on DclInfo; -synthesized attribute givenRecVarNameEnv::[Pair] occurs on DclInfo; -synthesized attribute givenRecVarTotalEnv::[Pair] occurs on DclInfo; -attribute partialRefs, totalRefs occurs on DclInfo; -synthesized attribute strategyExpr :: StrategyExpr occurs on DclInfo; - -aspect default production -top::DclInfo ::= -{ - top.isStrategy = false; - top.isTotal = true; - top.containsErrors = false; - top.liftedStrategyNames = []; - top.givenRecVarNameEnv = []; - top.givenRecVarTotalEnv = []; - top.partialRefs := []; - top.totalRefs := []; - top.strategyExpr = error("Internal compiler error: must be defined for all strategy attribute declarations"); -} - -abstract production strategyDcl -top::DclInfo ::= - sg::String sl::Location fn::String isTotal::Boolean tyVar::TyVar - containsErrors::Boolean liftedStrategyNames::[String] givenRecVarNameEnv::[Pair] givenRecVarTotalEnv::[Pair] partialRefs::[String] totalRefs::[String] - e::StrategyExpr -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = - if isTotal - then varType(tyVar) - else nonterminalType("core:Maybe", [varType(tyVar)]); - top.dclBoundVars = [tyVar]; - top.isSynthesized = true; - top.isStrategy = true; - - top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); - top.attrDefDispatcher = synthesizedAttributeDef(_, _, _, location=_); -- Allow normal syn equations - top.attributionDispatcher = strategyAttributionDcl(_, _, _, _, location=_); - top.propagateDispatcher = propagateStrategy(_, location=_); - - top.isTotal = isTotal; - top.containsErrors = containsErrors; - top.liftedStrategyNames = liftedStrategyNames; - top.givenRecVarNameEnv = givenRecVarNameEnv; - top.givenRecVarTotalEnv = givenRecVarTotalEnv; - top.partialRefs := partialRefs; - top.totalRefs := totalRefs; - top.strategyExpr = e; -} diff --git a/grammars/silver/extension/strategyattr/Project.sv b/grammars/silver/extension/strategyattr/Project.sv deleted file mode 100644 index 49a1ffc1a..000000000 --- a/grammars/silver/extension/strategyattr/Project.sv +++ /dev/null @@ -1,16 +0,0 @@ -grammar silver:extension:strategyattr; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:extension:autoattr; -imports silver:extension:patternmatching; -imports silver:extension:list; ---imports silver:extension:rewriting; -imports silver:extension:silverconstruction; -imports silver:modification:let_fix; -imports silver:modification:lambda_fn; - -exports silver:extension:strategyattr:convenience; -exports silver:extension:strategyattr:construction; diff --git a/grammars/silver/extension/strategyattr/Strategy.sv b/grammars/silver/extension/strategyattr/Strategy.sv deleted file mode 100644 index 495953e8b..000000000 --- a/grammars/silver/extension/strategyattr/Strategy.sv +++ /dev/null @@ -1,179 +0,0 @@ -grammar silver:extension:strategyattr; - -import silver:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; -import silver:driver:util; - -abstract production strategyAttributeDcl -top::AGDcl ::= isTotal::Boolean a::Name recVarNameEnv::[Pair] recVarTotalEnv::[Pair] e::StrategyExpr -{ - top.unparse = (if isTotal then "" else "partial ") ++ "strategy attribute " ++ a.unparse ++ "=" ++ e.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - -- Define these directly to avoid circular dependencies, - -- since the forward contributes to the env. - propagate errors, moduleNames; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; - top.errors <- - if null(getValueDcl("core:monad:bindMaybe", top.env)) - then [err(top.location, "Strategy attributes require import of core:monad")] - else []; - top.errors <- - if isTotal && !e.isTotal - -- Not an error since we can still translate this, but the translation may raise run-time errors in case of failure - then [wrn(e.location, s"Implementation of total strategy ${a.name} is not total")] - else []; - - -- Frame doesn't really matter, since we will re-check any expressions occuring in e when propagated. - -- Need all this to construct a bogus frame... - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - local myFlowGraph :: ProductionGraph = - constructAnonymousGraph(e.flowDefs, top.env, myProds, myFlow); - e.frame = globalExprContext(myFlowGraph); - - e.recVarNameEnv = recVarNameEnv; - e.recVarTotalEnv = recVarTotalEnv; - e.outerAttr = just(a.name); - - local fwrd::AGDcl = - foldr( - appendAGDcl(_, _, location=top.location), - defsAGDcl( - [attrDef( - defaultEnvItem( - strategyDcl( - top.grammarName, a.location, fName, isTotal, freshTyVar(), - !null(top.errors), map(fst, e.liftedStrategies), recVarNameEnv, recVarTotalEnv, e.partialRefs, e.totalRefs, e)))], - location=top.location), - map( - \ d::Pair -> - strategyAttributeDcl( - d.snd.isTotal, name(d.fst, top.location), d.snd.recVarNameEnv, d.snd.recVarTotalEnv, new(d.snd), - location=top.location), - decorate e with { - env = emptyEnv(); -- Forward (and thus lifting) cannot depend on top.env to avoid circular dependency - config = e.config; grammarName = e.grammarName; recVarNameEnv = recVarNameEnv; recVarTotalEnv = recVarTotalEnv; outerAttr = e.outerAttr; - }.liftedStrategies)); - - -- Uncomment for debugging - --forwards to unsafeTrace(fwrd, print(a.name ++ " = " ++ e.unparse ++ "; lifted " ++ implode(", ", map(fst, e.liftedStrategies)) ++ "\n\n", unsafeIO())); - - -- Flow errors here due to exceeding the allowable host forward flow type. - -- I'm not actually sure where we depend on flowEnv, config or compiledGrammars. - -- This could be fixed by seeding the host flow type or tracking down those dependencies and substituting dummy values. - forwards to fwrd; -} - -abstract production strategyAttributionDcl -top::AGDcl ::= at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - production attribute localErrors::[Message] with ++; - localErrors := - attl.errors ++ attl.errorsTyVars ++ nt.lookupType.errors ++ nttl.errors ++ nttl.errorsTyVars; - localErrors <- - if length(attl.types) > 0 - then [err(attl.location, "Explicit type arguments are not allowed for strategy attributes")] - else []; - - -- Technically we could do this check on the propagate, but it seems clearer to raise it here - localErrors <- - flatMap( - \ totalAttr::String -> - if null(getOccursDcl(totalAttr, nt.lookupType.fullName, top.env)) - then [err(top.location, s"Total strategy attribute ${totalAttr} referenced by ${at.name} does not occur on ${nt.name}")] - else [], - nubBy(stringEq, at.lookupAttribute.dcl.totalRefs)); - - -- TODO: Check that the type parameters of any rules of type nt match nttl - - top.errors := if !null(localErrors) then localErrors else forward.errors; - - forwards to - foldr( - appendAGDcl(_, _, location=top.location), - defaultAttributionDcl( - at, - botlSome( - '<', - typeListSingle( - nominalTypeExpr(nt.qNameType, nttl, location=top.location), - location=top.location), - '>', location=top.location), - nt, nttl, - location=top.location), - map( - \ n::String -> - attributionDcl( - 'attribute', qName(top.location, n), attl, 'occurs', 'on', nt, nttl, ';', - location=top.location), - at.lookupAttribute.dcl.liftedStrategyNames)); -} - -{-- - - Propagate a strategy attribute on the enclosing production - - @param attr The name of the attribute to propagate - -} -abstract production propagateStrategy -top::ProductionStmt ::= attr::Decorated QName -{ - top.unparse = s"propagate ${attr.unparse}"; - - production isTotal::Boolean = attr.lookupAttribute.dcl.isTotal; - production e::StrategyExpr = attr.lookupAttribute.dcl.strategyExpr; - e.grammarName = top.grammarName; - e.config = top.config; - e.frame = top.frame; - e.env = top.env; - e.recVarNameEnv = attr.lookupAttribute.dcl.givenRecVarNameEnv; - e.recVarTotalEnv = attr.lookupAttribute.dcl.givenRecVarTotalEnv; - e.outerAttr = just(attr.lookupAttribute.fullName); - e.inlinedStrategies = [attr.lookupAttribute.fullName]; -- Don't unfold the top-level strategy within itself - - production e2::StrategyExpr = e.optimize; - e2.grammarName = e.grammarName; - e2.config = e.config; - e2.frame = e.frame; - e2.env = e.env; - e2.recVarNameEnv = e.recVarNameEnv; - e2.recVarTotalEnv = e.recVarTotalEnv; - e2.outerAttr = e.outerAttr; - e2.inlinedStrategies = e.inlinedStrategies; - - -- Can't do this with forwarding to avoid circular dependency of - -- forward -> dcl.containsErrors -> dcl.flowEnv -> forward.flowDefs - top.errors := - if - -- Check for errors in this or inlined strategy expressions that would be reported on the attribute definition - attr.lookupAttribute.dcl.containsErrors || - any(map((.containsErrors), flatMap(getAttrDcl(_, top.env), attr.lookupAttribute.dcl.partialRefs))) || - -- Check for total strategy ref occurs errors that would already be reported on the occurence - (!null(getOccursDcl(attr.lookupAttribute.fullName, top.frame.signature.outputElement.typerep.typeName, top.env)) && - any(map(null, map(getOccursDcl(_, top.frame.signature.outputElement.typerep.typeName, top.env), attr.lookupAttribute.dcl.totalRefs)))) - then [] - else forward.errors; - - local fwrd::ProductionStmt = - foldr( - productionStmtAppend(_, _, location=top.location), - attributeDef( - concreteDefLHS(qName(top.location, top.frame.signature.outputElement.elementName), location=top.location), - '.', - qNameAttrOccur(new(attr), location=top.location), - '=', - if isTotal then e2.totalTranslation else e2.partialTranslation, - ';', - location=top.location), - map( - \ n::String -> propagateOneAttr(qName(top.location, n), location=top.location), - attr.lookupAttribute.dcl.liftedStrategyNames)); - - -- Uncomment for debugging - --forwards to unsafeTrace(fwrd, print(attr.name ++ " on " ++ top.frame.fullName ++ " = " ++ (if isTotal then e2.totalTranslation else e2.partialTranslation).unparse ++ ";\n\n", unsafeIO())); - forwards to fwrd; -} diff --git a/grammars/silver/extension/strategyattr/StrategyExpr.sv b/grammars/silver/extension/strategyattr/StrategyExpr.sv deleted file mode 100644 index bba3109c7..000000000 --- a/grammars/silver/extension/strategyattr/StrategyExpr.sv +++ /dev/null @@ -1,994 +0,0 @@ -grammar silver:extension:strategyattr; - -import silver:metatranslation; -import core:monad; - -annotation genName::String; -- Used to generate the names of lifted strategy attributes - -autocopy attribute recVarNameEnv::[Pair]; -- name, (isTotal, genName) -autocopy attribute recVarTotalEnv::[Pair]; -- name, (isTotal, genName) -inherited attribute outerAttr::Maybe; -autocopy attribute inlinedStrategies::[String]; -monoid attribute liftedStrategies::[Pair] with [], ++; -synthesized attribute attrRefName::Maybe; -synthesized attribute isId::Boolean; -synthesized attribute isTotal::Boolean; -inherited attribute givenInputElements::[NamedSignatureElement]; -synthesized attribute attrRefNames::[Maybe]; -monoid attribute containsFail::Boolean with false, ||; -monoid attribute allId::Boolean with true, &&; -monoid attribute freeRecVars::[String] with [], ++; -monoid attribute partialRefs::[String] with [], ++; -monoid attribute totalRefs::[String] with [], ++; -monoid attribute matchesFrame::Boolean with false, ||; - -synthesized attribute partialTranslation::Expr; -- Maybe on a -synthesized attribute totalTranslation::Expr; -- a on a, can raise a runtime error if demanded on partial strategy expression - --- Nonterminal-independent algebraic simplifications --- Theoretically these could be applied to the strategy before lifting/propagation, --- but probably not much of an improvement. -partial strategy attribute genericStep = - rule on top::StrategyExpr of - | sequence(fail(), _) -> fail(location=top.location, genName=top.genName) - | sequence(_, fail()) -> fail(location=top.location, genName=top.genName) - | sequence(id(), s) -> s - | sequence(s, id()) -> s - | choice(fail(), s) -> s - | choice(s, fail()) -> s - | choice(s, _) when s.isTotal -> s - | allTraversal(id()) -> id(location=top.location, genName=top.genName) - | someTraversal(fail()) -> fail(location=top.location, genName=top.genName) - | oneTraversal(fail()) -> fail(location=top.location, genName=top.genName) - | prodTraversal(_, ss) when ss.containsFail -> fail(location=top.location, genName=top.genName) - | prodTraversal(_, ss) when ss.allId -> id(location=top.location, genName=top.genName) - | recComb(n, s) when !containsBy(stringEq, n.name, s.freeRecVars) -> s - | inlined(_, fail()) -> fail(location=top.location, genName=top.genName) - end; --- Nonterminal-dependent, production-independent optimizations -partial strategy attribute ntStep = - rule on top::StrategyExpr of - -- Only inline references to partial strategies, as inlining total - -- strategies would not permit any additional simplification. - | partialRef(n) when - n.matchesFrame && n.attrDcl.isStrategy && - !containsBy(stringEq, n.attrDcl.fullName, top.inlinedStrategies) && - null(n.attrDcl.givenRecVarNameEnv) -> - inlined(n, n.attrDcl.strategyExpr, location=top.location, genName=top.genName) - | partialRef(n) when !n.matchesFrame -> fail(location=top.location, genName=top.genName) - | inlined(n, _) when !n.matchesFrame -> fail(location=top.location, genName=top.genName) - | inlined(n, id()) when n.matchesFrame -> id(location=top.location, genName=top.genName) - | inlined(n1, totalRef(n2)) when n1.matchesFrame -> totalRef(n2, location=top.location, genName=top.genName) - end; --- Production-dependent optimizations -partial strategy attribute prodStep = - rule on top::StrategyExpr of - | allTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> id(location=top.location, genName=top.genName) - | someTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> fail(location=top.location, genName=top.genName) - | oneTraversal(s) when !attrMatchesChild(top.env, fromMaybe(s.genName, s.attrRefName), top.frame) -> fail(location=top.location, genName=top.genName) - | prodTraversal(p, s) when p.lookupValue.fullName != top.frame.fullName -> fail(location=top.location, genName=top.genName) - | rewriteRule(_, _, ml) when !ml.matchesFrame -> fail(location=top.location, genName=top.genName) - end <+ - rewriteRule( - id, id, - onceBottomUp( - rule on top::MRuleList of - | mRuleList_cons(h, _, t) when !h.matchesFrame -> t - | mRuleList_cons(h, _, mRuleList_one(t)) when !t.matchesFrame -> mRuleList_one(h, location=top.location) - end)); -attribute prodStep occurs on MRuleList; - -strategy attribute simplify = innermost(genericStep <+ ntStep); -strategy attribute optimize = - (sequence(optimize, simplify) <+ - choice(optimize, optimize) <+ - allTraversal(simplify) <+ - someTraversal(simplify) <+ - oneTraversal(simplify) <+ - prodTraversal(id, simplify) <+ - recComb(id, optimize) <+ - inlined(id, optimize) <+ - id) <* - try((genericStep <+ ntStep <+ prodStep) <* optimize); - -nonterminal StrategyExpr with - config, grammarName, env, location, unparse, errors, frame, compiledGrammars, flowEnv, flowDefs, -- Normal expression stuff - genName, outerAttr, recVarNameEnv, recVarTotalEnv, liftedStrategies, attrRefName, isId, isTotal, freeRecVars, partialRefs, totalRefs, -- Frame-independent attrs - partialTranslation, totalTranslation, matchesFrame, -- Frame-dependent attrs - inlinedStrategies, genericStep, ntStep, prodStep, simplify, optimize; -- Optimization stuff - -nonterminal StrategyExprs with - config, grammarName, env, unparse, errors, frame, compiledGrammars, flowEnv, flowDefs, -- Normal expression stuff - recVarNameEnv, recVarTotalEnv, givenInputElements, liftedStrategies, attrRefNames, containsFail, allId, freeRecVars, partialRefs, totalRefs, -- Frame-independent attrs - inlinedStrategies, simplify; -- Optimization stuff - -flowtype StrategyExpr = - decorate {env, grammarName, config, recVarNameEnv, recVarTotalEnv, outerAttr}, -- NOT frame - -- Normal expression stuff - unparse {}, errors {decorate, frame, compiledGrammars, flowEnv}, flowDefs {decorate, frame, compiledGrammars, flowEnv}, - -- Frame-independent attrs - liftedStrategies {decorate}, attrRefName {decorate}, isId {decorate}, isTotal {decorate}, freeRecVars {decorate}, partialRefs {decorate}, totalRefs {decorate}, - -- Frame-dependent attrs - partialTranslation {decorate, frame}, totalTranslation {decorate, frame}, matchesFrame {decorate, frame}; - -flowtype StrategyExprs = - decorate {env, grammarName, config, recVarNameEnv, recVarTotalEnv}, -- NOT frame - -- Normal expression stuff - unparse {}, errors {decorate, frame, givenInputElements, compiledGrammars, flowEnv}, flowDefs {decorate, frame, compiledGrammars, flowEnv}, - -- Frame-independent attrs - liftedStrategies {decorate}, attrRefNames {decorate, givenInputElements}, - containsFail {decorate}, allId {decorate}, freeRecVars {decorate}, partialRefs {decorate}, totalRefs {decorate}; - -propagate errors on StrategyExpr, StrategyExprs excluding partialRef, totalRef; -propagate flowDefs on StrategyExpr, StrategyExprs; -propagate containsFail, allId on StrategyExprs; -propagate freeRecVars on StrategyExpr, StrategyExprs excluding recComb; -propagate partialRefs, totalRefs on StrategyExpr, StrategyExprs; -propagate simplify on StrategyExprs; -propagate prodStep on MRuleList; -propagate genericStep, ntStep, prodStep, simplify, optimize on StrategyExpr; - --- Convert an expression of type a to Maybe -function asPartial -Expr ::= e::Expr -{ return Silver_Expr { core:just($Expr{e}) }; } - --- Convert an expression of type Maybe to a -function asTotal -Expr ::= t::Type e::Expr -{ - return - Silver_Expr { - let res::$TypeExpr{typerepTypeExpr(t, location=e.location)} = - core:error("Total result demanded when partial strategy failed") - in core:fromMaybe(res, $Expr{e}) - end - }; -} - -aspect default production -top::StrategyExpr ::= -{ - -- At least 1 of these should be defined for every production: - top.partialTranslation = asPartial(top.totalTranslation); - top.totalTranslation = asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); - - top.attrRefName = nothing(); - top.matchesFrame := true; -- Consulted only when attrRefName is just(...) - top.isId = false; - top.isTotal = false; -} - --- Basic combinators -abstract production id -top::StrategyExpr ::= -{ - top.unparse = "id"; - propagate liftedStrategies; - top.isId = true; - top.isTotal = true; - top.totalTranslation = Silver_Expr { $name{top.frame.signature.outputElement.elementName} }; -} - -abstract production fail -top::StrategyExpr ::= -{ - top.unparse = "fail"; - propagate liftedStrategies; - top.partialTranslation = Silver_Expr { core:nothing() }; -} - -abstract production sequence -top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr -{ - top.unparse = s"(${s1.unparse} <* ${s2.unparse})"; - - local s2Name::String = fromMaybe(top.genName ++ "_snd", s2.attrRefName); - local s2Total::Boolean = attrIsTotal(top.env, s2Name); -- Can differ from s2.isTotal because we lift without env - top.liftedStrategies := - s1.liftedStrategies ++ - if s2.attrRefName.isJust - then [] - else [pair(s2Name, s2)]; - top.isTotal = s1.isTotal && s2.isTotal; - - s1.outerAttr = nothing(); - s2.outerAttr = nothing(); - - -- Equations for all inh attributes on the nt that we know about. - -- This is safe because the MWDA requires that all inh dependencies of a syn attribute - -- be exported by the syn occurence anyway. - -- TODO - future optimization potential: this is where common sub-trees shared between - -- the incoming tree and the result of s1 get re-decorated. - local allInhs::ExprInhs = - foldr( - exprInhsCons(_, _, location=top.location), - exprInhsEmpty(location=top.location), - map( - \ a::DclInfo -> - Silver_ExprInh { - $name{a.fullName} = $name{top.frame.signature.outputElement.elementName}.$name{a.fullName}; - }, - filter( - (.isInherited), - flatMap( - getAttrDcl(_, top.env), - map((.attrOccurring), getAttrsOn(top.frame.lhsNtName, top.env)))))); - top.partialTranslation = - -- Optimizations when one or both of these is total, in this case a - -- monadic bind may not be required. - case s1.isTotal, s2Total of - | true, true -> - Silver_Expr { - core:just(decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name}) - } - | true, false -> - Silver_Expr { - decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name} - } - | false, true -> - Silver_Expr { - core:mapMaybe( - \ res::$TypeExpr{typerepTypeExpr(top.frame.signature.outputElement.typerep, location=top.location)} -> - decorate res with { $ExprInhs{allInhs} }.$name{s2Name}, - $Expr{s1.partialTranslation}) - } - | false, false -> - Silver_Expr { - core:monad:bindMaybe( - $Expr{s1.partialTranslation}, - \ res::$TypeExpr{typerepTypeExpr(top.frame.signature.outputElement.typerep, location=top.location)} -> - decorate res with { $ExprInhs{allInhs} }.$name{s2Name}) - } - end; - local totalTrans::Expr = - Silver_Expr { - decorate $Expr{s1.totalTranslation} with { $ExprInhs{allInhs} }.$name{s2Name} - }; - top.totalTranslation = if s2Total then totalTrans else asTotal(top.frame.signature.outputElement.typerep, totalTrans); -} - -abstract production choice -top::StrategyExpr ::= s1::StrategyExpr s2::StrategyExpr -{ - top.unparse = s"(${s1.unparse} <+ ${s2.unparse})"; - propagate liftedStrategies; - top.isTotal = s1.isTotal || s2.isTotal; - - s1.outerAttr = nothing(); - s2.outerAttr = nothing(); - - top.partialTranslation = - Silver_Expr { - core:orElse($Expr{s1.partialTranslation}, $Expr{s2.partialTranslation}) - }; - top.totalTranslation = - if s1.isTotal - then s1.totalTranslation - else - Silver_Expr { - core:fromMaybe($Expr{s2.totalTranslation}, $Expr{s1.partialTranslation}) - }; -} - --- Traversals -abstract production allTraversal -top::StrategyExpr ::= s::StrategyExpr -{ - top.unparse = s"all(${s.unparse})"; - - local sName::String = fromMaybe(top.genName ++ "_all_arg", s.attrRefName); - local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env - top.liftedStrategies := - if s.attrRefName.isJust - then [] - else [pair(sName, s)]; - top.isTotal = s.isTotal; - - s.outerAttr = nothing(); - - local sBaseName::String = last(explode(":", sName)); - -- pair(child name, attr occurs on child) - local childAccesses::[Pair] = - map( - \ e::NamedSignatureElement -> - pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), - top.frame.signature.inputElements); - top.partialTranslation = - if sTotal - then asPartial(top.totalTranslation) - else - {- Translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - case a.s, c.s of - | just(a_s), just(c_s) -> just(prod(a_s, b, c_s)) - | _, _ -> nothing() - end - Could also be implemented as chained monadic binds. Maybe more efficient this way? -} - caseExpr( - flatMap( - \ a::Pair -> - if a.snd then [Silver_Expr { $name{a.fst}.$name{sName} }] else [], - childAccesses), - [matchRule( - flatMap( - \ a::Pair -> - if a.snd - then - [decorate Silver_Pattern { core:just($name{a.fst ++ "_" ++ sBaseName}) } - with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }] - else [], - childAccesses), - nothing(), - Silver_Expr { - core:just( - $Expr{ - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> - if a.snd - then Silver_Expr { $name{a.fst ++ "_" ++ sBaseName} } - else Silver_Expr { $name{a.fst} }, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements))}) - }, - location=top.location)], - Silver_Expr { core:nothing() }, - nonterminalType("core:Maybe", [top.frame.signature.outputElement.typerep]), - location=top.location); - top.totalTranslation = - if sTotal - then - {- When s is total, optimized translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - prod(a.s, b, c.s) -} - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> - if a.snd - then Silver_Expr { $name{a.fst}.$name{sName} } - else Silver_Expr { $name{a.fst} }, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements)) - else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); -} - -abstract production someTraversal -top::StrategyExpr ::= s::StrategyExpr -{ - top.unparse = s"some(${s.unparse})"; - - local sName::String = fromMaybe(top.genName ++ "_some_arg", s.attrRefName); - local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env - top.liftedStrategies := - if s.attrRefName.isJust - then [] - else [pair(sName, s)]; - - s.outerAttr = nothing(); - - -- pair(child name, attr occurs on child) - local childAccesses::[Pair] = - map( - \ e::NamedSignatureElement -> - pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), - top.frame.signature.inputElements); - local matchingChildren::[String] = map(fst, filter(snd, childAccesses)); - top.partialTranslation = - if sTotal - then - if !null(matchingChildren) - then asPartial(top.totalTranslation) - else Silver_Expr { core:nothing() } - else - {- Translation of some(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - if a.s.isJust || c.s.isJust - then just(prod(fromMaybe(a, a.s), b, fromMaybe(c, c.s))) - else nothing() - Not sure of a clean way to do this with monads -} - Silver_Expr { - if $Expr{ - foldr( - or(_, '||', _, location=top.location), - falseConst('false', location=top.location), - map( - \ a::String -> Silver_Expr { $name{a}.$name{sName}.isJust }, - matchingChildren))} - then - core:just( - $Expr{ - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> - if a.snd - then Silver_Expr { core:fromMaybe($name{a.fst}, $name{a.fst}.$name{sName}) } - else Silver_Expr { $name{a.fst} }, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements))}) - else core:nothing() - }; - top.totalTranslation = - if sTotal && !null(matchingChildren) - then - {- When s is total, optimized translation of all(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - prod(a.s, b, c.s) -} - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> - if a.snd - then Silver_Expr { $name{a.fst}.$name{sName} } - else Silver_Expr { $name{a.fst} }, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements)) - else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); -} -abstract production oneTraversal -top::StrategyExpr ::= s::StrategyExpr -{ - top.unparse = s"one(${s.unparse})"; - - local sName::String = fromMaybe(top.genName ++ "_one_arg", s.attrRefName); - local sTotal::Boolean = attrIsTotal(top.env, sName); -- Can differ from s.isTotal because we lift without env - top.liftedStrategies := - if s.attrRefName.isJust - then [] - else [pair(sName, s)]; - - s.outerAttr = nothing(); - - local sBaseName::String = last(explode(":", sName)); - -- pair(child name, attr occurs on child) - local childAccesses::[Pair] = - map( - \ e::NamedSignatureElement -> - pair(e.elementName, attrMatchesFrame(top.env, sName, e.typerep)), - top.frame.signature.inputElements); - local matchingChildren::[String] = map(fst, filter(snd, childAccesses)); - top.partialTranslation = - if sTotal - then - if !null(matchingChildren) - then asPartial(top.totalTranslation) - else Silver_Expr { core:nothing() } - else - {- Translation of one(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - case a.s, c.s of - | just(a_s), _ -> just(prod(a_s, b, c)) - | _, just(c_s) -> just(prod(a, b, c_s)) - | _, _ -> nothing() - end - Could also be implemented as - orElse( - bindMaybe(a.s, \ a_s::Foo -> returnMaybe(prod(a_s, b, c))), - bindMaybe(c.s, \ c_s::Bar -> returnMaybe(prod(a, b, c_s))) -} - caseExpr( - map( - \ a::String -> Silver_Expr { $name{a}.$name{sName} }, - matchingChildren), - map( - \ i::Integer -> - let childI::String = head(drop(i, matchingChildren)) - in let childIndex::Integer = positionOf(stringEq, childI, map(fst, childAccesses)) - in - matchRule( - map( - \ p::Pattern -> decorate p with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }, - repeat(wildcPattern('_', location=top.location), i) ++ - Silver_Pattern { core:just($name{childI ++ "_" ++ sBaseName}) } :: - repeat(wildcPattern('_', location=top.location), length(matchingChildren) - (i + 1))), - nothing(), - Silver_Expr { - core:just( - $Expr{ - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> Silver_Expr { $name{a.fst} }, - take(childIndex, childAccesses)) ++ - Silver_Expr { $name{childI ++ "_" ++ sBaseName} } :: - map( - \ a::Pair -> Silver_Expr { $name{a.fst} }, - drop(childIndex + 1, childAccesses)), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements))}) - }, - location=top.location) - end end, - range(0, length(matchingChildren))), - Silver_Expr { core:nothing() }, - nonterminalType("core:Maybe", [top.frame.signature.outputElement.typerep]), - location=top.location); - top.totalTranslation = - if sTotal && !null(matchingChildren) - then - {- When s is total, optimized translation of one(s) for prod::(Foo ::= a::Foo b::Integer c::Bar): - prod(a.s, b, c) -} - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair -> - if a.fst == head(matchingChildren) - then Silver_Expr { $name{a.fst}.$name{sName} } - else Silver_Expr { $name{a.fst} }, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements)) - else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); -} - -abstract production prodTraversal -top::StrategyExpr ::= prod::QName s::StrategyExprs -{ - top.unparse = s"${prod.unparse}(${s.unparse})"; - - top.errors <- prod.lookupValue.errors; - - local numParams::Integer = length(s.givenInputElements); - local numArgs::Integer = length(s.attrRefNames); - top.errors <- - if prod.lookupValue.found && numArgs != numParams - then [err(top.location, s"Wrong number of arguments to ${prod.name}: expected ${toString(numParams)}, got ${toString(numArgs)}")] - else []; - - propagate liftedStrategies; - - s.givenInputElements = - if prod.lookupValue.found - then prod.lookupValue.dcl.namedSignature.inputElements - else []; - - -- pair(child name, if attr occurs on child then just(attr name) else nothing()) - local childAccesses::[Pair>] = - zipWith(pair, top.frame.signature.inputNames, s.attrRefNames); - top.partialTranslation = -- This is never total - if prod.lookupValue.fullName == top.frame.fullName - then - {- Translation of prod(s1, s2, s3, s4) for prod::(Foo ::= a::Foo b::Integer c::Bar d::Baz) - where s4 is total: - case a.s1, c.s3 of - | just(a_s1), just(c_s3) -> just(prod(a_s1, b, c_s3, d.s4)) - | _, _ -> nothing() - end - Could also be implemented as chained monadic binds. Maybe more efficient this way? -} - caseExpr( - flatMap( - \ a::Pair> -> - case a.snd of - | just(attr) when !attrIsTotal(top.env, attr) -> [Silver_Expr { $name{a.fst}.$name{attr} }] - | _ -> [] - end, - childAccesses), - [matchRule( - flatMap( - \ a::Pair> -> - case a.snd of - | just(attr) when !attrIsTotal(top.env, attr) -> - [decorate Silver_Pattern { core:just($name{a.fst ++ "_" ++ last(explode(":", attr))}) } - with { config = top.config; env = top.env; frame = top.frame; patternVarEnv = []; }] - | _ -> [] - end, - childAccesses), - nothing(), - Silver_Expr { - core:just( - $Expr{ - mkFullFunctionInvocation( - top.location, - baseExpr(qName(top.location, top.frame.fullName), location=top.location), - map( - \ a::Pair> -> - case a.snd of - | just(attr) when attrIsTotal(top.env, attr) -> Silver_Expr { $name{a.fst}.$name{attr} } - | just(attr) -> Silver_Expr { $name{a.fst ++ "_" ++ last(explode(":", attr))} } - | nothing() -> Silver_Expr { $name{a.fst} } - end, - childAccesses), - map( - makeAnnoArg(top.location, top.frame.signature.outputElement.elementName, _), - top.frame.signature.namedInputElements))}) - }, - location=top.location)], - Silver_Expr { core:nothing() }, - nonterminalType("core:Maybe", [top.frame.signature.outputElement.typerep]), - location=top.location) - else Silver_Expr { core:nothing() }; -} - -abstract production consStrategyExpr -top::StrategyExprs ::= h::StrategyExpr t::StrategyExprs -{ - top.unparse = s"${h.unparse}, ${t.unparse}"; - - top.liftedStrategies := - -- Slight hack: when h is id (common case for prod traversals), there is no need for a new attribute. - -- However this can't be avoided during the optimization phase, which happens after lifting. - -- So, just don't lift the strategy, and we won't find the occurence of the non-existant attribute - -- during translation - which means we will treat it as id anyway! - (if h.attrRefName.isJust || h.isId - then [] - else [pair(h.genName, h)]) ++ - t.liftedStrategies; - - local hType::Type = head(top.givenInputElements).typerep; - local attr::String = fromMaybe(h.genName, h.attrRefName); - local attrMatch::Boolean = attrMatchesFrame(top.env, attr, hType); - top.attrRefNames = - (if !null(top.givenInputElements) && attrMatch && !h.isId - then just(attr) - else nothing()) :: t.attrRefNames; - top.errors <- - if !null(top.givenInputElements) && !attrMatch && !h.isId - then [wrn(h.location, s"This (non-identity) strategy attribute does not occur on ${prettyType(hType)} and will be treated as identity")] - else []; - - top.containsFail <- case h of fail() -> true | _ -> false end; - top.allId <- case h of id() -> true | _ -> false end; - - h.outerAttr = nothing(); - t.givenInputElements = - if !null(top.givenInputElements) then tail(top.givenInputElements) else []; -} - -abstract production nilStrategyExpr -top::StrategyExprs ::= -{ - top.unparse = ""; - top.liftedStrategies := []; - top.attrRefNames = []; -} - --- Recursive strategies -abstract production recComb -top::StrategyExpr ::= n::Name s::StrategyExpr -{ - top.unparse = s"rec ${n.name} -> (${s.unparse})"; - - local sName::String = fromMaybe(top.genName ++ "_rec_body", top.outerAttr); - top.liftedStrategies := - if top.outerAttr.isJust - then s.liftedStrategies - else [pair(sName, s)]; - top.freeRecVars := removeBy(stringEq, n.name, s.freeRecVars); - top.isTotal = - decorate s with { - recVarTotalEnv = pair(n.name, true) :: s.recVarTotalEnv; - env = s.env; config = s.config; grammarName = s.grammarName; recVarNameEnv = s.recVarNameEnv; outerAttr = s.outerAttr; - }.isTotal; - - s.recVarNameEnv = pair(n.name, sName) :: top.recVarNameEnv; - s.recVarTotalEnv = pair(n.name, top.isTotal) :: top.recVarTotalEnv; - s.outerAttr = top.outerAttr; - - local sTotal::Boolean = attrIsTotal(top.env, sName); - top.partialTranslation = - if top.outerAttr.isJust - then s.partialTranslation - else if sTotal - then asPartial(top.totalTranslation) - else Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$name{sName} }; - top.totalTranslation = - if top.outerAttr.isJust - then s.totalTranslation - else if sTotal - then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$name{sName} } - else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); -} - --- Rules -abstract production rewriteRule -top::StrategyExpr ::= id::Name ty::TypeExpr ml::MRuleList -{ - top.unparse = "rule on " ++ id.name ++ "::" ++ ty.unparse ++ " of " ++ ml.unparse ++ " end"; - propagate liftedStrategies; - - -- Pattern matching error checking (mostly) happens on what caseExpr forwards to, - -- so we need to decorate one of those here. - local checkExpr::Expr = - letp( - assignExpr(id, '::', ty, '=', errorExpr([], location=top.location), location=top.location), - caseExpr( - [hackExprType(ty.typerep, location=top.location)], - ml.matchRuleList, - errorExpr([], location=top.location), - ty.typerep, - location=top.location), - location=top.location); - checkExpr.env = top.env; - checkExpr.flowEnv = top.flowEnv; - checkExpr.downSubst = emptySubst(); - checkExpr.finalSubst = checkExpr.upSubst; - checkExpr.grammarName = top.grammarName; - checkExpr.frame = top.frame; - checkExpr.config = top.config; - checkExpr.compiledGrammars = top.compiledGrammars; - - top.errors <- checkExpr.errors; - top.errors <- - if !ty.typerep.isDecorable - then [wrn(ty.location, "Only rules on nonterminals can have an effect")] - else []; - - top.flowDefs <- checkExpr.flowDefs; - - ml.matchRulePatternSize = 1; - - local res::Expr = - caseExpr( - [Silver_Expr { $name{top.frame.signature.outputElement.elementName} }], - ml.translation, - Silver_Expr { core:nothing() }, - nonterminalType("core:Maybe", [ty.typerep]), - location=top.location); - top.partialTranslation = - if unify(ty.typerep, top.frame.signature.outputElement.typerep).failure - then Silver_Expr { core:nothing() } - else if top.frame.signature.outputElement.elementName == id.name - then res - else Silver_Expr { - let $Name{id}::$TypeExpr{ty} = $name{top.frame.signature.outputElement.elementName} - in $Expr{res} - end - }; -} - --- Hack dummy expr with a given type -abstract production hackExprType -top::Expr ::= t::Type -{ - top.typerep = t; - forwards to errorExpr([], location=top.location); -} - -attribute matchesFrame occurs on MRuleList, MatchRule, PatternList, Pattern; -propagate matchesFrame on MRuleList, MatchRule, PatternList; - -synthesized attribute translation::a; -attribute translation<[AbstractMatchRule]> occurs on MRuleList; - -aspect production mRuleList_one -top::MRuleList ::= m::MatchRule -{ - top.translation = [m.translation]; -} - -aspect production mRuleList_cons -top::MRuleList ::= h::MatchRule '|' t::MRuleList -{ - top.translation = h.translation :: t.translation; -} - -attribute translation occurs on MatchRule; - -aspect production matchRule_c -top::MatchRule ::= pt::PatternList _ e::Expr -{ - top.translation = - matchRule( - pt.patternList, nothing(), Silver_Expr { core:just($Expr{e}) }, - location=top.location); -} - -aspect production matchRuleWhen_c -top::MatchRule ::= pt::PatternList 'when' cond::Expr _ e::Expr -{ - top.translation = - matchRule( - pt.patternList, just(pair(cond, nothing())), Silver_Expr { core:just($Expr{e}) }, - location=top.location); -} - -aspect production matchRuleWhenMatches_c -top::MatchRule ::= pt::PatternList 'when' cond::Expr 'matches' p::Pattern _ e::Expr -{ - top.translation = - matchRule( - pt.patternList, just(pair(cond, just(p))), Silver_Expr { core:just($Expr{e}) }, - location=top.location); -} - -aspect default production -top::Pattern ::= -{ - top.matchesFrame := true; -} - -aspect production prodAppPattern_named -top::Pattern ::= prod::QName '(' ps::PatternList ',' nps::NamedPatternList ')' -{ - top.matchesFrame := prod.lookupValue.fullName == top.frame.fullName; -} - --- References to other attributes or rec variables -abstract production nameRef -top::StrategyExpr ::= id::QName -{ - top.unparse = id.unparse; - - -- Forwarding depends on env here, these must be computed without env - propagate liftedStrategies; - top.attrRefName = just(fromMaybe(id.name, lookupBy(stringEq, id.name, top.recVarNameEnv))); - top.isId = false; - - local attrDcl::DclInfo = id.lookupAttribute.dcl; - attrDcl.givenNonterminalType = error("Not actually needed"); -- Ugh environment needs refactoring - forwards to - if lookupBy(stringEq, id.name, top.recVarNameEnv).isJust - then recVarRef(id, genName=top.genName, location=top.location) - else if !null(id.lookupAttribute.errors) - then errorRef(id.lookupAttribute.errors, id, genName=top.genName, location=top.location) - else if attrIsTotal(top.env, id.name) - then totalRef(qNameAttrOccur(id, location=top.location), genName=top.genName, location=top.location) - else partialRef(qNameAttrOccur(id, location=top.location), genName=top.genName, location=top.location); -} -abstract production errorRef -top::StrategyExpr ::= msg::[Message] id::Decorated QName -{ - top.unparse = id.unparse; - - propagate liftedStrategies; - top.attrRefName = just(id.name); - - top.errors <- msg; - top.partialTranslation = Silver_Expr { core:nothing() }; -} -abstract production recVarRef -top::StrategyExpr ::= id::Decorated QName -{ - top.unparse = id.unparse; - - propagate liftedStrategies; - top.attrRefName = lookupBy(stringEq, id.name, top.recVarNameEnv); - top.isTotal = lookupBy(stringEq, id.name, top.recVarTotalEnv).fromJust; - top.freeRecVars <- [id.name]; - - top.partialTranslation = - if attrIsTotal(top.env, top.attrRefName.fromJust) - then asPartial(top.totalTranslation) - else Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$qName{top.attrRefName.fromJust} }; - top.totalTranslation = - if attrIsTotal(top.env, top.attrRefName.fromJust) - then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$qName{top.attrRefName.fromJust} } - else asTotal(top.frame.signature.outputElement.typerep, top.partialTranslation); -} -abstract production partialRef -top::StrategyExpr ::= attr::QNameAttrOccur -{ - top.unparse = attr.unparse; - - -- Lookup for error checking is *not* contextual, since we don't know the frame here - local attrDcl::DclInfo = case attr of qNameAttrOccur(a) -> a.lookupAttribute.dcl end; - attrDcl.givenNonterminalType = error("Not actually needed"); -- Ugh environment needs refactoring - top.errors := - case attrDcl.typerep, attrDcl.dclBoundVars of - | nonterminalType("core:Maybe", [varType(a1)]), [a2] when tyVarEqual(a1, a2) -> [] - | nonterminalType("core:Maybe", [nonterminalType(nt, _)]), _ -> - if null(getOccursDcl(attrDcl.fullName, nt, top.env)) - then [wrn(attr.location, s"Attribute ${attr.name} cannot be used as a partial strategy, because it doesn't occur on its own nonterminal type ${nt}")] - else [] - | errorType(), _ -> [] - | _, _ -> [err(attr.location, s"Attribute ${attr.name} cannot be used as a partial strategy")] - end; - - propagate liftedStrategies; - top.attrRefName = just(attr.name); - top.matchesFrame := attr.matchesFrame; - top.isTotal = false; - top.partialRefs <- [attrDcl.fullName]; - - attr.attrFor = top.frame.signature.outputElement.typerep; - - top.partialTranslation = - if attr.matchesFrame - then Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$QNameAttrOccur{attr} } - else Silver_Expr { core:nothing() }; -} -abstract production totalRef -top::StrategyExpr ::= attr::QNameAttrOccur -{ - top.unparse = attr.unparse; - - -- Lookup for error checking is *not* contextual, since we don't know the frame here - local attrDcl::DclInfo = case attr of qNameAttrOccur(a) -> a.lookupAttribute.dcl end; - attrDcl.givenNonterminalType = error("Not actually needed"); -- Ugh environment needs refactoring - top.errors := - case attrDcl.typerep, attrDcl.dclBoundVars of - | varType(a1), [a2] when tyVarEqual(a1, a2) -> [] - | nonterminalType(nt, _), _ -> - if null(getOccursDcl(attrDcl.fullName, nt, top.env)) - then [wrn(attr.location, s"Attribute ${attr.name} cannot be used as total strategy, because it doesn't occur on its own nonterminal type ${nt}")] - else [] - | errorType(), _ -> [] - | _, _ -> [err(attr.location, s"Attribute ${attr.name} cannot be used as total strategy")] - end; - - propagate liftedStrategies; - top.attrRefName = just(attr.name); - top.matchesFrame := attr.matchesFrame; - top.isTotal = true; - top.totalRefs <- [attrDcl.fullName]; - - attr.attrFor = top.frame.signature.outputElement.typerep; - - top.totalTranslation = Silver_Expr { $name{top.frame.signature.outputElement.elementName}.$QNameAttrOccur{attr} }; -} - --- The result of performing an inlining optimization -abstract production inlined -top::StrategyExpr ::= attr::Decorated QNameAttrOccur s::StrategyExpr -{ - top.unparse = s"(${s.unparse} aka ${attr.unparse})"; - propagate liftedStrategies; - top.attrRefName = just(attr.attrDcl.fullName); - top.isTotal = s.isTotal; - top.partialTranslation = - if attr.matchesFrame - then s.partialTranslation - else Silver_Expr { core:nothing() }; - top.totalTranslation = s.totalTranslation; - - s.outerAttr = top.outerAttr; - s.inlinedStrategies = attr.attrDcl.fullName :: top.inlinedStrategies; -} - -attribute matchesFrame occurs on QNameAttrOccur; - -aspect production qNameAttrOccur -top::QNameAttrOccur ::= at::QName -{ - top.matchesFrame := top.found && - case top.typerep of - | nonterminalType("core:Maybe", [t]) -> !unify(top.attrFor, t).failure - | t -> !unify(top.attrFor, t).failure - end; -} - -function attrIsTotal -Boolean ::= env::Decorated Env attrName::String -{ - local dcls::[DclInfo] = getAttrDcl(attrName, env); - return - case dcls of - | [] -> false - | d :: _ -> - case decorate d with { givenNonterminalType = error("Not actually needed"); }.typerep of -- Ugh environment needs refactoring - | nonterminalType("core:Maybe", _) -> false - | _ -> true - end - end; -} - -function attrMatchesFrame -Boolean ::= env::Decorated Env attrName::String attrFor::Type -{ - return - decorate qNameAttrOccur(qName(loc("", -1, -1, -1, -1, -1, -1), attrName), location=loc("", -1, -1, -1, -1, -1, -1)) - with { env = env; attrFor = attrFor; }.matchesFrame; -} - -function attrMatchesChild -Boolean ::= env::Decorated Env attrName::String frame::BlockContext -{ - return - any( - map( - \ e::NamedSignatureElement -> attrMatchesFrame(env, attrName, e.typerep), - frame.signature.inputElements)); -} diff --git a/grammars/silver/extension/strategyattr/Terminals.sv b/grammars/silver/extension/strategyattr/Terminals.sv deleted file mode 100644 index 86ebabb37..000000000 --- a/grammars/silver/extension/strategyattr/Terminals.sv +++ /dev/null @@ -1,38 +0,0 @@ -grammar silver:extension:strategyattr; - -terminal Strategy_kwd 'strategy' lexer classes {KEYWORD, RESERVED}; -terminal Partial_kwd 'partial' lexer classes {KEYWORD, RESERVED}; - -terminal Sequence_t '<*' precedence = 12, association = left; -- Same as * -terminal Choice_t '<+' precedence = 11, association = left; -- Same as + - -lexer class Strategy dominates StrategyName_t; - -terminal Id_t 'id' lexer classes {KEYWORD, Strategy}; -terminal Fail_t 'fail' lexer classes {KEYWORD, Strategy}; -terminal All_t 'all' lexer classes {KEYWORD, Strategy}; -terminal Some_t 'some' lexer classes {KEYWORD, Strategy}; -terminal One_t 'one' lexer classes {KEYWORD, Strategy}; -terminal Rule_t 'rule' lexer classes {KEYWORD, Strategy}; -terminal Rec_t 'rec' lexer classes {KEYWORD, Strategy}; - -terminal PrintTerm_t 'printTerm' lexer classes {KEYWORD, Strategy}; -terminal Try_t 'try' lexer classes {KEYWORD, Strategy}; -terminal Repeat_t 'repeat' lexer classes {KEYWORD, Strategy}; -terminal Reduce_t 'reduce' lexer classes {KEYWORD, Strategy}; -terminal BottomUp_t 'bottomUp' lexer classes {KEYWORD, Strategy}; -terminal TopDown_t 'topDown' lexer classes {KEYWORD, Strategy}; -terminal DownUp_t 'downUp' lexer classes {KEYWORD, Strategy}; -terminal AllBottomUp_t 'allBottomUp' lexer classes {KEYWORD, Strategy}; -terminal AllTopDown_t 'allTopDown' lexer classes {KEYWORD, Strategy}; -terminal AllDownUp_t 'allDownUp' lexer classes {KEYWORD, Strategy}; -terminal SomeBottomUp_t 'someBottomUp' lexer classes {KEYWORD, Strategy}; -terminal SomeTopDown_t 'someTopDown' lexer classes {KEYWORD, Strategy}; -terminal SomeDownUp_t 'someDownUp' lexer classes {KEYWORD, Strategy}; -terminal OnceBottomUp_t 'onceBottomUp' lexer classes {KEYWORD, Strategy}; -terminal OnceTopDown_t 'onceTopDown' lexer classes {KEYWORD, Strategy}; -terminal OnceDownUp_t 'onceDownUp' lexer classes {KEYWORD, Strategy}; -terminal Innermost_t 'innermost' lexer classes {KEYWORD, Strategy}; -terminal Outermost_t 'outermost' lexer classes {KEYWORD, Strategy}; - -terminal StrategyName_t /[a-z][A-Za-z0-9\_]*/ lexer classes {IDENTIFIER}; diff --git a/grammars/silver/extension/strategyattr/convenience/Convenience.sv b/grammars/silver/extension/strategyattr/convenience/Convenience.sv deleted file mode 100644 index a31847dad..000000000 --- a/grammars/silver/extension/strategyattr/convenience/Convenience.sv +++ /dev/null @@ -1,31 +0,0 @@ -grammar silver:extension:strategyattr:convenience; - -import silver:extension:strategyattr; -import silver:extension:convenience; -import silver:definition:core; -import silver:definition:concrete_syntax; -import silver:definition:type:syntax; -import silver:definition:type; -import silver:definition:env; - -concrete production partialStrategyAttributeDclMultiple -top::AGDcl ::= 'partial' 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "partial strategy attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; - forwards to - appendAGDcl( - partialStrategyAttributeDcl($1, $2, $3, a, $5, e, $10, location=a.location), - makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), - location=top.location); -} - -concrete production totalStrategyAttributeDclMultiple -top::AGDcl ::= 'strategy' 'attribute' a::Name '=' e::StrategyExpr_c 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "strategy attribute " ++ a.name ++ " occurs on " ++ qs.unparse ++ ";"; - forwards to - appendAGDcl( - totalStrategyAttributeDcl($1, $2, a, $4, e, $9, location=a.location), - makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), botlNone(location=top.location)), qs.qnames), - location=top.location); -} diff --git a/grammars/silver/extension/templating/DocConfig.sv b/grammars/silver/extension/templating/DocConfig.sv deleted file mode 100644 index cb9353e3d..000000000 --- a/grammars/silver/extension/templating/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension:templating; - -{@config - header:"---\nlayout: sv_wiki\ntitle: String Templating\nmenu_title: String Templating\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/templating/StringTemplating.sv b/grammars/silver/extension/templating/StringTemplating.sv deleted file mode 100644 index 90494d2ae..000000000 --- a/grammars/silver/extension/templating/StringTemplating.sv +++ /dev/null @@ -1,183 +0,0 @@ -grammar silver:extension:templating; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; - -exports silver:extension:templating:syntax; - -terminal Template_kwd 's"""' lexer classes {LITERAL}; -terminal SLTemplate_kwd 's"' lexer classes {LITERAL}; - -concrete production templateExpr -top::Expr ::= Template_kwd t::TemplateString -layout {} -{ - forwards to infold(plusPlus(_, '++', _, location=top.location), t.stringTemplate); -} - -concrete production singleLineTemplateExpr -top::Expr ::= SLTemplate_kwd t::SingleLineTemplateString -layout {} -{ - forwards to infold(plusPlus(_, '++', _, location=top.location), t.stringTemplate); -} - -terminal PPTemplate_kwd 'pp"""' lexer classes {LITERAL}; -terminal SLPPTemplate_kwd 'pp"' lexer classes {LITERAL}; - -concrete production pptemplateExpr -top::Expr ::= PPTemplate_kwd t::TemplateString -layout {} -{ - forwards to infold(catcall(_, _, top.location), t.ppTemplate); -} - -concrete production singleLinepptemplateExpr -top::Expr ::= SLPPTemplate_kwd t::SingleLineTemplateString -layout {} -{ - forwards to infold(catcall(_, _, top.location), t.ppTemplate); -} - -function catcall -Expr ::= a::Expr b::Expr l::Location -{ - return mkStrFunctionInvocation(l, "silver:langutil:pp:cat", [a, b]); -} - --- TODO: make standard somehow? -function infold -a ::= f::(a ::= a a) l::[a] -{ - return if null(l) then error("invalid use of infold") - else if null(tail(l)) then head(l) - else f(head(l), infold(f, tail(l))); -} - -synthesized attribute stringTemplate :: [Expr] occurs on TemplateString, SingleLineTemplateString, - TemplateStringBody, SingleLineTemplateStringBody, - TemplateStringBodyItem, SingleLineTemplateStringBodyItem, NonWater; -synthesized attribute ppTemplate :: [Expr] occurs on TemplateString, SingleLineTemplateString, - TemplateStringBody, SingleLineTemplateStringBody, - TemplateStringBodyItem, SingleLineTemplateStringBodyItem, NonWater; - -aspect production templateString -top::TemplateString ::= b::TemplateStringBody _ -{ - top.stringTemplate = b.stringTemplate; - top.ppTemplate = b.ppTemplate; -} - -aspect production templateStringEmpty -top::TemplateString ::= _ -{ - top.stringTemplate = [stringConst(terminal(String_t, "\"\"", top.location), location=top.location)]; - top.ppTemplate = [mkStrFunctionInvocation(top.location, "silver:langutil:pp:notext", [])]; -} - -aspect production singleLineTemplateString -top::SingleLineTemplateString ::= b::SingleLineTemplateStringBody _ -{ - top.stringTemplate = b.stringTemplate; - top.ppTemplate = b.ppTemplate; -} - -aspect production singleLineTemplateStringEmpty -top::SingleLineTemplateString ::= _ -{ - top.stringTemplate = [stringConst(terminal(String_t, "\"\"", top.location), location=top.location)]; - top.ppTemplate = [mkStrFunctionInvocation(top.location, "silver:langutil:pp:notext", [])]; -} - -aspect production bodyCons -top::TemplateStringBody ::= h::TemplateStringBodyItem t::TemplateStringBody -{ - top.stringTemplate = h.stringTemplate ++ t.stringTemplate; - top.ppTemplate = h.ppTemplate ++ t.ppTemplate; -} - -aspect production bodyOne -top::TemplateStringBody ::= h::TemplateStringBodyItem -{ - top.stringTemplate = h.stringTemplate; - top.ppTemplate = h.ppTemplate; -} - -aspect production bodyOneWater -top::TemplateStringBody ::= w::Water -{ - top.stringTemplate = [stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)]; - top.ppTemplate = [ - mkStrFunctionInvocation(w.location, "silver:langutil:pp:text", [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)])]; -} - -aspect production singleLineBodyCons -top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem t::SingleLineTemplateStringBody -{ - top.stringTemplate = h.stringTemplate ++ t.stringTemplate; - top.ppTemplate = h.ppTemplate ++ t.ppTemplate; -} - -aspect production singleLineBodyOne -top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem -{ - top.stringTemplate = h.stringTemplate; - top.ppTemplate = h.ppTemplate; -} - -aspect production singleLineBodyOneWater -top::SingleLineTemplateStringBody ::= w::SingleLineWater -{ - top.stringTemplate = [stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)]; - top.ppTemplate = [ - mkStrFunctionInvocation(w.location, "silver:langutil:pp:text", [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)])]; -} - -aspect production itemWaterEscape -top::TemplateStringBodyItem ::= w::Water nw::NonWater -{ - top.stringTemplate = [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)] ++ - nw.stringTemplate; - top.ppTemplate = [ - mkStrFunctionInvocation(w.location, "silver:langutil:pp:text", [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)])] ++ - nw.ppTemplate; -} - -aspect production itemEscape -top::TemplateStringBodyItem ::= nw::NonWater -{ - top.stringTemplate = nw.stringTemplate; - top.ppTemplate = nw.ppTemplate; -} - -aspect production singleLineItemWaterEscape -top::SingleLineTemplateStringBodyItem ::= w::SingleLineWater nw::NonWater -{ - top.stringTemplate = [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)] ++ - nw.stringTemplate; - top.ppTemplate = [ - mkStrFunctionInvocation(w.location, "silver:langutil:pp:text", [ - stringConst(terminal(String_t, "\"" ++ w.waterString ++ "\"", w.location), location=w.location)])] ++ - nw.ppTemplate; -} - -aspect production singleLineItemEscape -top::SingleLineTemplateStringBodyItem ::= nw::NonWater -{ - top.stringTemplate = nw.stringTemplate; - top.ppTemplate = nw.ppTemplate; -} - -aspect production nonwater -top::NonWater ::= '${' e::Expr '}' -{ - top.stringTemplate = [e]; - top.ppTemplate = [e]; -} diff --git a/grammars/silver/extension/templating/syntax/DocConfig.sv b/grammars/silver/extension/templating/syntax/DocConfig.sv deleted file mode 100644 index 085a9e9a8..000000000 --- a/grammars/silver/extension/templating/syntax/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension:templating:syntax; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Syntax\nmenu_title: Syntax\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/templating/syntax/Templating.sv b/grammars/silver/extension/templating/syntax/Templating.sv deleted file mode 100644 index 3795e4a68..000000000 --- a/grammars/silver/extension/templating/syntax/Templating.sv +++ /dev/null @@ -1,199 +0,0 @@ -grammar silver:extension:templating:syntax; - -imports silver:definition:core; - -terminal TripleQuote /\"\"\"/ lexer classes {LITERAL}; -terminal DoubleDollar '$$' lexer classes {LITERAL}; -terminal QuoteWater /[^$\r\n\t\"\\]+/ lexer classes {LITERAL}; -terminal SingleLineQuoteWater /([^$\r\n\t\"\\]|[\\][\"]|[\\][\\]|[\\]b|[\\]n|[\\]r|[\\]f|[\\]t)+/ lexer classes {LITERAL}; -terminal LiteralNewline /(\n|\r\n)/ lexer classes {LITERAL}; -terminal LiteralTab /\t/ lexer classes {LITERAL}; -terminal LiteralQuote /\"/ lexer classes {LITERAL}; -terminal LiteralBackslash /\\/ lexer classes {LITERAL}; - -terminal OpenEscape '${'; - -{-- A string without the first triple quote, with escaped expressions within -} -nonterminal TemplateString with location; -{-- A single-line string without the first quote, with escaped expressions within -} -nonterminal SingleLineTemplateString with location; -{-- A list of alternating String/Exprs -} -nonterminal TemplateStringBody with location; -{-- A single-line list of alternating String/Exprs -} -nonterminal SingleLineTemplateStringBody with location; -{-- Either a String or an Expr -} -nonterminal TemplateStringBodyItem with location; -{-- Either a single-line String or an Expr -} -nonterminal SingleLineTemplateStringBodyItem with location; -{-- An escape -} -nonterminal NonWater with location; -{-- List that yields a string -} -nonterminal Water with location, waterString; -{-- List that yields a single-line string -} -nonterminal SingleLineWater with location, waterString; -{-- Components that yield a string -} -nonterminal WaterItem with location, waterString; -{-- Components that yield a single-line string -} -nonterminal SingleLineWaterItem with location, waterString; - -{-- The string corresponding to the water -} -synthesized attribute waterString :: String; - -concrete production templateString -top::TemplateString ::= b::TemplateStringBody TripleQuote -{ -} - -concrete production templateStringEmpty -top::TemplateString ::= TripleQuote -{ -} - -concrete production singleLineTemplateString -top::SingleLineTemplateString ::= b::SingleLineTemplateStringBody LiteralQuote -{ -} - -concrete production singleLineTemplateStringEmpty -top::SingleLineTemplateString ::= LiteralQuote -{ -} - -concrete production bodyCons -top::TemplateStringBody ::= h::TemplateStringBodyItem t::TemplateStringBody -{ -} - -concrete production bodyOne -top::TemplateStringBody ::= h::TemplateStringBodyItem -{ -} - -concrete production bodyOneWater -top::TemplateStringBody ::= h::Water -{ -} - -concrete production singleLineBodyCons -top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem t::SingleLineTemplateStringBody -{ -} - -concrete production singleLineBodyOne -top::SingleLineTemplateStringBody ::= h::SingleLineTemplateStringBodyItem -{ -} - -concrete production singleLineBodyOneWater -top::SingleLineTemplateStringBody ::= h::SingleLineWater -{ -} - -concrete production itemWaterEscape -top::TemplateStringBodyItem ::= w::Water nw::NonWater -{ -} - -concrete production itemEscape -top::TemplateStringBodyItem ::= nw::NonWater -{ -} - -concrete production singleLineItemWaterEscape -top::SingleLineTemplateStringBodyItem ::= w::SingleLineWater nw::NonWater -{ -} - -concrete production singleLineItemEscape -top::SingleLineTemplateStringBodyItem ::= nw::NonWater -{ -} - -concrete production nonwater -top::NonWater ::= '${' e::Expr '}' -layout {BlockComments, Comments, WhiteSpace} -{ -} - -concrete production waterCons -top::Water ::= h::Water t::WaterItem -{ - top.waterString = h.waterString ++ t.waterString; -} - -concrete production waterOne -top::Water ::= h::WaterItem -{ - top.waterString = h.waterString; -} - -concrete production water -top::WaterItem ::= w::QuoteWater -{ - top.waterString = w.lexeme; -} - -concrete production waterDollar -top::WaterItem ::= '$$' -{ - top.waterString = "$"; -} - -concrete production waterBackSlash -top::WaterItem ::= LiteralBackslash -{ - -- The reason I decided to make backslashes not "work" is due to - -- dealing with \" Originally, this turned into \\" in the string - -- because the quote got escaped... this of course, was disaster." - top.waterString = "\\\\"; -} - -concrete production waterNewline -top::WaterItem ::= LiteralNewline -{ - -- We always interpret newlines as just \n, even if the source file was \r\n. - top.waterString = "\\n"; -} - -concrete production waterTab -top::WaterItem ::= LiteralTab -{ - top.waterString = "\\t"; -} - -concrete production waterQuote -top::WaterItem ::= LiteralQuote -{ - top.waterString = "\\\""; -} - -concrete production singleLineWaterCons -top::SingleLineWater ::= h::SingleLineWater t::SingleLineWaterItem -{ - top.waterString = h.waterString ++ t.waterString; -} - -concrete production singleLineWaterOne -top::SingleLineWater ::= h::SingleLineWaterItem -{ - top.waterString = h.waterString; -} - -concrete production singleLineWater -top::SingleLineWaterItem ::= w::SingleLineQuoteWater -{ - top.waterString = w.lexeme; -} - -concrete production singleLineWaterDollar -top::SingleLineWaterItem ::= '$$' -{ - top.waterString = "$"; -} - -concrete production singleLineWaterBackSlash -top::SingleLineWaterItem ::= LiteralBackslash -{ - -- Same as waterBackSlash - top.waterString = "\\\\"; -} diff --git a/grammars/silver/extension/testing/DocConfig.sv b/grammars/silver/extension/testing/DocConfig.sv deleted file mode 100644 index f09a3a512..000000000 --- a/grammars/silver/extension/testing/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:extension:testing; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Testing\nmenu_title: Testing\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/extension/testing/EqualityTest.sv b/grammars/silver/extension/testing/EqualityTest.sv deleted file mode 100644 index c8f68afc3..000000000 --- a/grammars/silver/extension/testing/EqualityTest.sv +++ /dev/null @@ -1,248 +0,0 @@ -grammar silver:extension:testing; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:concrete_syntax; -import silver:definition:type; -import silver:definition:type:syntax; -import silver:modification:collection; -import silver:extension:list; - -import silver:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; -- for the "oh no again!" hack below -import silver:driver:util only RootSpec; -- ditto - ---import silver:analysis:typechecking:core; - -import lib:extcore; - -terminal EqualityTest_t 'equalityTest' lexer classes {KEYWORD}; - -concrete production equalityTest2_p -ag::AGDcl ::= kwd::'equalityTest' - '(' value::Expr ',' expected::Expr ',' - valueType::TypeExpr ',' testSuite::Name ')' ';' -{ - ag.unparse = "equalityTest (" ++ value.unparse ++ "," ++ expected.unparse ++ ",\n" ++ - " " ++ valueType.unparse ++ ", " ++ testSuite.unparse ++ ");\n"; - - ag.errors := case equalityTestExpr of - | just(_) -> [] - | nothing() -> - [err(valueType.location, "Type \"" ++ valueType.unparse ++ "\" not suported on equality tests.")] - end; - - local attribute errCheck1 :: TypeCheck; - local attribute errCheck2 :: TypeCheck; - local attribute errCheck3 :: TypeCheck; - errCheck1 = check(value.typerep, expected.typerep); - errCheck2 = check(value.typerep, valueType.typerep); - errCheck3 = check(expected.typerep, valueType.typerep); - - ag.errors <- - if !errCheck1.typeerror then [] - else [err(value.location, "Type of first and second expressions in equalityTest do not match. Instead they are " ++ errCheck1.leftpp ++ " and " ++ errCheck1.rightpp)]; - - ag.errors <- - if !errCheck2.typeerror then [] - else [err(value.location, "Type of initial expression does not match specified type (3rd argument). Instead they are " ++ errCheck2.leftpp ++ " and " ++ errCheck2.rightpp)]; - - ag.errors <- - if !errCheck3.typeerror then [] - else [err(value.location, "Type of second expression does not match specified type (3rd argument). Instead they are " ++ errCheck3.leftpp ++ " and " ++ errCheck3.rightpp)]; - - value.downSubst = emptySubst(); - expected.downSubst = value.upSubst; - errCheck1.downSubst = expected.upSubst; - errCheck2.downSubst = errCheck1.upSubst; - errCheck3.downSubst = errCheck2.upSubst; - - value.finalSubst = errCheck3.upSubst; - expected.finalSubst = errCheck3.upSubst; - errCheck1.finalSubst = errCheck3.upSubst; - errCheck2.finalSubst = errCheck3.upSubst; - errCheck3.finalSubst = errCheck3.upSubst; - - -- TODO: one of those type error checks above is redundant - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(ag.grammarName, ag.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(ag.grammarName, ag.compiledGrammars)).productionFlowGraphs; - - value.frame = globalExprContext(constructAnonymousGraph(value.flowDefs, ag.env, myProds, myFlow)); - expected.frame = globalExprContext(constructAnonymousGraph(expected.flowDefs, ag.env, myProds, myFlow)); - - - ag.errors <- forward.errors; - -{- Causes some circularities with the environment. TODO - forwards to if !errCheck1.typeerror && !errCheck2.typeerror && !errCheck3.typeerror - then appendAGDcl(absProdCS, aspProdCS) - else emptyAGDcl(); --} - - forwards to appendAGDcl(absProdCS, aspProdCS, location=ag.location); - -{- - local absProdCS :: AGDcl = asAGDcl ( - "abstract production " ++ testName ++ "\n" ++ - "t::Test ::= \n" ++ - "{ \n" ++ - " local attribute value :: %%%Type valueType; \n" ++ - " value = %%%Expr value; \n" ++ - " local attribute expected :: %%%Type valueType; \n" ++ - " expected = %%%Expr expected; \n" ++ - " t.msg = \"Test at " ++ ag.location.unparse ++ " failed. \\n\" ++ \n" ++ - " \"Checking that expression \\n\" ++ \n" ++ - " \" " ++ stringifyString(value.unparse) ++ "\" ++ \n" ++ - " \"\\nshould be same as expression \\n\" ++ \n" ++ - " \" " ++ stringifyString(expected.unparse) ++ "\\n\" ++ \n" ++ - " \"Actual value: \\n \" ++ \n" ++ - " %%%Expr toStringValueExpr ++ \"\\n\" ++ \n" ++ - " \"Expected value: \\n \" ++ \n" ++ - " %%%Expr toStringExpectedExpr ++ \"\\n\" ++ \n" ++ - " \"\";\n" ++ - " t.pass = %%%Expr equalityTestCode; \n" ++ - " forwards to defTest(); \n" ++ - "}" , - cons_CS_env("value", wrapExpr(value), - cons_CS_env("expected", wrapExpr(expected), - cons_CS_env("valueType", wrapType(valueType), - cons_CS_env("testSuite", wrapName(testSuite), - cons_CS_env("toStringValueExpr", - wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,toStringValueExpr)), - cons_CS_env("toStringExpectedExpr", - wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,toStringExpectedExpr)), - cons_CS_env("equalityTestCode", - wrapExpr( fromMaybe(error("TypeNotSupportedInternalError") ,equalityTestExpr)) , - empty_CS_env()))))))) , 3 ); --} - - -- TODO: BUG: FIXME: these names should be mangled. I ran into 't' being shadowed in a test I wrote! - local tref :: Name = name("t", ag.location); - local testNameref :: Name = name(testName, ag.location); - local valueref :: Name = name("value", ag.location); - local expectedref :: Name = name("expected", ag.location); - local msgref :: Name = name("msg", ag.location); - local passref :: Name = name("pass", ag.location); - - local absProdCS :: AGDcl = - productionDcl('abstract', 'production', testNameref, - productionSignature( - productionLHS(tref, '::', - nominalTypeExpr(qNameTypeId(terminal(IdUpper_t, "Test", ag.location), location=ag.location), botlNone(location=ag.location), location=ag.location), location=ag.location), - '::=', productionRHSNil(location=ag.location), location=ag.location), - productionBody('{', foldl(productionStmtsSnoc(_, _, location=ag.location), productionStmtsNil(location=ag.location), [ - localAttributeDcl('local', 'attribute', valueref, '::', valueType, ';', location=ag.location), - valueEq(qNameId(valueref, location=valueref.location), '=', value, ';', location=ag.location), - localAttributeDcl('local', 'attribute', expectedref, '::', valueType, ';', location=ag.location), - valueEq(qNameId(expectedref, location=expectedref.location), '=', expected, ';', location=ag.location), - attributeDef(concreteDefLHS(qNameId(tref, location=tref.location), location=tref.location), '.', qNameAttrOccur(qNameId(msgref, location=msgref.location), location=ag.location), '=', - foldStringExprs([ - strCnst("Test at " ++ ag.location.unparse ++ " failed.\nChecking that expression\n " ++ - stringifyString(value.unparse) ++ "\nshould be same as expression\n " ++ - stringifyString(expected.unparse) ++ "\nActual value:\n "), - toStringValueExpr.fromJust, - strCnst("\nExpected value: \n "), - toStringExpectedExpr.fromJust, - strCnst("\n")]), ';', location=ag.location), - attributeDef(concreteDefLHS(qNameId(tref, location=tref.location), location=tref.location), '.', qNameAttrOccur(qNameId(passref, location=passref.location), location=ag.location), '=', - equalityTestExpr.fromJust, ';', location=ag.location), - forwardsTo('forwards', 'to', mkStrFunctionInvocation(ag.location, "defTest", []), ';', location=ag.location)]), '}', location=ag.location), location=ag.location); - -{- - local aspProdCS :: AGDcl = asAGDcl ( - "aspect production %%%Name testSuite \n" ++ - "t ::= \n" ++ - "{ testsToPerform <- [ " ++ testName ++ "() ]; } " , - cons_CS_env("testSuite", wrapName(testSuite), empty_CS_env()) , 4 ); --} - - local aspProdCS :: AGDcl = - aspectProductionDcl('aspect', 'production', qNameId(testSuite, location=ag.location), - aspectProductionSignature( - aspectProductionLHSId(tref, location=ag.location), - '::=', aspectRHSElemNil(location=ag.location), location=ag.location), - productionBody('{', - productionStmtsSnoc( - productionStmtsNil(location=ag.location), - valContainsAppend( - qName(ag.location, "testsToPerform"), - '<-', - fullList('[', - exprsSingle( - applicationEmpty( - baseExpr(qNameId(testNameref, location=ag.location), location=ag.location), '(', ')', location=ag.location), location=ag.location), - ']', location=ag.location), - ';', location=ag.location), location=ag.location), '}', location=ag.location), location=ag.location); - - - - -- If valueType is a base type (Integer, Float, etc.) or a List whose - -- element type is a base type, then we can check for equality. - -- With curried functions we could handle nested lists, but not now. - local equalityTestExpr :: Maybe = - mkEqualityTestExprCS(valueType, ag.location); - - local toStringValueExpr :: Maybe = - mkToStringExprCS(valueType, "value", ag.location); - local toStringExpectedExpr :: Maybe = - mkToStringExprCS(valueType, "expected", ag.location); - - local testName :: String = "generatedTest" ++ "_" ++ - replaceChars(".","_",kwd.filename) ++ "_" ++ - toString(kwd.line) ++ "_" ++ - toString(genInt()); -} - --- Oh, boy... this whole pile of code is awful - -function functionNameForBaseTypesCS -Maybe ::= valueType::TypeExpr prefixS::String -{ return - case valueType of - | integerTypeExpr(_) -> just(prefixS ++ "Integer") - | floatTypeExpr(_) -> just(prefixS ++ "Float") - | stringTypeExpr(_) -> just(prefixS ++ "String") - | booleanTypeExpr(_) -> just(prefixS ++ "Boolean") - | _ -> nothing() - end; -} - -function mkToStringExprCS -Maybe ::= valueType::TypeExpr exprName::String l::Location -{ - return - case functionNameForBaseTypesCS(valueType, "toStringFrom") of - | just(btt) -> just(mkStrFunctionInvocation(l, btt, [mkNameExpr(exprName, l)])) - | nothing() -> - case valueType of - | listTypeExpr(_,elemType,_) -> - case functionNameForBaseTypesCS(elemType,"toStringFrom") of - | just(btt) -> - just(mkStrFunctionInvocation(l, "toStringFromList", [mkNameExpr(btt, l), mkNameExpr(exprName, l)])) - | _ -> nothing() - end - | _ -> nothing() - end - end; -} - -function mkEqualityTestExprCS -Maybe ::= valueType::TypeExpr l::Location -{ - return - case functionNameForBaseTypesCS(valueType, "equals") of - | just(btt) -> just(mkStrFunctionInvocation(l, btt, [mkNameExpr("value", l), mkNameExpr("expected", l)])) - | nothing() -> - case valueType of - | listTypeExpr(_,elemType,_) -> - case functionNameForBaseTypesCS(elemType, "equals") of - | just(btt) -> - just(mkStrFunctionInvocation(l, "equalsList", [mkNameExpr(btt, l), mkNameExpr("value", l), mkNameExpr("expected", l)])) - | _ -> nothing() - end - | _ -> nothing() - end - end; -} - diff --git a/grammars/silver/extension/testing/WrongCode.sv b/grammars/silver/extension/testing/WrongCode.sv deleted file mode 100644 index ba4832717..000000000 --- a/grammars/silver/extension/testing/WrongCode.sv +++ /dev/null @@ -1,64 +0,0 @@ -grammar silver:extension:testing; - -import silver:definition:core; -import silver:definition:env; - -terminal WrongCode_kwd 'wrongCode' lexer classes {KEYWORD}; -terminal WarnCode_kwd 'warnCode' lexer classes {KEYWORD}; -terminal WrongFlowCode_kwd 'wrongFlowCode' lexer classes {KEYWORD}; - -function containsMessage -Boolean ::= text::String severity::Integer msgs::[Message] -{ - return any(map((\x::Message -> x.severity==severity && indexOf(text, x.output)!=-1), msgs)); -} - -concrete production wrongDecl -top::AGDcl ::= 'wrongCode' s::String_t '{' ags::AGDcls '}' -{ - top.unparse = "wrongCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; - - top.errors := - if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 2, ags.errors) - then [err(top.location, "Wrong code did not raise an error containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors - else []; - - -- do extend its environment with its defs - ags.env = occursEnv(ags.occursDefs, newScopeEnv(ags.defs, top.env)); - - forwards to emptyAGDcl(location=top.location); -} - -concrete production warnDecl -top::AGDcl ::= 'warnCode' s::String_t '{' ags::AGDcls '}' -{ - top.unparse = "warnCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; - - top.errors := - if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 1, ags.errors) - then [err(top.location, "Warn code did not raise a warning containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors - else []; - - forwards to makeAppendAGDclOfAGDcls(ags); - -- Forward to the decls so that we can use the stuff declared with warnings in other tests -} - -concrete production wrongFlowDecl -top::AGDcl ::= 'wrongFlowCode' s::String_t '{' ags::AGDcls '}' -{ - top.unparse = "wrongFlowCode" ++ s.lexeme ++ "{" ++ ags.unparse ++ "}"; - - top.errors := - if !containsMessage(substring(1, length(s.lexeme) - 1, s.lexeme), 2, ags.errors) - then [err(top.location, "Wrong code did not raise an error containing " ++ s.lexeme ++ ". Bubbling up errors from lines " ++ toString($3.line) ++ " to " ++ toString($5.line))] ++ ags.errors - else []; - - -- do extend its environment with its defs - ags.env = occursEnv(ags.occursDefs, newScopeEnv(ags.defs, top.env)); - - -- let's ALSO propagate up flow info, so these kinds of errors are checked/caught - top.flowDefs := ags.flowDefs; - - forwards to emptyAGDcl(location=top.location); -} - diff --git a/grammars/silver/extension/treegen/Arbitrary.sv b/grammars/silver/extension/treegen/Arbitrary.sv deleted file mode 100644 index f4471cb31..000000000 --- a/grammars/silver/extension/treegen/Arbitrary.sv +++ /dev/null @@ -1,148 +0,0 @@ -grammar silver:extension:treegen; - -terminal Derive_t 'derive' lexer classes {KEYWORD}; - -terminal Arbitrary_t 'Arbitrary' lexer classes {KEYWORD}; - - -concrete production deriveagdcl -top::AGDcl ::= 'derive' 'Arbitrary' 'on' names::QNames ';' -{ - forwards to - foldr( - appendAGDcl(_, _, location=top.location), - emptyAGDcl(location=top.location), - map( - deriveArbitraryOn(_, top.env), - map((.qnwtQN), names.qnames))); -} - - - -function prodDclInfoNumChildLte -Boolean ::= l::DclInfo r::DclInfo -{ - return length(l.typerep.inputTypes) <= length(r.typerep.inputTypes); -} -function prodDclInfoNumChildEq -Boolean ::= l::DclInfo r::DclInfo -{ - return length(l.typerep.inputTypes) == length(r.typerep.inputTypes); -} - --- splits where operator becomes false in list -function takeWhile2 -[a] ::= f::(Boolean ::= a a) l::[a] -{ - return if null(l) then [] - else if null(tail(l)) then l - else if f(head(l), head(tail(l))) then head(l) :: takeWhile2(f, tail(l)) - else [head(l)]; -} - --- create a function called 'generateArbitraryID' -function deriveArbitraryOn -AGDcl ::= id::QName env::Decorated Env -{ - local l :: Location = id.location; - - id.env = env; - - local prods :: [DclInfo] = - sortBy(prodDclInfoNumChildLte, - getKnownProds(id.lookupType.fullName, env)); - - local num_lowest_arity :: Integer = length(takeWhile2(prodDclInfoNumChildEq, prods)); - - local sig :: FunctionSignature = - functionSignature( - functionLHS(typerepTypeExpr(id.lookupType.typerep, location=l), location=l), - '::=', - productionRHSCons( - productionRHSElem(name("current__depth", l), '::', typerepTypeExpr(intType(), location=l), location=l), - productionRHSNil(location=l), location=l), - location=l); - - local body :: ProductionBody = - productionBody('{', foldl(productionStmtsSnoc(_, _, location=l), productionStmtsNil(location=l), stmts), '}', location=l); - - local stmts :: [ProductionStmt] = - [ - shortLocalDecl('local', name("pval", l), '::', typerepTypeExpr(floatType(), location=l), '=', - ifThenElse( - 'if', gt(baseExpr(qName(l, "current__depth"), location=l), '>', intConst(terminal(Int_t, "12"), location=l), location=l), - 'then', multiply( - mkStrFunctionInvocation(l, "genRand", []), '*', - floatConst(terminal(Float_t, toString(toFloat(num_lowest_arity)/toFloat(length(prods)))), location=l), location=l), - 'else', mkStrFunctionInvocation(l, "genRand", []), - location=l), - ';', location=l), - returnDef('return', generateExprChain(0, prods, length(prods), l), ';', location=l) - ]; - - return - functionDcl( - 'function', - name(getGenArbName(id.lookupType.typerep), l), - sig, - body, location=l); -{- - -We got the id 'Expr' incoming. - -We should look up 'Expr' and get a list of productions, from that we get the probabilities. - -We then map deriveGenerateOn over the list of productions and generate a big if-then-else tree based on the genRand() - --} -} - -function generateExprChain -Expr ::= index::Integer lst::[DclInfo] total::Integer l::Location -{ - return if null(lst) then error("no productions for nonterminal at " ++ l.filename ++ ":" ++ toString(l.line) ++ "." ++ toString(l.column)) - else if null(tail(lst)) then - deriveGenerateOn(head(lst), l) - else - -- yield "if pval < '(index+1)/total' then 'deriveGenerateOn' else generateExprChain..." - ifThenElse( - 'if', lt(baseExpr(qName(l, "pval"), location=l), '<', floatConst(terminal(Float_t, toString(toFloat(index+ 1) / toFloat(total))), location=l), location=l), - 'then', deriveGenerateOn(head(lst), l), - 'else', generateExprChain(index + 1, tail(lst), total, l), - location=l); -} - --- construct a production, calling 'generateArbitraryID' for each child -function deriveGenerateOn -Expr ::= id::DclInfo l::Location -{ - local annos :: [Pair] = - if null(id.typerep.namedTypes) then - [] - else - -- we just erroneously assume the annotation must be location, for now - [pair("location", mkStrFunctionInvocation(l, "bogusLoc", []))]; - - return - mkFullFunctionInvocation( - l, - baseExpr(qName(l, id.fullName), location=l), - map(callGenArb(_, l), id.typerep.inputTypes), - annos); -} - --- Call generateArbitraryID -function callGenArb -Expr ::= te::Type l::Location -{ - return mkStrFunctionInvocation(l, getGenArbName(te), [plus(baseExpr(qName(l, "current__depth"), location=l), '+', intConst(terminal(Int_t, "1"), location=l), location=l)]); -} - --- Map a type to its ID name for use in 'generateArbitraryID' -function getGenArbName -String ::= te::Type -{ - return "generateArbitrary" ++ te.idNameForGenArb; -} - - diff --git a/grammars/silver/extension/treegen/Eq.sv b/grammars/silver/extension/treegen/Eq.sv deleted file mode 100644 index 08d3e7ce5..000000000 --- a/grammars/silver/extension/treegen/Eq.sv +++ /dev/null @@ -1,146 +0,0 @@ -grammar silver:extension:treegen; - -import silver:modification:let_fix; -import silver:extension:patternmatching; -import silver:modification:primitivepattern; -import silver:definition:flow:env; - -terminal Eq_t 'Eq' lexer classes {KEYWORD}; - -concrete production deriveEqagdcl -top::AGDcl ::= 'derive' 'Eq' 'on' names::QNames ';' -{ - -- bug: stupid hack. Find some other way to fix this, maybe? - top.flowDefs := []; - - forwards to - foldr( - appendAGDcl(_, _, location=top.location), - emptyAGDcl(location=top.location), - map( - deriveEqOn(_, top.env, top.flowEnv), - map((.qnwtQN), names.qnames))); -} - -function nonforwardingProd -Boolean ::= d::DclInfo fe::Decorated FlowEnv -{ - return null(lookupFwd(d.fullName, fe)); -} - -function deriveEqOn -AGDcl ::= id::QName env::Decorated Env fenv::Decorated FlowEnv -{ - id.env = env; - - local l :: Location = id.location; - - local prods :: [DclInfo] = filter(nonforwardingProd(_, fenv), getKnownProds(id.lookupType.fullName, env)); - - local sig :: FunctionSignature = - functionSignature( - functionLHS(typerepTypeExpr(boolType(), location=l), location=l), - '::=', - productionRHSCons( - productionRHSElem(name("l", l), '::', typerepTypeExpr(id.lookupType.typerep, location=l), location=l), - productionRHSCons( - productionRHSElem(name("r", l), '::', typerepTypeExpr(id.lookupType.typerep, location=l), location=l), - productionRHSNil(location=l), location=l), location=l), - location=l); - - local body :: ProductionBody = - productionBody('{', foldl(productionStmtsSnoc(_, _, location=l), productionStmtsNil(location=l), stmts), '}', location=l); - - local stmts :: [ProductionStmt] = - [ - returnDef('return', caseExpr_c('case', exprsCons(baseExpr(qName(l, "l"), location=l), ',', exprsSingle(baseExpr(qName(l, "r"), location=l), location=l), location=l), 'of', terminal(Opt_Vbar_t, "|"), ml, 'end', location=l), ';', location=l) - ]; - - local ml :: MRuleList = foldmrl(map(generateCheckEqMRuleList(_, l), prods)); - - return - functionDcl( - 'function', - name(getCheckEqName(id.lookupType.typerep), l), - sig, - body, location=l); -} - - -function foldmrl -MRuleList ::= l::[MatchRule] -{ - return if null(l) then error("unexpectedly empty match rules for generated pattern") - else if null(tail(l)) then mRuleList_one(head(l), location=head(l).location) - else mRuleList_cons(head(l), '|', foldmrl(tail(l)), location=head(l).location); -} - -function foldpattlist -PatternList ::= l::[Pattern] -{ - return if null(l) then patternList_nil(location=loc("generated",-1,-1,-1,-1,-1,-1)) - else if null(tail(l)) then patternList_one(head(l), location=head(l).location) - else patternList_more(head(l), ',', foldpattlist(tail(l)), location=head(l).location); -} - - -function generateCheckEqMRuleList -MatchRule ::= prod::DclInfo l::Location -{ - local children :: [Type] = prod.typerep.inputTypes; - - local lchildren :: [Name] = genIds("l", 0, length(children), l); - local rchildren :: [Name] = genIds("r", 0, length(children), l); - - local lchildpatt :: PatternList = foldpattlist(map(varPattern(_, location=l), lchildren)); - local rchildpatt :: PatternList = foldpattlist(map(varPattern(_, location=l), rchildren)); - - local lpatt :: Pattern = prodAppPattern(qName(l, prod.fullName), '(', lchildpatt, ')', location=l); - local rpatt :: Pattern = prodAppPattern(qName(l, prod.fullName), '(', rchildpatt, ')', location=l); - - -- match on prod(a1, a2, a3...), prod(b1, b2, b3...) -> checkEq(a1, b1) && chechEq(a2, b2) && ... - return - matchRule_c( - foldpattlist([lpatt, rpatt]), - '->', - zipCheckEqCalls(children, lchildren, rchildren), - location=l); -} - -function zipCheckEqCalls -Expr ::= t::[Type] l::[Name] r::[Name] -{ - local c :: Expr = callCheckEq(head(t), head(l).location, [baseExpr(qNameId(head(l), location=head(l).location), location=head(l).location), baseExpr(qNameId(head(r), location=head(l).location), location=head(r).location)]); - - return if null(t) then trueConst('true', location=loc("",-1,-1,-1,-1,-1,-1)) - else if null(tail(t)) then - c - else - and(c, '&&', zipCheckEqCalls(tail(t), tail(l), tail(r)), location=head(l).location); -} - - -function genIds -[Name] ::= side::String index::Integer total::Integer l::Location -{ - return if index == total then [] else - name(side ++ toString(index), l) :: genIds(side, index + 1, total, l); -} - - - -function callCheckEq -Expr ::= te::Type l::Location es::[Expr] -{ - return mkStrFunctionInvocation(l, getCheckEqName(te), es); -} - -function getCheckEqName -String ::= te::Type -{ - return "checkEq" ++ te.idNameForGenArb; -} - - - - diff --git a/grammars/silver/extension/treegen/Type.sv b/grammars/silver/extension/treegen/Type.sv deleted file mode 100644 index 66c178daa..000000000 --- a/grammars/silver/extension/treegen/Type.sv +++ /dev/null @@ -1,93 +0,0 @@ -grammar silver:extension:treegen; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:concrete_syntax; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:extension:convenience; -imports silver:extension:list; -imports silver:modification:ffi; - -imports silver:modification:collection; - - ---- conventional suffix for function names e.g. "checkEq" ++ t.idNameForGenArb -synthesized attribute idNameForGenArb :: String occurs on Type; - -aspect production varType -top::Type ::= tv::TyVar -{ - top.idNameForGenArb = "VAR"; -} -aspect production skolemType -top::Type ::= tv::TyVar -{ - top.idNameForGenArb = "SKOLEM"; -} -aspect production errorType -top::Type ::= -{ - top.idNameForGenArb = "ERROR"; -} -aspect production intType -top::Type ::= -{ - top.idNameForGenArb = "Integer"; -} -aspect production boolType -top::Type ::= -{ - top.idNameForGenArb = "Boolean"; -} -aspect production floatType -top::Type ::= -{ - top.idNameForGenArb = "Float"; -} -aspect production stringType -top::Type ::= -{ - top.idNameForGenArb = "String"; -} -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - -- ignore parameters, we don't support them for now - top.idNameForGenArb = substring(lastIndexOf(":", fn) + 1, length(fn), fn); -} -aspect production terminalType -top::Type ::= fn::String -{ - top.idNameForGenArb = substring(lastIndexOf(":", fn) + 1, length(fn), fn); -} -aspect production decoratedType -top::Type ::= te::Type -{ - top.idNameForGenArb = "Decorated" ++ te.idNameForGenArb; -} -aspect production ntOrDecType -top::Type ::= nt::Type hidden::Type -{ - -- err, shouldn't happen? - top.idNameForGenArb = "WTFnrOrDecTypeExpr"; -} -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - -- err, shouldn't happen? - top.idNameForGenArb = "FUNCTION"; -} -aspect production listType -top::Type ::= el::Type -{ - top.idNameForGenArb = "List" ++ el.idNameForGenArb; -} -aspect production foreignType -top::Type ::= fn::String transType::String params::[Type] -{ - -- err, shouldn't happen? - top.idNameForGenArb = "FOREIGN"; -} - - diff --git a/grammars/silver/host/DocConfig.sv b/grammars/silver/host/DocConfig.sv deleted file mode 100644 index f127e8d98..000000000 --- a/grammars/silver/host/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:host; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Host\nmenu_title: Host\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/host/Project.sv b/grammars/silver/host/Project.sv deleted file mode 100644 index 2cc9ad779..000000000 --- a/grammars/silver/host/Project.sv +++ /dev/null @@ -1,56 +0,0 @@ -grammar silver:host; - -{- Silver is built as an extensible language with a core "host" language and a - - number of extensions and modifications containing additional features. - - However many of these extensions we typically always want to include by - - when building extended versions of Silver, and it becomes cumbersome to list - - them repeatedly. - - Thus we provide this grammar that exports all the components of the - - "default" Silver host language in one place. - - Note that this list may grow over time. - -} - --- The "core" host language: -exports silver:host:core; - --- Modifications to Silver = optional features that are not pure extensions. --- These are explicitly annotated as "options" within the core host language -exports silver:modification:let_fix; -exports silver:modification:lambda_fn; -exports silver:modification:collection; -exports silver:modification:primitivepattern; -exports silver:modification:autocopyattr; -exports silver:modification:ffi; -exports silver:modification:typedecl; -exports silver:modification:copper; -exports silver:modification:defaultattr; --- slight hacks, for the moment -exports silver:modification:copper_mda; -exports silver:modification:impide; - --- Pure extensions to Silver -exports silver:extension:doc; -exports silver:extension:convenience; -exports silver:extension:list; -- Not really a pure extension, yuck. -exports silver:extension:easyterminal; -exports silver:extension:deprecation; -exports silver:extension:testing; -exports silver:extension:auto_ast; -exports silver:extension:templating; -exports silver:extension:patternmatching; -exports silver:extension:treegen; -exports silver:extension:doc; -exports silver:extension:autoattr; -exports silver:extension:strategyattr; -exports silver:extension:monad; -exports silver:extension:reflection; -exports silver:extension:rewriting; -exports silver:extension:silverconstruction; -exports silver:extension:astconstruction; -exports silver:extension:constructparser; - --- Other generally useful stuff: -exports silver:translation:java; -exports silver:driver; -exports silver:analysis:warnings:flow; -exports silver:analysis:warnings:exporting; diff --git a/grammars/silver/host/core/Project.sv b/grammars/silver/host/core/Project.sv deleted file mode 100644 index 343d8312e..000000000 --- a/grammars/silver/host/core/Project.sv +++ /dev/null @@ -1,28 +0,0 @@ -grammar silver:host:core; - -{- - - This file contains exports for the "core" Silver host language, excluding - - all extensions and modifications. - - Note that the "default" host version of Silver specified in silver:host is - - still required to build this. - -} - --- concrete syntax from these grammars -exports silver:definition:core; -exports silver:definition:concrete_syntax; -exports silver:definition:type:syntax; -exports silver:definition:regex; -exports silver:definition:flow:syntax; - --- symbols -exports silver:analysis:typechecking:core; - ---We wish regex to remain a generic grammar, so we resolve the conflict here! -import silver:definition:regex; -import silver:definition:core; - --- Regexes end with /. Escape it if you want it. -disambiguate RegexChar_t, Divide_t -{ - pluck Divide_t; -} diff --git a/grammars/silver/json/DocConfig.sv b/grammars/silver/json/DocConfig.sv deleted file mode 100644 index 9d42464f7..000000000 --- a/grammars/silver/json/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:json; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Json\nmenu_title: Json\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/json/Json.sv b/grammars/silver/json/Json.sv index 0e53fb4c3..26e228a12 100644 --- a/grammars/silver/json/Json.sv +++ b/grammars/silver/json/Json.sv @@ -3,10 +3,119 @@ -- as silver:json, with proper concrete syntax and everything. grammar silver:json; -nonterminal Json with jsonString; -synthesized attribute json :: Json; +@{- Json is the type of JSON values. -} +nonterminal Json with jsonString, compareTo, isEqual, compareKey, compare; +propagate compareTo, isEqual, compareKey, compare on Json; + +@{- Converts the JSON value to a string. -} synthesized attribute jsonString :: String; +-- TODO: We probably want to also add concrete syntax and a utility function for String -> Json + +@{- + ToJson represents conversion to JSON. + The following should hold when an instance FromJson a also exists: + fromJson(toJson(x)) == right(x) + toJson(fromJson(x).fromRight) == x when fromJson(x).isRight +-} +class ToJson a { + toJson :: (Json ::= a); +} + +@{- + FromJson represents conversion from JSON. + The following should hold when an instance ToJson a also exists: + fromJson(toJson(x)) == right(x) + toJson(fromJson(x).fromRight) == x when fromJson(x).isRight +-} +class FromJson a { + fromJson :: (Either ::= Json); +} + +instance ToJson Json { + toJson = id; +} + +instance FromJson Json { + fromJson = right; +} + +instance ToJson Boolean { + toJson = jsonBoolean; +} + +instance FromJson Boolean { + fromJson = \ j::Json -> + case j of + | jsonBoolean(x) -> right(x) + | _ -> left(s"Expected boolean, got ${j.jsonString}") + end; +} + +instance ToJson Float { + toJson = jsonFloat; +} + +instance FromJson Float { + fromJson = \ j::Json -> + case j of + | jsonFloat(x) -> right(x) + | _ -> left(s"Expected float, got ${j.jsonString}") + end; +} + +instance ToJson Integer { + toJson = jsonInteger; +} + +instance FromJson Integer { + fromJson = \ j::Json -> + case j of + | jsonFloat(x) when x == toFloat(toInteger(x)) -> right(toInteger(x)) + | _ -> left(s"Expected integer, got ${j.jsonString}") + end; +} + +instance ToJson String { + toJson = jsonString; +} + +instance FromJson String { + fromJson = \ j::Json -> + case j of + | jsonString(x) -> right(x) + | _ -> left(s"Expected string, got ${j.jsonString}") + end; +} + +instance ToJson a => ToJson [a] { + toJson = \xs::[a] -> jsonArray(map(toJson, xs)); +} + +instance FromJson a => FromJson [a] { + fromJson = \ j::Json -> + case j of + | jsonArray(x) -> traverseA(fromJson, x) + | _ -> left(s"Expected array, got ${j.jsonString}") + end; +} + +instance ToJson a => ToJson Maybe { + toJson = \mx::Maybe -> + case mx of + | just(x) -> toJson(x) + | nothing() -> jsonNull() + end; +} + +instance FromJson a => FromJson Maybe { + fromJson = \ j::Json -> + case j of + | jsonNull() -> right(nothing()) + | _ -> map(just, fromJson(j)) + end; +} + abstract production jsonString top::Json ::= str::String { @@ -73,10 +182,10 @@ top::Json ::= vals::[Json] } abstract production jsonObject -top::Json ::= vals::[Pair] +top::Json ::= vals::[(String, Json)] { local strs :: [String] = map( - \p::Pair -> jsonString(p.fst).jsonString ++ ":" ++ p.snd.jsonString, + \p::(String, Json) -> jsonString(p.fst).jsonString ++ ":" ++ p.snd.jsonString, vals); top.jsonString = "{" ++ implode(",", strs) ++ "}"; } diff --git a/grammars/silver/langutil/Attributes.sv b/grammars/silver/langutil/Attributes.sv index efbbbd621..3814230b8 100644 --- a/grammars/silver/langutil/Attributes.sv +++ b/grammars/silver/langutil/Attributes.sv @@ -1,20 +1,10 @@ {- A Universal set of common attributes for use in language descriptions -} grammar silver:langutil; -exports silver:langutil:reflect with core:reflect; -- Contains pp definitions for AST +exports silver:langutil:reflect; -- Contains pp definitions for AST import silver:langutil:pp; -{- -This has been "deprecated" for a very long time. We should either not use -this library or get rid of the message. - -I've gotten rid of the message for now. - - -deprecated "This library is not deprecated, but users should be aware it is not stable and very subject to change!"; --} - {-- - The unparse of a syntax tree. -} @@ -38,5 +28,5 @@ synthesized attribute ast :: a; {-- - For accumulating error/warning messages over a syntax tree -} -monoid attribute errors :: [Message] with [], ++; +monoid attribute errors :: [Message]; diff --git a/grammars/silver/langutil/DocConfig.sv b/grammars/silver/langutil/DocConfig.sv deleted file mode 100644 index b042f1430..000000000 --- a/grammars/silver/langutil/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:langutil; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Language Utilities\nmenu_title: Lang Util\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/langutil/Location.sv b/grammars/silver/langutil/Location.sv index 6d668fca3..874305190 100644 --- a/grammars/silver/langutil/Location.sv +++ b/grammars/silver/langutil/Location.sv @@ -1,6 +1,8 @@ grammar silver:langutil; -attribute unparse occurs on Location; +import silver:langutil:pp; + +attribute unparse, pp occurs on Location; aspect production loc top::Location ::= filename::String line::Integer column::Integer @@ -8,10 +10,12 @@ top::Location ::= filename::String line::Integer column::Integer index::Integer endIndex::Integer { top.unparse = filename ++ ":" ++ toString(line) ++ ":" ++ toString(column); + top.pp = text(top.unparse); } aspect production txtLoc -top::Location ::= text::String +top::Location ::= txt::String { - top.unparse = text; + top.unparse = txt; + top.pp = text(txt); } diff --git a/grammars/silver/langutil/Message.sv b/grammars/silver/langutil/Message.sv index 98e83a5e0..7d380edc7 100644 --- a/grammars/silver/langutil/Message.sv +++ b/grammars/silver/langutil/Message.sv @@ -1,30 +1,49 @@ -{- A Universal error/warning message data structure -} grammar silver:langutil; -{-- +@@{- + - @config fileSplit + - + - ## A Universal error/warning message data structure + -} + +@{-- - A Message represents a compiler output message (error/warning) -} -nonterminal Message with message, where, output, severity; +tracked nonterminal Message with message, where, noLocOutput, output, severity; -{-- +@{-- - The location of an error message. -} synthesized attribute where :: Location; -{-- + +@{-- - The contents of the error message. -} synthesized attribute message :: String; -{-- - - A recommended way to turn this message into console output. + +@{-- + - A recommended way to turn this message into console output with location info. -} synthesized attribute output :: String; -{-- + +@{-- + - A recommended way to turn this message into console output without location info. + -} +synthesized attribute noLocOutput :: String; + +@{-- - A convention for determining message severity. - err=2, wrn=1, info=0 -} synthesized attribute severity :: Integer; -{-- +aspect default production +top::Message ::= +{ + top.output = s"${top.where.unparse}: ${top.noLocOutput}"; +} + +@{-- - A error that should halt compilation before translation proceeds on the - compilation unit the error occurs in. -} @@ -33,11 +52,17 @@ top::Message ::= l::Location m::String { top.where = l; top.message = m; - top.output = s"${l.unparse}: error: ${m}"; + top.noLocOutput = s"error: ${m}"; top.severity = 2; } -{-- +function errFromOrigin +Message ::= a::a m::String +{ + return err(getParsedOriginLocationOrFallback(a), m); +} + +@{-- - A warning that is not required to halt compilation before translation - proceeds on the compilation unit the warning occurs in. -} @@ -46,11 +71,17 @@ top::Message ::= l::Location m::String { top.where = l; top.message = m; - top.output = s"${l.unparse}: warning: ${m}"; + top.noLocOutput = s"warning: ${m}"; top.severity = 1; } -{-- +function wrnFromOrigin +Message ::= a::a m::String +{ + return wrn(getParsedOriginLocationOrFallback(a), m); +} + +@{-- - An informational message that does not halt compilation, but is usually - attached to an error or warning. -} @@ -59,11 +90,17 @@ top::Message ::= l::Location m::String { top.where = l; top.message = m; - top.output = s"${l.unparse}: info: ${m}"; + top.noLocOutput = s"info: ${m}"; top.severity = 0; } -{-- +function infoFromOrigin +Message ::= a::a m::String +{ + return info(getParsedOriginLocationOrFallback(a), m); +} + +@{-- - A group of messages. -} abstract production nested @@ -71,14 +108,14 @@ top::Message ::= l::Location m::String others::[Message] { top.where = l; top.message = s"${m}\n${messagesToString(others)}"; - top.output = s"${l.unparse}: ${m}\n${messagesToString(others)}\n"; + top.noLocOutput = s"${top.message}\n"; top.severity = foldr(max, 0, map((.severity), others)); } -- Users can extend Message with more messages (e.g. dbg) as they desire -- map, filter, etc should all be quite useful on messages -{-- +@{-- - Determines if a list has any errors (or, optionally, warnings, too) - Note: user extended messages that forward to err or wrn will have - the same effect, and unknown completely messages will be skipped as @@ -96,7 +133,7 @@ Boolean ::= l::[Message] wError::Boolean end; } -{-- +@{-- - Returns a list of strings, ready to be printed to the command line. -} function messagesToString @@ -106,9 +143,10 @@ String ::= msgs::[Message] } -- for use with sortBy +-- not an instance of Eq/Ord for now, does it really make sense to compare messages for equality? function messageLte Boolean ::= m1::Message m2::Message { - return locationLte(m1.where, m2.where); + return m1.where <= m2.where; } diff --git a/grammars/silver/langutil/lsp/SemanticTokens.sv b/grammars/silver/langutil/lsp/SemanticTokens.sv new file mode 100644 index 000000000..cd4489d0d --- /dev/null +++ b/grammars/silver/langutil/lsp/SemanticTokens.sv @@ -0,0 +1,48 @@ +grammar silver:langutil:lsp; + +@@{- + - These lexer classes correspond to the semantic token types and modifiers as + - specified in the language server protocol - the names match the names + - specified in the protocol, with the first letter made capitalized. + - These names are fairly generic, so this grammar should typically be given a + - prefix when imported, e.g. `import silver:langutil:lsp as lsp;`. + - + - See https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens + -} + +-- Token types +lexer class Namespace; +lexer class Type; +lexer class Class; +lexer class Enum; +lexer class Interface; +lexer class Struct; +lexer class TypeParameter; +lexer class Parameter; +lexer class Variable; +lexer class Property; +lexer class EnumMember; +lexer class Event; +lexer class Function; +lexer class Method; +lexer class Macro; +lexer class Keyword; +lexer class Modifier; +lexer class Comment; +lexer class String_; +lexer class Number; +lexer class Regexp; +lexer class Operator; +lexer class Decorator; + +-- Token modifiers +lexer class Declaration; +lexer class Definition; +lexer class Readonly; +lexer class Static; +lexer class Deprecated; +lexer class Abstract; +lexer class Async; +lexer class Modification; +lexer class Documentation; +lexer class DefaultLibrary; diff --git a/grammars/silver/langutil/pp/Document.sv b/grammars/silver/langutil/pp/Document.sv index 434eba0b9..a79c669f4 100644 --- a/grammars/silver/langutil/pp/Document.sv +++ b/grammars/silver/langutil/pp/Document.sv @@ -1,15 +1,16 @@ grammar silver:langutil:pp; -import silver:util:deque; +imports silver:core with group as groupList; +import silver:util:deque as dq; -function show +function showDoc String ::= width::Integer d::Document { d.indent = 0; d.width = width; d.inPosition = 0; - d.inDq = dqEmpty(); + d.inDq = dq:empty(); d.inCHorizontals = false :: d.horizontals; d.inRemaining = width; @@ -18,7 +19,16 @@ String ::= width::Integer d::Document -------------------------------------------------------------------------------- -{-- +instance Semigroup Document { + append = cat; +} + +instance Monoid Document { + mempty = notext(); + concat = ppConcat; +} + +@{-- - Concatenates a list of fragments into one fragment. -} function ppConcat @@ -27,7 +37,8 @@ Document ::= ds::[Document] return if null(ds) then notext() else foldl(cat, head(ds), tail(ds)); } -{-- + +@{-- - Intersperse a separator fragment between a list of fragments. - e.g. implode(text(", "), list) -} @@ -38,7 +49,8 @@ Document ::= sep::Document ds::[Document] else if null(tail(ds)) then head(ds) else cat(cat(head(ds), sep), ppImplode(sep, tail(ds))); } -{-- + +@{-- - Introduce a separator fragment after every element of a list of fragments. - Including the last. -} @@ -49,7 +61,8 @@ Document ::= sep::Document ds::[Document] then notext() else cat(cat(head(ds), sep), terminate(sep, tail(ds))); } -{-- + +@{-- - Introduce a separator fragment before every element of a list of fragments. - Including the first. -} @@ -60,7 +73,8 @@ Document ::= sep::Document ds::[Document] then notext() else cat(cat(sep, head(ds)), initiate(sep, tail(ds))); } -{-- + +@{-- - Insert lines before and after the inner fragment, with proper nesting. - (That is, usually you want the first line inside the nest, but the second - OUTSIDE the nest.) @@ -78,7 +92,7 @@ Document ::= n::Integer inner::Document function groupnestlines Document ::= n::Integer inner::Document { - return cat(groupnest(n, cat(line(), inner)), line()); + return group(cat(nest(n, cat(line(), inner)), line())); } function softbreak Document ::= @@ -118,7 +132,7 @@ Document ::= d::Document return cat(cat(text("["), d), text("]")); } -{- Below this line: +@@{- Below this line: text Document ::= String cat Document ::= Document Document line Document ::= @@ -133,16 +147,17 @@ realLine Document ::= nonterminal Document with indent, width, inPosition, inDq, inCHorizontals, inRemaining, outPosition, outDq, outCHorizontals, outRemaining, - result, horizontals; + result, horizontals, + compareTo, isEqual; autocopy attribute indent :: Integer; autocopy attribute width :: Integer; -- The scanning process inherited attribute inPosition :: Integer; -inherited attribute inDq :: Deque>; +inherited attribute inDq :: dq:Deque>; synthesized attribute outPosition :: Integer; -synthesized attribute outDq :: Deque>; +synthesized attribute outDq :: dq:Deque>; synthesized attribute horizontals :: [Boolean]; -- output of scanning process @@ -154,7 +169,10 @@ synthesized attribute outRemaining :: Integer; synthesized attribute result :: String; -- output of printing process -{- +-- Strict equality of Document terms, for testing purposes +propagate compareTo, isEqual on Document; + +@@{- Some notes on deciphering all this: in/out Position is some hypothetical value that's part of the "scanning process" @@ -176,13 +194,13 @@ meant to be consumed by a pre-order scan of the tree. -} -{-- +@{-- - Literal text. (Do not use with newlines!) -} abstract production text top::Document ::= s::String { - local pr :: Pair> [Boolean]> = prune(top.outPosition, top.inDq); + local pr :: Pair> [Boolean]> = prune(top.outPosition, top.inDq); top.outPosition = top.inPosition + length(s); top.outDq = pr.fst; @@ -193,7 +211,7 @@ top::Document ::= s::String top.horizontals = pr.snd; } -{-- +@{-- - Concatenate two documents. -} abstract production cat @@ -218,14 +236,14 @@ top::Document ::= d1::Document d2::Document top.horizontals = d1.horizontals ++ d2.horizontals; } -{-- +@{-- - Either a space, or a linebreak plus indentation. - The behavior of EVERY line in a group is identical. -} abstract production line top::Document ::= { - local pr :: Pair> [Boolean]> = prune(top.outPosition, top.inDq); + local pr :: Pair> [Boolean]> = prune(top.outPosition, top.inDq); local horizontal :: Boolean = head(top.inCHorizontals); top.outPosition = top.inPosition + 1; @@ -237,7 +255,7 @@ top::Document ::= top.horizontals = pr.snd; } -{-- +@{-- - Does nothing but control the behavior of all lines that have this group - as their closest enclosing group. -} @@ -251,7 +269,7 @@ top::Document ::= d::Document d.inCHorizontals = tail(top.inCHorizontals); d.inRemaining = top.inRemaining; - local le :: Pair> [Boolean]> = leave(d.outPosition, d.outDq); + local le :: Pair> [Boolean]> = leave(d.outPosition, d.outDq); top.outPosition = d.outPosition; top.outDq = le.fst; @@ -264,7 +282,7 @@ top::Document ::= d::Document top.horizontals = d.horizontals ++ le.snd; } -{-- +@{-- - Increase the indentation level (but does not directly indent itself!) -} abstract production nest @@ -304,33 +322,33 @@ top::Document ::= -------------------------------------------------------------------------------- function prune -Pair> [Boolean]> ::= p::Integer q::Deque> +Pair> [Boolean]> ::= p::Integer q::dq:Deque> { - return if dqIsEmpty(q) then pair(q, []) - else let h::Pair = dqHead(q) + return if dq:isEmpty(q) then pair(q, []) + else let h::Pair = dq:head(q) in if p <= h.fst then pair(q, []) - else let recur::Pair> [Boolean]> = prune(p, dqTail(q)) + else let recur::Pair> [Boolean]> = prune(p, dq:tail(q)) in pair(recur.fst, false :: (h.snd ++ recur.snd)) end end; } function enter -Deque> ::= p::Integer q::Deque> +dq:Deque> ::= p::Integer q::dq:Deque> { - return dqSnoc(q, pair(p, [])); + return dq:snoc(q, pair(p, [])); } function leave -Pair> [Boolean]> ::= p::Integer q::Deque> +Pair> [Boolean]> ::= p::Integer q::dq:Deque> { - return if dqIsEmpty(q) then pair(q, []) - else let h1::Pair = dqLast(q), - t1::Deque> = dqInit(q) - in if dqIsEmpty(t1) then pair(t1, true :: h1.snd) - else let h2::Pair = dqLast(t1), - t2::Deque> = dqInit(t1) - in pair(dqSnoc(t2, pair(h2.fst, h2.snd ++ [p <= h1.fst] ++ h1.snd)), []) + return if dq:isEmpty(q) then pair(q, []) + else let h1::Pair = dq:last(q), + t1::dq:Deque> = dq:init(q) + in if dq:isEmpty(t1) then pair(t1, true :: h1.snd) + else let h2::Pair = dq:last(t1), + t2::dq:Deque> = dq:init(t1) + in pair(dq:snoc(t2, pair(h2.fst, h2.snd ++ [p <= h1.fst] ++ h1.snd)), []) end end; } diff --git a/grammars/silver/langutil/pp/Show.sv b/grammars/silver/langutil/pp/Show.sv new file mode 100644 index 000000000..087de87d0 --- /dev/null +++ b/grammars/silver/langutil/pp/Show.sv @@ -0,0 +1,103 @@ +grammar silver:langutil:pp; + +import silver:langutil only pp; + +@{- +Show represents types that can be rendered as formatted (Document) strings. + +For nonterminals, this class should typically be implemented by defining the pp attribute. + +Minimal complete definition: pp +-} +class Show a { + @{- + Pretty-print a value of type a into a Document representation. + -} + pp :: (Document ::= a); + + @{- + Pretty-print a value of type a as a String with a desired maximum line width. + -} + show :: (String ::= Integer a) = + \ width::Integer x::a -> showDoc(width, pp(x)); +} + +-- Default instances using the pp attribute on undecorated and decorated nonterminal types +instance attribute pp {} occurs on a => Show a { + pp = (.pp); +} + +instance attribute pp i occurs on a => Show Decorated a with i { + pp = (.pp); +} + +-- Instance provided for backwards compatability, so that show(80, foo.pp) is still equivalent to show(80, foo). +instance Show Document { + pp = id; +} + +instance Show Decorated Document with i { + pp = new; +} + +-- Instances for primitive types +instance Show Integer { + pp = \ x::Integer -> text(toString(x)); +} + +instance Show Float { + pp = \ x::Float -> text(toString(x)); +} + +instance Show Boolean { + pp = \ x::Boolean -> text(toString(x)); +} + +instance Show String { + pp = \ x::String -> text("\"" ++ escapeString(x) ++ "\""); +} + +instance Show a => Show [a] { + pp = \ xs::[a] -> pp"[${ppImplode(pp", ", map(pp, xs))}]"; +} + +-- tuples +instance Show a, ShowTuple b => Show (a, b) { + pp = \ x::(a, b) -> pp"(${pp(x.fst)}, ${ppTuple(x.snd)})"; +} + +class ShowTuple a { + ppTuple :: (Document ::= a); +} + +instance Show a, ShowTuple b => ShowTuple (a, b) { + ppTuple = \ x::(a, b) -> pp"${pp(x.fst)}, ${ppTuple(x.snd)}"; +} + +-- This instance would normally be disallowed; we are exempting it in silver/definition/type/syntax/Constraint.sv +-- since we know it is safe - this is like enabling the UndecidableInstances extension in Haskell +instance Show a => ShowTuple a { + ppTuple = pp; +} + +instance Show () { + pp = \ () -> pp"()"; +} + +-- Other standard lib types. +-- Note that these can't be done with attributes due to the polymorphic children requiring a Show context. +instance Show a => Show Maybe { + pp = \ x::Maybe -> + case x of + | just(y) -> pp"just(${pp(y)})" + | nothing() -> pp"nothing()" + end; +} + +instance Show a, Show b => Show Either { + pp = \ x::Either -> + case x of + | left(y) -> pp"left(${pp(y)})" + | right(y) -> pp"right(${pp(y)})" + end; +} diff --git a/grammars/silver/langutil/reflect/AST.sv b/grammars/silver/langutil/reflect/AST.sv index a908448be..e1db3b14e 100644 --- a/grammars/silver/langutil/reflect/AST.sv +++ b/grammars/silver/langutil/reflect/AST.sv @@ -1,6 +1,6 @@ grammar silver:langutil:reflect; -imports silver:reflect; +imports silver:reflect:util; imports silver:langutil; imports silver:langutil:pp; diff --git a/grammars/silver/metatranslation/Translation.sv b/grammars/silver/metatranslation/Translation.sv deleted file mode 100644 index 353ffbefe..000000000 --- a/grammars/silver/metatranslation/Translation.sv +++ /dev/null @@ -1,370 +0,0 @@ -grammar silver:metatranslation; - -imports silver:reflect; -imports silver:langutil:pp; -imports core:monad; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type:syntax; -imports silver:extension:list; -imports silver:extension:patternmatching; - -function translate -Expr ::= loc::Location ast::AST -{ - ast.givenLocation = loc; - return ast.translation; -} - -function translatePattern -Pattern ::= loc::Location ast::AST -{ - ast.givenLocation = loc; - return ast.patternTranslation; -} - -synthesized attribute translation::a; -synthesized attribute patternTranslation::a; -synthesized attribute foundLocation::Maybe; -autocopy attribute givenLocation::Location; - -flowtype translation {givenLocation} on AST, ASTs, NamedASTs, NamedAST; -flowtype patternTranslation {givenLocation} on AST, ASTs; -flowtype foundLocation {} on ASTs, NamedASTs, NamedAST; - -attribute givenLocation, translation, patternTranslation occurs on AST; - -aspect production nonterminalAST -top::AST ::= prodName::String children::ASTs annotations::NamedASTs -{ - production givenLocation::Location = - fromMaybe(top.givenLocation, orElse(children.foundLocation, annotations.foundLocation)); - - production attribute antiquoteTranslation::Maybe with orElse; - antiquoteTranslation := nothing(); - - -- "Direct" antiquote productions - production attribute directAntiquoteProductions::[String] with ++; - directAntiquoteProductions := []; - antiquoteTranslation <- - if containsBy(stringEq, prodName, directAntiquoteProductions) - then - let wrapped::AST = - case children of - | consAST(a, nilAST()) -> a - | consAST( - terminalAST(_, _, _), - consAST( - terminalAST(_, _, _), - consAST( - a, - consAST( - terminalAST(_, _, _), - nilAST())))) -> a - | _ -> error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") - end - in - case reify(wrapped) of - | right(e) -> just(e) - | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") - end - end - else nothing(); - - -- "Collection" antiquote productions - -- Key: antiquote production name - -- Value: pair(nonterminal short name, pair(cons production name, append production name)) - production attribute collectionAntiquoteProductions::[Pair>>] with ++; - collectionAntiquoteProductions := []; - antiquoteTranslation <- - do (bindMaybe, returnMaybe) { - -- pair(antiquote production name, antiquote expr AST, rest AST) - antiquote::Pair> <- - case children of - | consAST( - nonterminalAST(n, consAST(a, _), _), - consAST(rest, nilAST())) -> just(pair(n, pair(a, rest))) - | _ -> nothing() - end; - -- pair(nonterminal short name, pair(cons production name, append production name)) - trans::Pair> <- - lookupBy(stringEq, antiquote.fst, collectionAntiquoteProductions); - if prodName == trans.snd.fst then just(unit()) else nothing(); -- require prodName == trans.snd.fst - return - case reify(antiquote.snd.fst) of - | right(e) -> - mkStrFunctionInvocation( - givenLocation, trans.snd.snd, [e, antiquote.snd.snd.translation]) - | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") - end; - }; - antiquoteTranslation <- - do (bindMaybe, returnMaybe) { - -- pair(nonterminal short name, pair(cons production name, append production name)) - trans::Pair> <- - lookupBy(stringEq, prodName, collectionAntiquoteProductions); - return - errorExpr([err(givenLocation, s"$$${trans.fst} may only occur as a member of ${trans.fst}")], location=givenLocation); - }; - - antiquoteTranslation <- - if containsBy(stringEq, prodName, patternAntiquoteProductions) - then just(errorExpr([err(givenLocation, "Pattern antiquote is invalid in expression context")], location=givenLocation)) - else nothing(); - - top.translation = - fromMaybe( - mkFullFunctionInvocation( - givenLocation, - baseExpr(qName(givenLocation, prodName), location=givenLocation), - children.translation, - annotations.translation), - antiquoteTranslation); - - production attribute patternAntiquoteTranslation::Maybe with orElse; - patternAntiquoteTranslation := nothing(); - - production attribute patternAntiquoteProductions::[String] with ++; - patternAntiquoteProductions := []; - patternAntiquoteTranslation <- - if containsBy(stringEq, prodName, patternAntiquoteProductions) - then - let wrapped::AST = - case children of - | consAST(a, nilAST()) -> a - | consAST(terminalAST(_, _, _), consAST(a, nilAST())) -> a - | consAST( - terminalAST(_, _, _), - consAST( - terminalAST(_, _, _), - consAST( - a, - consAST( - terminalAST(_, _, _), - nilAST())))) -> a - | _ -> error(s"Unexpected antiquote production arguments: ${show(80, top.pp)}") - end - in - case reify(wrapped) of - | right(p) -> just(p) - | left(msg) -> error(s"Error in reifying child of production ${prodName}:\n${msg}") - end - end - else nothing(); - - patternAntiquoteTranslation <- - if containsBy(stringEq, prodName, directAntiquoteProductions ++ map(fst, collectionAntiquoteProductions)) - then just(errorPattern([err(givenLocation, "Expression antiquote is invalid in pattern context")], location=givenLocation)) - else nothing(); - - -- Note that we intentionally ignore annotations here - top.patternTranslation = - fromMaybe( - prodAppPattern( - qName(givenLocation, prodName), - '(', - children.patternTranslation, - ')', - location=givenLocation), - patternAntiquoteTranslation); - - children.givenLocation = givenLocation; - annotations.givenLocation = givenLocation; -} - -aspect production terminalAST -top::AST ::= terminalName::String lexeme::String location::Location -{ - local locationAST::AST = reflect(new(location)); - locationAST.givenLocation = top.givenLocation; - - top.translation = - terminalConstructor( - 'terminal', '(', - nominalTypeExpr( - makeQNameType(terminalName, top.givenLocation), botlNone(location=top.givenLocation), - location=top.givenLocation), - ',', - stringConst( - terminal(String_t, s"\"${escapeString(lexeme)}\"", top.givenLocation), - location=top.givenLocation), - ',', - locationAST.translation, - ')', location=top.givenLocation); - - -- TODO: What to do here- warn about this maybe? - -- Shouldn't really be an issue unless matching against concrete syntax containing non-fixed terminals - top.patternTranslation = wildcPattern('_', location=top.givenLocation); -} - -aspect production listAST -top::AST ::= vals::ASTs -{ - top.translation = - fullList( - '[', - foldr( - exprsCons(_, ',', _, location=top.givenLocation), - exprsEmpty(location=top.givenLocation), - vals.translation), - ']', - location=top.givenLocation); - top.patternTranslation = - listPattern('[', vals.patternTranslation, ']', location=top.givenLocation); -} - -aspect production stringAST -top::AST ::= s::String -{ - top.translation = - stringConst( - terminal(String_t, s"\"${escapeString(s)}\"", top.givenLocation), - location=top.givenLocation); - top.patternTranslation = - strPattern( - terminal(String_t, s"\"${escapeString(s)}\"", top.givenLocation), - location=top.givenLocation); -} - -aspect production integerAST -top::AST ::= i::Integer -{ - top.translation = - intConst(terminal(Int_t, toString(i), top.givenLocation), location=top.givenLocation); - top.patternTranslation = - intPattern(terminal(Int_t, toString(i), top.givenLocation), location=top.givenLocation); -} - -aspect production floatAST -top::AST ::= f::Float -{ - top.translation = - floatConst(terminal(Float_t, toString(f), top.givenLocation), location=top.givenLocation); - top.patternTranslation = - fltPattern(terminal(Float_t, toString(f), top.givenLocation), location=top.givenLocation); -} - -aspect production booleanAST -top::AST ::= b::Boolean -{ - top.translation = - if b - then trueConst('true', location=top.givenLocation) - else falseConst('false', location=top.givenLocation); - top.patternTranslation = - if b - then truePattern('true', location=top.givenLocation) - else falsePattern('false', location=top.givenLocation); -} - -aspect production anyAST -top::AST ::= x::a -{ - top.translation = - case reflectTypeName(x) of - just(n) -> error(s"Can't translate anyAST (type ${n})") - | nothing() -> error("Can't translate anyAST") - end; - top.patternTranslation = - case reflectTypeName(x) of - just(n) -> error(s"Can't translate anyAST (type ${n})") - | nothing() -> error("Can't translate anyAST") - end; -} - -attribute givenLocation, translation<[Expr]>, patternTranslation, foundLocation occurs on ASTs; - -aspect production consAST -top::ASTs ::= h::AST t::ASTs -{ - top.translation = h.translation :: t.translation; - top.patternTranslation = - patternList_more(h.patternTranslation, ',', t.patternTranslation, location=top.givenLocation); - top.foundLocation = - -- Try to reify the last child as a location - case t of - | nilAST() -> - case reify(h) of - | right(l) -> just(l) - | left(_) -> nothing() - end - | _ -> t.foundLocation - end; -} - -aspect production nilAST -top::ASTs ::= -{ - top.translation = []; - top.patternTranslation = patternList_nil(location=top.givenLocation); - top.foundLocation = nothing(); -} - -attribute givenLocation, translation<[Pair]>, foundLocation occurs on NamedASTs; - -aspect production consNamedAST -top::NamedASTs ::= h::NamedAST t::NamedASTs -{ - top.translation = h.translation :: t.translation; - top.foundLocation = orElse(h.foundLocation, t.foundLocation); -} - -aspect production nilNamedAST -top::NamedASTs ::= -{ - top.translation = []; - top.foundLocation = nothing(); -} - -attribute givenLocation, translation>, foundLocation occurs on NamedAST; - -aspect production namedAST -top::NamedAST ::= n::String v::AST -{ - top.translation = - -- hack to get annotation shortname - pair(last(explode(":", n)), v.translation); - top.foundLocation = - if n == "core:location" - then - case reify(v) of - | right(l) -> just(l) - | left(msg) -> error(s"Error in reifying location:\n${msg}") - end - else nothing(); -} - --- the functions below are directly referenced in reflection code in silver:extensions:silverconstruction --- so make sure you grep for that if you change/move them. - -function makeName -Name ::= n::String loc::Location -{ - return - if isUpper(head(explode("", n))) - then nameIdUpper(terminal(IdUpper_t, n, loc), location=loc) - else nameIdLower(terminal(IdLower_t, n, loc), location=loc); -} - -function makeQName -QName ::= n::String loc::Location -{ - local ns::[Name] = map(makeName(_, loc), explode(":", n)); - return - foldr( - qNameCons(_, ':', _, location=loc), - qNameId(last(ns), location=loc), - init(ns)); -} - -function makeQNameType -QNameType ::= n::String loc::Location -{ - local ns::[String] = explode(":", n); - return - foldr( - qNameTypeCons(_, ':', _, location=loc), - qNameTypeId(terminal(IdUpper_t, last(ns), loc), location=loc), - map(makeName(_, loc), init(ns))); -} diff --git a/grammars/silver/modification/DocConfig.sv b/grammars/silver/modification/DocConfig.sv deleted file mode 100644 index 84552ca4c..000000000 --- a/grammars/silver/modification/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Modification\nmenu_title: Modification\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/autocopyattr/DclInfo.sv b/grammars/silver/modification/autocopyattr/DclInfo.sv deleted file mode 100644 index 412640503..000000000 --- a/grammars/silver/modification/autocopyattr/DclInfo.sv +++ /dev/null @@ -1,39 +0,0 @@ -grammar silver:modification:autocopyattr; - -synthesized attribute isAutocopy :: Boolean occurs on DclInfo; - -aspect default production -top::DclInfo ::= -{ - top.isAutocopy = false; -} - -abstract production autocopyDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - - top.isInherited = true; - top.isAutocopy = true; - - -- the core dispatchers - top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: should probably be an error handler! - top.attrDefDispatcher = inheritedAttributeDef(_, _, _, location=_); - - forwards to inhDcl(sg,sl,fn,bound,ty); -} - --- Defs: - -function autocopyDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return attrDef(defaultEnvItem(autocopyDcl(sg,sl,fn,bound,ty))); -} - diff --git a/grammars/silver/modification/autocopyattr/DocConfig.sv b/grammars/silver/modification/autocopyattr/DocConfig.sv deleted file mode 100644 index 88a34df3f..000000000 --- a/grammars/silver/modification/autocopyattr/DocConfig.sv +++ /dev/null @@ -1,5 +0,0 @@ -grammar silver:modification:autocopyattr; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Autocopy Attribute\nmenu_title: Autocopy Attribute\nmenu_weight: 100\n---" -@} diff --git a/grammars/silver/modification/autocopyattr/Project.sv b/grammars/silver/modification/autocopyattr/Project.sv deleted file mode 100644 index 242c26ced..000000000 --- a/grammars/silver/modification/autocopyattr/Project.sv +++ /dev/null @@ -1,10 +0,0 @@ -grammar silver:modification:autocopyattr; - -imports silver:definition:env; -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:type:syntax; - -exports silver:modification:autocopyattr:java with silver:translation:java:core; -exports silver:modification:autocopyattr:convenience with silver:extension:convenience; - diff --git a/grammars/silver/modification/autocopyattr/convenience/Convenience.sv b/grammars/silver/modification/autocopyattr/convenience/Convenience.sv deleted file mode 100644 index e514cf346..000000000 --- a/grammars/silver/modification/autocopyattr/convenience/Convenience.sv +++ /dev/null @@ -1,18 +0,0 @@ -grammar silver:modification:autocopyattr:convenience; - -import silver:modification:autocopyattr; -import silver:extension:convenience; -import silver:definition:core; -import silver:definition:concrete_syntax; -import silver:definition:type:syntax; -import silver:definition:type; -import silver:definition:env; - -concrete production attributeDclAutoMultiple -top::AGDcl ::= 'autocopy' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'occurs' 'on' qs::QNames ';' -{ - top.unparse = "autocopy attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " occurs on " ++ qs.unparse ++ ";" ; - forwards to appendAGDcl(attributeDclAuto($1, $2, a, tl, $5, te, $10, location=a.location), - makeOccursDclsHelp($1.location, qNameWithTL(qNameId(a, location=a.location), tl), qs.qnames), location=top.location); -} - diff --git a/grammars/silver/modification/autocopyattr/java/Autocopy.sv b/grammars/silver/modification/autocopyattr/java/Autocopy.sv deleted file mode 100644 index e4d3e3493..000000000 --- a/grammars/silver/modification/autocopyattr/java/Autocopy.sv +++ /dev/null @@ -1,51 +0,0 @@ -grammar silver:modification:autocopyattr:java; -import silver:modification:autocopyattr; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type:syntax; -import silver:definition:type; - -import silver:translation:java:core; -import silver:translation:java:type; - -import silver:util; - - -aspect production attributeDclAuto -top::AGDcl ::= 'autocopy' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - local attribute className :: String; - className = "D" ++ a.name; - - top.genFiles := [pair(className ++ ".java", - -"package " ++ makeName(top.grammarName) ++ ";\n\n" ++ - -"import java.util.*;\n\n" ++ - -"public class " ++ className ++ " extends common.Decorator {\n\n" ++ - -"public static final " ++ className ++ " singleton = new " ++ className ++ "();\n\n" ++ - -"\tpublic void decorate(Class production) {\n" ++ -"\t\tdecorateAutoCopy(production, \"" ++ fName ++ "\");\n" ++ -"\t}\n" ++ -"}\n")]; -} - -aspect production attributionDcl -top::AGDcl ::= 'attribute' at::QName attl::BracketedOptTypeExprs 'occurs' 'on' nt::QName nttl::BracketedOptTypeExprs ';' -{ - top.setupInh <- - if at.lookupAttribute.dcl.isAutocopy then - "\t\t" ++ makeNTClassName(nt.lookupType.fullName) ++ ".decorators.add(" ++ makeDecoratorClassName(at.lookupAttribute.fullName) ++ ".singleton);\n" - else ""; -} - -function makeDecoratorClassName -String ::= s::String -{ - return substituteLast(".", ".D", makeName(s)); -} - diff --git a/grammars/silver/modification/collection/Collection.sv b/grammars/silver/modification/collection/Collection.sv deleted file mode 100644 index f6c09c7d0..000000000 --- a/grammars/silver/modification/collection/Collection.sv +++ /dev/null @@ -1,391 +0,0 @@ -grammar silver:modification:collection; - -import silver:definition:type:syntax; -import silver:extension:list; -import silver:util; - ---import silver:analysis:typechecking:core; - -nonterminal NameOrBOperator with config, location, grammarName, errors, env, unparse, operation, operatorForType; -nonterminal Operation; - -synthesized attribute operation :: Operation; -inherited attribute operatorForType :: Type; - -concrete production nameOperator -top::NameOrBOperator ::= q::QName -{ - top.unparse = q.unparse; - - top.operation = case q.lookupValue.dcl of - | funDcl(_,_,_) -> functionOperation(q.lookupValue.fullName) - | prodDcl(_,_,_,_) -> productionOperation(q.lookupValue.fullName) - | _ -> error("INTERNAL ERROR: operation attribute demanded for non-function or production.") - end; - - top.errors := q.lookupValue.errors; - - local checkOperationType :: TypeCheck = - check(freshenCompletely(q.lookupValue.typerep), - functionType(top.operatorForType, [top.operatorForType, top.operatorForType], [])); - checkOperationType.downSubst = emptySubst(); - checkOperationType.finalSubst = checkOperationType.upSubst; - - local operationErrors :: [Message] = - if !checkOperationType.typeerror then [] - else [err(top.location, q.name ++ " must be of type " ++ checkOperationType.rightpp ++ - " instead it is of type " ++ checkOperationType.leftpp)]; - - top.errors <- if !q.lookupValue.found then [] else - case q.lookupValue.dcl of - | funDcl(_,_,_) -> operationErrors - | prodDcl(_,_,_,_) -> operationErrors - | _ -> [err(top.location, q.name ++ " is not a valid operator for collections.")] - end; -} - -concrete production plusplusOperator -top::NameOrBOperator ::= '++' -{ - top.unparse = "++"; - - top.operation = case top.operatorForType of - | stringType() -> plusPlusOperationString() - | listType(_) -> plusPlusOperationList() - | _ -> error("INTERNAL ERROR: operation attribute demanded for ++ that isn't string or list.") - end; - top.errors := case top.operatorForType of - | stringType() -> [] - | listType(_) -> [] - | _ -> [err(top.location, "++ operator will only work for collections of type list or String")] - end; -} - -concrete production borOperator -top::NameOrBOperator ::= '||' -{ - top.unparse = "||"; - - top.operation = borOperation(); - top.errors := case top.operatorForType of - | boolType() -> [] - | _ -> [err(top.location, "|| operator will only work for collections of type Boolean")] - end; -} -concrete production bandOperator -top::NameOrBOperator ::= '&&' -{ - top.unparse = "&&"; - - top.operation = bandOperation(); - top.errors := case top.operatorForType of - | boolType() -> [] - | _ -> [err(top.location, "&& operator will only work for collections of type Boolean")] - end; -} - -abstract production functionOperation -top::Operation ::= s::String -{ -} -abstract production productionOperation -top::Operation ::= s::String -{ -} -abstract production plusPlusOperationString -top::Operation ::= -{ -} -abstract production plusPlusOperationList -top::Operation ::= -{ -} -abstract production borOperation -top::Operation ::= -{ -} -abstract production bandOperation -top::Operation ::= -{ -} - ---- Declarations --------------------------------------------------------------- -concrete production collectionAttributeDclSyn -top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - top.unparse = "synthesized attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - q.operatorForType = te.typerep; - - top.defs := [synColDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep, q.operation)]; - - propagate errors, flowDefs; - - top.errors <- tl.errorsTyVars; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; -} - -concrete production collectionAttributeDclInh -top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - top.unparse = "inherited attribute " ++ a.name ++ tl.unparse ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ a.name; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - q.operatorForType = te.typerep; - - top.defs := [inhColDef(top.grammarName, a.location, fName, tl.freeVariables, te.typerep, q.operation)]; - - propagate errors, flowDefs; - - top.errors <- tl.errorsTyVars; - - top.errors <- - if length(getAttrDclAll(fName, top.env)) > 1 - then [err(a.location, "Attribute '" ++ fName ++ "' is already bound.")] - else []; -} - - -concrete production collectionAttributeDclProd -top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - top.unparse = "production attribute " ++ a.name ++ " :: " ++ te.unparse ++ " with " ++ q.unparse ++ " ;" ; - - top.productionAttributes := [localColDef(top.grammarName, a.location, fName, te.typerep, q.operation)]; - - production attribute fName :: String; - fName = top.frame.fullName ++ ":local:" ++ a.name; - - top.defs := []; - - propagate errors; - - top.errors <- - if length(getValueDclAll(fName, top.env)) > 1 - then [err(a.location, "Value '" ++ fName ++ "' is already bound.")] - else []; - - q.operatorForType = te.typerep; - - forwards to productionAttributeDcl($1, $2, a, $4, te, $8, location=top.location); -} - ---- The use semantics ---------------------------------------------------------- - --- ERROR ON VALUE DEFS: -abstract production errorCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.errors <- [err(top.location, "The ':=' and '<-' operators can only be used for collections. " ++ val.name ++ " is not a collection.")]; - - -- TODO: this production also produces an error message, so we'll produce two errors for one flaw. - -- We don't want to use := for the errors, because we'd miss any errors in e, and we don't want to repeat - -- it because that will produce duplicate trees. - forwards to errorValueDef(val, e, location=top.location); -} -abstract production errorColNormalValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.errors <- [err(top.location, val.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")]; - - -- TODO: same problem - forwards to errorValueDef(val, e, location=top.location); -} - --- NON-ERRORS for PRODUCTIONS - -abstract production baseCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.unparse = "\t" ++ val.unparse ++ " := " ++ e.unparse ++ ";"; - - e.downSubst = top.downSubst; - -- the real type checking is done by the forward, but we must ensure things are tied up nicely - -- otherwise we don't specialize ntOrDecs in OUR e - forward.downSubst = unifyCheck(val.lookupValue.typerep, e.typerep, e.upSubst); - - forwards to localValueDef(val, e, location=top.location); -} -abstract production appendCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.unparse = "\t" ++ val.unparse ++ " <- " ++ e.unparse ++ ";"; - - e.downSubst = top.downSubst; - -- the real type checking is done by the forward, but we must ensure things are tied up nicely - -- otherwise we don't specialize ntOrDecs in OUR e - forward.downSubst = unifyCheck(val.lookupValue.typerep, e.typerep, e.upSubst); - - forwards to localValueDef(val, e, location=top.location); -} - --- NON-ERRORS for SYN ATTRS - -abstract production synBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; - - top.errors := e.errors; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} -abstract production synAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; - - top.errors := e.errors; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} - --- NON-ERRORS for INHERITED ATTRS - -abstract production inhBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; - - top.errors := e.errors; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} -abstract production inhAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; - - top.errors := e.errors; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - errCheck1 = check(attr.typerep, e.typerep); - top.errors <- - if errCheck1.typeerror - then [err(top.location, "Attribute " ++ attr.name ++ " has type " ++ errCheck1.leftpp ++ " but the expression being assigned to it has type " ++ errCheck1.rightpp)] - else []; -} - --- The use syntax -------------------------------------------------------------- - -terminal Contains_t '<-' lexer classes {SPECOP}; -terminal BaseContains_t ':=' lexer classes {SPECOP}; - -concrete production attrContainsAppend -top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur '<-' e::Expr ';' -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ ";"; - - -- defs must stay here explicitly, because we dispatch on types in the forward here! - top.productionAttributes := []; - top.defs := []; - - dl.defLHSattr = attr; - attr.attrFor = dl.typerep; - - forwards to - if !dl.found || !attr.found - then errorAttributeDef(dl.errors ++ attr.errors, dl, attr, e, location=top.location) - else attr.attrDcl.attrAppendDefDispatcher(dl, attr, e, top.location); -} - -concrete production attrContainsBase -top::ProductionStmt ::= dl::DefLHS '.' attr::QNameAttrOccur ':=' e::Expr ';' -{ - top.unparse = "\t" ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ ";"; - - -- defs must stay here explicitly, because we dispatch on types in the forward here! - top.productionAttributes := []; - top.defs := []; - - dl.defLHSattr = attr; - attr.attrFor = dl.typerep; - - forwards to - if !dl.found || !attr.found - then errorAttributeDef(dl.errors ++ attr.errors, dl, attr, e, location=top.location) - else attr.attrDcl.attrBaseDefDispatcher(dl, attr, e, top.location); -} - -concrete production valContainsAppend -top::ProductionStmt ::= val::QName '<-' e::Expr ';' -{ - top.unparse = val.unparse ++ " <- " ++ e.unparse ++ ";"; - - top.errors <- val.lookupValue.errors; - - top.productionAttributes := []; - top.defs := []; - - forwards to if null(val.lookupValue.dcls) - then errorValueDef(val, e, location=top.location) - else val.lookupValue.dcl.appendDefDispatcher(val, e, top.location); -} - -concrete production valContainsBase -top::ProductionStmt ::= val::QName ':=' e::Expr ';' -{ - top.unparse = val.unparse ++ " := " ++ e.unparse ++ ";"; - - top.errors <- val.lookupValue.errors; - - top.productionAttributes := []; - top.defs := []; - - forwards to if null(val.lookupValue.dcls) - then errorValueDef(val, e, location=top.location) - else val.lookupValue.dcl.baseDefDispatcher(val, e, top.location); -} - diff --git a/grammars/silver/modification/collection/DclInfo.sv b/grammars/silver/modification/collection/DclInfo.sv deleted file mode 100644 index cadc0a511..000000000 --- a/grammars/silver/modification/collection/DclInfo.sv +++ /dev/null @@ -1,113 +0,0 @@ -grammar silver:modification:collection; - -attribute operation, attrBaseDefDispatcher, attrAppendDefDispatcher, baseDefDispatcher, appendDefDispatcher occurs on DclInfo; - -synthesized attribute attrBaseDefDispatcher :: (ProductionStmt ::= Decorated DefLHS Decorated QNameAttrOccur Expr Location); -synthesized attribute attrAppendDefDispatcher :: (ProductionStmt ::= Decorated DefLHS Decorated QNameAttrOccur Expr Location); - -synthesized attribute baseDefDispatcher :: (ProductionStmt ::= Decorated QName Expr Location); -synthesized attribute appendDefDispatcher :: (ProductionStmt ::= Decorated QName Expr Location); - -aspect default production -top::DclInfo ::= -{ - top.operation = error("Internal compiler error: must be defined for all collection attribute declarations"); - - top.attrBaseDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, "The ':=' operator can only be used for collections. " ++ attr.name ++ " is not a collection.")], dl, attr, e, location=l); - top.attrAppendDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, "The '<-' operator can only be used for collections. " ++ attr.name ++ " is not a collection.")], dl, attr, e, location=l); - - top.baseDefDispatcher = errorCollectionValueDef(_, _, location=_); - top.appendDefDispatcher = errorCollectionValueDef(_, _, location=_); -} - -abstract production synCollectionDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isSynthesized = true; - top.operation = o; - - top.decoratedAccessHandler = synDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(synDecoratedAccessHandler(_, _, location=_), _, _, _); - top.attrDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, attr.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); - - top.attrBaseDefDispatcher = synBaseColAttributeDef(_, _, _, location=_); - top.attrAppendDefDispatcher = synAppendColAttributeDef(_, _, _, location=_); -} -abstract production inhCollectionDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; - top.isInherited = true; - top.operation = o; - - top.decoratedAccessHandler = inhDecoratedAccessHandler(_, _, location=_); - top.undecoratedAccessHandler = accessBounceDecorate(inhDecoratedAccessHandler(_, _, location=_), _, _, _); -- TODO: above should probably be an error handler! - top.attrDefDispatcher = - \ dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr l::Location -> - errorAttributeDef([err(l, attr.name ++ " is a collection attribute, and you must use ':=' or '<-', not '='.")], dl, attr, e, location=l); - top.attributionDispatcher = defaultAttributionDcl(_, _, _, _, location=_); - - top.attrBaseDefDispatcher = inhBaseColAttributeDef(_, _, _, location=_); - top.attrAppendDefDispatcher = inhAppendColAttributeDef(_, _, _, location=_); -} - -abstract production localCollectionDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type o::Operation -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.operation = o; - - top.refDispatcher = localReference(_, location=_); - top.defDispatcher = errorColNormalValueDef(_, _, location=_); - top.defLHSDispatcher = localDefLHS(_, location=_); - - top.baseDefDispatcher = baseCollectionValueDef(_, _, location=_); - top.appendDefDispatcher = appendCollectionValueDef(_, _, location=_); - - top.substitutedDclInfo = localCollectionDcl(sg,sl,fn, performRenaming(ty, top.givenSubstitution), o); - - -- TODO: attrOccursIndex - -- We shouldn't be forwarding here - forwards to localDcl(sg,sl,fn,ty); -} - - --- Defs -function synColDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation -{ - return attrDef(defaultEnvItem(synCollectionDcl(sg,sl,fn,bound,ty,o))); -} -function inhColDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type o::Operation -{ - return attrDef(defaultEnvItem(inhCollectionDcl(sg,sl,fn,bound,ty,o))); -} -function localColDef -Def ::= sg::String sl::Location fn::String ty::Type o::Operation -{ - return valueDef(defaultEnvItem(localCollectionDcl(sg,sl,fn,ty,o))); -} - diff --git a/grammars/silver/modification/collection/DocConfig.sv b/grammars/silver/modification/collection/DocConfig.sv deleted file mode 100644 index e93505f2c..000000000 --- a/grammars/silver/modification/collection/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:collection; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Collections\nmenu_title: Collection\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/collection/Project.sv b/grammars/silver/modification/collection/Project.sv deleted file mode 100644 index 11a7a47c3..000000000 --- a/grammars/silver/modification/collection/Project.sv +++ /dev/null @@ -1,8 +0,0 @@ -grammar silver:modification:collection; - -imports silver:definition:env; -imports silver:definition:core; -imports silver:definition:type; - -exports silver:modification:collection:java with silver:translation:java:core; - diff --git a/grammars/silver/modification/collection/java/Collection.sv b/grammars/silver/modification/collection/java/Collection.sv deleted file mode 100644 index 94bd4d960..000000000 --- a/grammars/silver/modification/collection/java/Collection.sv +++ /dev/null @@ -1,243 +0,0 @@ -grammar silver:modification:collection:java; -import silver:modification:collection; - -import silver:util; - -import silver:definition:core; -import silver:definition:env; - -import silver:translation:java:core; -import silver:translation:java:type; -import silver:definition:type; -import silver:definition:type:syntax; - -{- - The initialization order is a bit scattered. There a several problems. - - ONE: Grammars can have cyclic dependencies. As a result, - we can never rely on the declaration, or the base (:=), appearing before - a contribution (<-). - - TWO: Production bodies are unordered. So even within one block of code, - it's quite possible for an assignment to preceed a declaration. - Or a contribution to preceed a base. - - For LOCALS, it's okay to create the CA object at declaration with setupInh. - The array was created a couple of lines up. - - For SYN, it might be okay to? I'm not sure. Playing it safe for now. - - For INH, you can't for sure use setupInh. You might be defining an inherited - attribute on a local that hasn't had it's inherited array created yet. - e.g. - x.inh := ... - local attribute x :: .... - N.B. that's an ordinary local, we're talking about inherited collections here, - not local collections. --} - -synthesized attribute frontTrans :: String; -synthesized attribute midTrans :: String; -synthesized attribute endTrans :: String; - -attribute frontTrans, midTrans, endTrans occurs on Operation; - -aspect production functionOperation -top::Operation ::= s::String -{ - top.frontTrans = "" ++ makeClassName(s) ++".invoke("; - top.midTrans = ", "; - top.endTrans = ")"; -} -aspect production productionOperation -top::Operation ::= s::String -{ - top.frontTrans = "new " ++ makeClassName(s) ++"("; - top.midTrans = ", "; - top.endTrans = ")"; -} -aspect production plusPlusOperationString -top::Operation ::= -{ - top.frontTrans = "new common.StringCatter("; - top.midTrans = ", "; - top.endTrans = ")"; -} -aspect production plusPlusOperationList -top::Operation ::= -{ - top.frontTrans = "common.AppendCell.append("; - top.midTrans = ", "; - top.endTrans = ")"; -} -aspect production borOperation -top::Operation ::= -{ - top.frontTrans = "("; - top.midTrans = " || "; - top.endTrans = ")"; -} -aspect production bandOperation -top::Operation ::= -{ - top.frontTrans = "("; - top.midTrans = " && "; - top.endTrans = ")"; -} - ---- Declarations --------------------------------------------------------------- - -aspect production collectionAttributeDclProd -top::ProductionStmt ::= 'production' 'attribute' a::Name '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - local attribute o :: Operation; - o = q.operation; - - local attribute ugh_dcl_hack :: DclInfo; - ugh_dcl_hack = head(getValueDclAll(fName, top.env)); -- TODO - - -- Unlike synthesized and inherited attributes, locals can cheat because we know exactly - -- when the array we're indexing into was created: a couple of statements up from - -- exactly here. - - -- So we'll create the collection attribute object here, and not worry. - - top.setupInh <- - "\t\t" ++ top.frame.className ++ ".localAttributes[" ++ ugh_dcl_hack.attrOccursIndex ++ "] = new common.CollectionAttribute(){\n" ++ - "\t\t\tpublic Object eval(common.DecoratedNode context) {\n" ++ - "\t\t\t\t" ++ te.typerep.transType ++ " result = (" ++ te.typerep.transType ++ ")this.getBase().eval(context);\n" ++ - "\t\t\t\tfor(int i = 0; i < this.getPieces().size(); i++){\n" ++ - "\t\t\t\t\tresult = " ++ o.frontTrans ++ "result" ++ o.midTrans ++ "(" ++ te.typerep.transType ++ ")this.getPieces().get(i).eval(context)" ++ o.endTrans ++ ";\n" ++ - "\t\t\t\t}\n" ++ - "\t\t\t\treturn result;\n" ++ - "\t\t\t}\n" ++ - "\t\t};\n"; -} - -aspect production collectionAttributeDclSyn -top::AGDcl ::= 'synthesized' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - local attribute className :: String; - className = "CA" ++ a.name; - - local attribute o :: Operation; - o = q.operation; - - top.genFiles := [pair(className ++ ".java", - -"package " ++ makeName(top.grammarName) ++ ";\n\n" ++ - -"public class " ++ className ++ " extends common.CollectionAttribute {\n\n" ++ - -"\tpublic " ++ className ++ "(final int index) {\n" ++ -"\t\tsuper(index);\n" ++ -"\t}\n\n" ++ - -"\tpublic Object eval(common.DecoratedNode context) {\n" ++ -"\t\t" ++ te.typerep.transType ++ " result = (" ++ te.typerep.transType ++ ")this.getBase().eval(context);\n" ++ -"\t\tfor(int i = 0; i < this.getPieces().size(); i++){\n" ++ -"\t\t\tresult = " ++ o.frontTrans ++ "result" ++ o.midTrans ++ "(" ++ te.typerep.transType ++ ")this.getPieces().get(i).eval(context)" ++ o.endTrans ++ ";\n" ++ -"\t\t}\n" ++ -"\t\treturn result;\n" ++ -"\t}\n\n" ++ - -"}\n")]; -} - -aspect production collectionAttributeDclInh -top::AGDcl ::= 'inherited' 'attribute' a::Name tl::BracketedOptTypeExprs '::' te::TypeExpr 'with' q::NameOrBOperator ';' -{ - local attribute className :: String; - className = "CA" ++ a.name; - - local attribute o :: Operation; - o = q.operation; - - top.genFiles := [pair(className ++ ".java", - -"package " ++ makeName(top.grammarName) ++ ";\n\n" ++ - -"public class " ++ className ++ " extends common.CollectionAttribute {\n\n" ++ - -"\tpublic " ++ className ++ "() {\n" ++ -"\t\tsuper();\n" ++ -"\t}\n\n" ++ - -"\tpublic Object eval(common.DecoratedNode context) {\n" ++ -"\t\t" ++ te.typerep.transType ++ " result = (" ++ te.typerep.transType ++ ")this.getBase().eval(context);\n" ++ -"\t\tfor(int i = 0; i < this.getPieces().size(); i++){\n" ++ -"\t\t\tresult = " ++ o.frontTrans ++ "result" ++ o.midTrans ++ "(" ++ te.typerep.transType ++ ")this.getPieces().get(i).eval(context)" ++ o.endTrans ++ ";\n" ++ -"\t\t}\n" ++ -"\t\treturn result;\n" ++ -"\t}\n\n" ++ - -"}\n")]; -} - ---- Use semantics translation -------------------------------------------------- - ----------- LOCALS --- -aspect production baseCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - -- for locals, the CA object was created already - top.translation = - "\t\t// " ++ val.unparse ++ " := " ++ e.unparse ++ "\n" ++ - "\t\t((common.CollectionAttribute)" ++ top.frame.className ++ ".localAttributes[" ++ val.lookupValue.dcl.attrOccursIndex ++ "]).setBase(" ++ wrapLazy(e) ++ ");\n"; -} -aspect production appendCollectionValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - -- for locals, the CA object was created already - top.translation = - "\t\t// " ++ val.unparse ++ " <- " ++ e.unparse ++ "\n" ++ - "\t\t((common.CollectionAttribute)" ++ top.frame.className ++ ".localAttributes[" ++ val.lookupValue.dcl.attrOccursIndex ++ "]).addPiece(" ++ wrapLazy(e) ++ ");\n"; -} - ----------- SYNTHESIZED ---- -aspect production synBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- := -} e::Expr -{ - top.translation = - "\t\t// " ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ "\n" ++ - "\t\tif(" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] == null)\n" ++ - "\t\t\t" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] = new " ++ makeCAClassName(attr.attrDcl.fullName) ++"(" ++ attr.dcl.attrOccursIndex ++ ");\n" ++ - "\t\t((common.CollectionAttribute)" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "]).setBase(" ++ wrapLazy(e) ++ ");\n"; -} -aspect production synAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- <- -} e::Expr -{ - top.translation = - "\t\t// " ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ "\n" ++ - "\t\tif(" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] == null)\n" ++ - "\t\t\t" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] = new " ++ makeCAClassName(attr.attrDcl.fullName) ++"(" ++ attr.dcl.attrOccursIndex ++ ");\n" ++ - "\t\t((common.CollectionAttribute)" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "]).addPiece(" ++ wrapLazy(e) ++ ");\n"; -} - ----------- INHERITED ---- -aspect production inhBaseColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- := -} e::Expr -{ - top.translation = - "\t\t// " ++ dl.unparse ++ "." ++ attr.unparse ++ " := " ++ e.unparse ++ "\n" ++ - "\t\tif(" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] == null)\n" ++ - "\t\t\t" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] = new " ++ makeCAClassName(attr.attrDcl.fullName) ++ "();\n" ++ - "\t\t((common.CollectionAttribute)" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "]).setBase(" ++ wrapLazy(e) ++ ");\n"; -} -aspect production inhAppendColAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur {- <- -} e::Expr -{ - top.translation = - "\t\t// " ++ dl.unparse ++ "." ++ attr.unparse ++ " <- " ++ e.unparse ++ "\n" ++ - "\t\tif(" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] == null)\n" ++ - "\t\t\t" ++ dl.translation ++ "[" ++ attr.dcl.attrOccursIndex ++ "] = new " ++ makeCAClassName(attr.attrDcl.fullName) ++ "();\n" ++ - "\t\t((common.CollectionAttribute)" ++ dl.translation ++"[" ++ attr.dcl.attrOccursIndex ++ "]).addPiece(" ++ wrapLazy(e) ++ ");\n"; -} - - -function makeCAClassName -String ::= s::String -{ - return substituteLast(".", ".CA", makeName(s)); -} - diff --git a/grammars/silver/modification/copper/ActionCode.sv b/grammars/silver/modification/copper/ActionCode.sv deleted file mode 100644 index 99d1adbe0..000000000 --- a/grammars/silver/modification/copper/ActionCode.sv +++ /dev/null @@ -1,143 +0,0 @@ -grammar silver:modification:copper; - -terminal Action_kwd 'action' lexer classes {KEYWORD}; - -concrete production concreteProductionDclAction -top::AGDcl ::= 'concrete' 'production' id::Name ns::ProductionSignature pm::ProductionModifiers body::ProductionBody 'action' acode::ActionCode_c -{ - top.unparse = forward.unparse ++ "action " ++ acode.unparse; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - - top.syntaxAst := [ - syntaxProduction(ns.namedSignature, - foldr(consProductionMod, nilProductionMod(), - prodAction(acode.actionCode) :: pm.productionModifiers))]; - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local myFlowGraph :: ProductionGraph = - constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - - ns.signatureName = fName; - acode.frame = reduceActionContext(ns.namedSignature, myFlowGraph); - acode.env = newScopeEnv(productionActionVars ++ acode.defs ++ ns.actionDefs, top.env); - - top.errors <- acode.errors; - - -- note that we're not merging the typing contexts between action blocks and productions - -- this seems reasonable since inference should never have effects across this border... - - forwards to concreteProductionDcl($1, $2, id, ns, pm, body, location=top.location); -} - - -nonterminal ActionCode_c with location,config,unparse,actionCode,env,defs,grammarName,errors,frame, compiledGrammars, flowEnv, flowDefs; - -synthesized attribute actionCode :: String; - -concrete production actionCode_c -top::ActionCode_c ::= '{' stmts::ProductionStmts '}' -{ - top.unparse = "{\n" ++ stmts.unparse ++ "}\n"; - top.defs := flatMap(hackTransformLocals, stmts.defs); - propagate flowDefs; - - top.actionCode = sflatMap(hacklocaldeclarations, stmts.defs) ++ stmts.translation; - - top.errors := stmts.errors; - top.errors <- if top.frame.permitPluck && !stmts.containsPluck then - [err(top.location, "Disambiguation function without pluck")] else []; - - stmts.downSubst = emptySubst(); -} - - --- Support code to check the validity of disambiguation blocks. True if any elements --- contained in the snoc-list (so this statement or before) are a pluck. Handles --- raising errors if there are statements after a pluck. -synthesized attribute containsPluck :: Boolean occurs on ProductionStmts, ProductionStmt; -flowtype containsPluck {decorate} on ProductionStmts, ProductionStmt; - -aspect production productionStmtsSnoc -top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt -{ - top.containsPluck = t.containsPluck || h.containsPluck; - - top.errors <- if top.frame.permitPluck && h.containsPluck then [err(t.location, "Statement after pluck")] else []; -} - -aspect production productionStmtsNil -top::ProductionStmts ::= -{ - top.containsPluck = false; -} - -aspect default production -top::ProductionStmt ::= -{ - top.containsPluck = false; -} - -aspect production pluckDef -top::ProductionStmt ::= 'pluck' e::Expr ';' -{ - top.containsPluck = true; -} - -aspect production ifElseStmt -top::ProductionStmt ::= 'if' '(' c::Expr ')' th::ProductionStmt 'else' el::ProductionStmt -{ - -- Only guaranteed to pluck a terminal if both th and el contain a pluck - top.containsPluck = th.containsPluck && el.containsPluck; -} - --- TODO hacky. ideally we'd do this where local attributes are declared, not here. -function hacklocaldeclarations -String ::= d::Def -{ - return d.dcl.typerep.transType ++ " " ++ makeCopperName(d.dcl.fullName) ++ ";\n"; -} - -function hackTransformLocals -[Def] ::= d::Def -{ - return case d.dcl of - | localDcl(sg,sl,fn,ty) -> [parserLocalDef(sg,sl,fn,ty)] - | _ -> [] -- TODO: possibly error?? - end; -} - --------------------------------------------------------------------------------- --- Making children available in production action blocks - --- We don't care about the LHS. - -synthesized attribute actionDefs :: [Def] occurs on ProductionSignature, ProductionRHS, ProductionRHSElem; - -aspect production productionSignature -top::ProductionSignature ::= lhs::ProductionLHS '::=' rhs::ProductionRHS -{ - top.actionDefs = rhs.actionDefs; -} - -aspect production productionRHSNil -top::ProductionRHS ::= -{ - top.actionDefs = []; -} - -aspect production productionRHSCons -top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS -{ - top.actionDefs = h.actionDefs ++ t.actionDefs; -} - -aspect production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - top.actionDefs = [actionChildDef(top.grammarName, t.location, id.name, t.typerep)]; -} - diff --git a/grammars/silver/modification/copper/BlockContext.sv b/grammars/silver/modification/copper/BlockContext.sv deleted file mode 100644 index 98ba2c854..000000000 --- a/grammars/silver/modification/copper/BlockContext.sv +++ /dev/null @@ -1,55 +0,0 @@ -grammar silver:modification:copper; - --- hack for all uses of this stuff in this grammar. note s on imports -imports silver:definition:flow:driver only ProductionGraph, FlowType, constructAnonymousGraph; -imports silver:driver:util only RootSpec; - -attribute permitActions, permitPluck occurs on BlockContext; - -{-- - - Actions include parser attribute manipulation. print statement. - -} -synthesized attribute permitActions :: Boolean; -synthesized attribute permitPluck :: Boolean; - -aspect default production -top::BlockContext ::= -{ - top.permitActions = false; - top.permitPluck = false; -} - -{-- Terminal shift, parser attribute initialization -} -abstract production actionContext -top::BlockContext ::= g::ProductionGraph -{ - top.fullName = "__action__"; -- Used as part of naming locals... maybe we should fix that? TODO - top.signature = bogusNamedSignature(); - top.flowGraph = g; - - top.lazyApplication = false; - top.permitActions = true; - --top.permitProductionAttributes = false; -- denied by default - top.permitLocalAttributes = true; - -- TODO: signature? We DO have such info, but unclear what answer should be given... -} - -{-- Disambiguation groups -} -abstract production disambiguationContext -top::BlockContext ::= g::ProductionGraph -{ - top.permitPluck = true; - forwards to actionContext(g); -} - -{-- Production reduce actions -} -abstract production reduceActionContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.fullName = sig.fullName; - top.signature = sig; -- TODO: figure out if this is ever used for actions? - top.className = makeClassName(top.fullName); -- child references in production actions use it - - forwards to actionContext(g); -} - diff --git a/grammars/silver/modification/copper/BuildProcess.sv b/grammars/silver/modification/copper/BuildProcess.sv deleted file mode 100644 index 8c23e3e06..000000000 --- a/grammars/silver/modification/copper/BuildProcess.sv +++ /dev/null @@ -1,130 +0,0 @@ -grammar silver:modification:copper; - -import silver:driver; -import silver:translation:java:driver; - -import silver:util:cmdargs; - -synthesized attribute forceCopperDump :: Boolean occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= _ -{ - top.forceCopperDump = false; -} -abstract production copperdumpFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.forceCopperDump = true; - forwards to rest; -} -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--copperdump", flag(copperdumpFlag))]; - flagdescs <- ["\t--copperdump : force Copper to dump parse table information"]; -} -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - classpathCompiler <- ["${sh}/jars/CopperCompiler.jar"]; - classpathRuntime <- ["${sh}/jars/CopperRuntime.jar"]; - - -- Get the parsers - production allParsers :: [ParserSpec] = - flatMap(obtainParserSpecs(_, benv), grammarsRelevant); - - -- Have them get compiled by copper - extraGrammarsDeps <- ["copper"]; - extraTopLevelDecls <- [ - " ", - " \n" ++ sflatMap(buildAntParserPart(_, top.config), allParsers) ++ " "]; - - -- Generate the .copper files - top.postOps <- - map(parserSpecUnit(_, g.compiledGrammars, benv.silverGen), allParsers); -} - --- Skips parser specs from SILVER_HOST_GEN --- The way that feature works, they shouldn't need regeneration. -function obtainParserSpecs -[ParserSpec] ::= g::Decorated RootSpec benv::BuildEnv -{ - return if g.generateLocation != benv.silverGen then [] - else g.parserSpecs; -} - -function buildAntParserPart -String ::= p::ParserSpec a::Decorated CmdArgs -{ - local parserName :: String = makeParserName(p.fullName); - - local packagepath :: String = grammarToPath(p.sourceGrammar); - - local varyingopts :: String = - if a.forceCopperDump then - "avoidRecompile='false' dump='ON'" - else - "avoidRecompile='true' dump='ERROR_ONLY'"; - - return s""" - - - -"""; -} - -{-- - - At first, it might seem that parsers could be generated along with anything else in a grammar. - - (i.e. genFiles a .copper file) - - Unfortunately, it turns out this is not the case, due to a possibly over-aggressive - - build optimization we do: if the grammar was not directly modified, we assume it doesn't need - - to be re-translated. (TECHNICALLY, this isn't always the case...) - - - - So, parsers can change as a result of the grammars they depend on changing, so these - - DO need re-translation. So we treat them specially. - - - - If we ever fix the overall build process to re-translate grammars that depend on changed grammars, - - then we might be able to merge this into the normal process, maybe. - -} -abstract production parserSpecUnit -top::DriverAction ::= spec::ParserSpec cg::EnvTree silverGen::String -{ - local dir :: String = - silverGen ++ "src/" ++ grammarToPath(spec.sourceGrammar); - local file :: String = - dir ++ makeParserName(spec.fullName) ++ ".copper"; - - spec.compiledGrammars = cg; - local newSpec :: String = - spec.cstAst.xmlCopper; - - local specCst :: SyntaxRoot = spec.cstAst; - - local ex :: IOVal = isFile(file, top.ioIn); - local oldSpec :: IOVal = readFile(file, ex.io); - - local join :: IO = if ex.iovalue then oldSpec.io else ex.io; - - local err :: IO = - print("CST errors while generating parser " ++ spec.fullName ++ ":\n" ++ - implode("\n", specCst.cstErrors) ++ "\n", join); - - local doUTD :: IO = - print("Parser " ++ spec.fullName ++ " up to date.\n", join); - - local doWR :: IO = - writeFile(file, newSpec, - print("Generating parser " ++ spec.fullName ++ ".\n", - -- hack to ensure directory exists (for --dont-translate) - mkdir(dir, join).io)); - - top.io = if null(specCst.cstErrors) then - if ex.iovalue && oldSpec.iovalue == newSpec then doUTD - else doWR - else err; - - top.code = if null(specCst.cstErrors) then 0 else 1; - top.order = 7; -} - diff --git a/grammars/silver/modification/copper/DclInfo.sv b/grammars/silver/modification/copper/DclInfo.sv deleted file mode 100644 index e2901f7b5..000000000 --- a/grammars/silver/modification/copper/DclInfo.sv +++ /dev/null @@ -1,107 +0,0 @@ -grammar silver:modification:copper; - -{-- - - Reference to something declared as "parser attribute foo ..." - -} -abstract production parserAttrDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = parserAttributeReference(_, location=_); - top.defDispatcher = parserAttributeValueDef(_, _, location=_); - top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); -} - -{-- - - The names of possible pluckable terminals are jammed in the environment using this dcl. - -} -abstract production pluckTermDcl -top::DclInfo ::= sg::String sl::Location fn::String -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = terminalIdType(); -- TODO: Still needs work to prevent returning terminals - -- that are not part of the disambiguation set. - - top.refDispatcher = pluckTerminalReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} - -{-- - - Reference to a lexer class declaration. Has its own namespace in the environment, for now. - -} -abstract production lexerClassDcl -top::DclInfo ::= sg::String sl::Location fn::String -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - -- If we made lexer classes proper types, it might simplify a lot of code. - -- We wouldn't need a separate namespace, they could just be in the type namespace. - -- Currently referencing a lexer class gives a list of its member's TerminalIds. - top.typerep = listType(terminalIdType()); - top.refDispatcher = lexerClassReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} - -{-- - - lexeme/filename/line/column. Used in terminal and production action code. - -} -abstract production termAttrValueDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = termAttrValueReference(_, location=_); - top.defDispatcher = termAttrValueValueDef(_, _, location=_); - top.defLHSDispatcher = errorDefLHS(_, location=_); -} - -{-- - - Reference to production's children from production action code. - -} -abstract production actionChildDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = actionChildReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); - top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); -- TODO: specialize this -} - -{-- - - Reference to a local variable ("local foo :: Type = ...") inside an action block. - -} -abstract production parserLocalDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - -- TODO: use specialized ones that give better errors messages! - top.refDispatcher = parserAttributeReference(_, location=_); - top.defDispatcher = parserAttributeValueDef(_, _, location=_); - top.defLHSDispatcher = parserAttributeDefLHS(_, location=_); -} diff --git a/grammars/silver/modification/copper/DocConfig.sv b/grammars/silver/modification/copper/DocConfig.sv deleted file mode 100644 index c7a0cf505..000000000 --- a/grammars/silver/modification/copper/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:copper; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Copper\nmenu_title: Copper\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/copper/Env.sv b/grammars/silver/modification/copper/Env.sv deleted file mode 100644 index bf4fa9b86..000000000 --- a/grammars/silver/modification/copper/Env.sv +++ /dev/null @@ -1,148 +0,0 @@ -grammar silver:modification:copper; - --------------------------------------------------------------------------------- --- Defs.sv - -synthesized attribute lexerClassList :: [EnvItem] occurs on Defs, Def; - -aspect production nilDefs -top::Defs ::= -{ - top.lexerClassList = []; -} - -aspect production consDefs -top::Defs ::= e1::Def e2::Defs -{ - top.lexerClassList = e1.lexerClassList ++ e2.lexerClassList; -} - -aspect default production -top::Def ::= -{ - top.lexerClassList = []; -} - -abstract production lxrClsDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.lexerClassList = [d]; - top.valueList = [d]; - top.filterDef = top.filterFn(d); - top.mapDef = lxrClsDef(top.mapFn(d)); -} - -function parserAttrDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(parserAttrDcl(sg,sl,fn,ty))); -} - -function pluckTermDef -Def ::= sg::String sl::Location fn::String -{ - return valueDef(defaultEnvItem(pluckTermDcl(sg,sl,fn))); -} - -function lexerClassDef -Def ::= sg::String sl::Location fn::String -{ - return lxrClsDef(defaultEnvItem(lexerClassDcl(sg,sl,fn))); -} - -function termAttrValueDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(termAttrValueDcl(sg,sl,fn,ty))); -} - -function actionChildDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(actionChildDcl(sg,sl,fn,ty))); -} - -function parserLocalDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(parserLocalDcl(sg,sl,fn,ty))); -} - --------------------------------------------------------------------------------- --- Env.sv - -synthesized attribute lexerClassTree :: EnvScope occurs on Env; - -aspect production i_emptyEnv -top::Env ::= -{ - top.lexerClassTree = emptyEnvScope(); -} - -aspect production i_appendEnv -top::Env ::= e1::Decorated Env e2::Decorated Env -{ - top.lexerClassTree = appendEnvScope(e1.lexerClassTree, e2.lexerClassTree); -} - -aspect production i_newScopeEnv -top::Env ::= d::Defs e::Decorated Env -{ - top.lexerClassTree = consEnvScope(buildTree(d.lexerClassList), e.lexerClassTree); -} - -aspect production i_occursEnv -top::Env ::= _ e::Decorated Env -{ - top.lexerClassTree = e.lexerClassTree; -} - -function getLexerClassDcl -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvScope(search, e.lexerClassTree); -} - --------------------------------------------------------------------------------- --- QName.sv - -synthesized attribute lookupLexerClass :: Decorated QNameLookup occurs on QName; - -aspect production qNameId -top::QName ::= id::Name -{ - top.lookupLexerClass = decorate customLookup("lexer class", getLexerClassDcl(top.name, top.env), top.name, top.location) with {}; -} - -aspect production qNameCons -top::QName ::= id::Name ':' qn::QName -{ - top.lookupLexerClass = decorate customLookup("lexer class", getLexerClassDcl(top.name, top.env), top.name, top.location) with {}; -} - -aspect production qNameError -top::QName ::= msg::[Message] -{ - top.lookupLexerClass = decorate errorLookup(msg) with {}; -} - - --------------------------------------------------------------------------------- - --- Some pre-defined variables in certain contexts - -global i_lexemeVariable :: [Def] = - [termAttrValueDef("DBGtav", bogusLoc(), "lexeme", stringType())]; -global i_shiftableVariable :: [Def] = - [termAttrValueDef("DBGtav", bogusLoc(), "shiftable", listType(terminalIdType()))]; -global i_locVariables :: [Def] = [ - termAttrValueDef("DBGtav", bogusLoc(), "filename", stringType()), - termAttrValueDef("DBGtav", bogusLoc(), "line", intType()), - termAttrValueDef("DBGtav", bogusLoc(), "column", intType())]; - -global terminalActionVars :: [Def] = i_lexemeVariable ++ i_locVariables; -global productionActionVars :: [Def] = i_locVariables; -global disambiguationActionVars :: [Def] = i_lexemeVariable ++ i_locVariables; -global disambiguationClassActionVars :: [Def] = i_lexemeVariable ++ i_shiftableVariable ++ i_locVariables; - diff --git a/grammars/silver/modification/copper/Expr.sv b/grammars/silver/modification/copper/Expr.sv deleted file mode 100644 index cf62c66d2..000000000 --- a/grammars/silver/modification/copper/Expr.sv +++ /dev/null @@ -1,126 +0,0 @@ -grammar silver:modification:copper; - -terminal DisambiguationFailure_t 'disambiguationFailure' lexer classes {KEYWORD, RESERVED}; - -concrete production failureTerminalIdExpr -top::Expr ::= 'disambiguationFailure' -{ - top.unparse = "disambiguationFailure"; - top.errors := []; - top.typerep = terminalIdType(); - - top.translation = "(-1)"; - top.lazyTranslation = top.translation; - - top.upSubst = top.downSubst; -} - -abstract production actionChildReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := []; -- Should only ever be in scope when valid - - top.typerep = q.lookupValue.typerep; - - top.translation = "((" ++ top.typerep.transType ++ ")((common.Node)RESULTfinal).getChild(" ++ top.frame.className ++ ".i_" ++ q.lookupValue.fullName ++ "))"; - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} - -abstract production pluckTerminalReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := []; -- Should only be referenceable from a context where its valid. - - -- TODO: It would be nice to have a more specific type here, see comment below. - top.typerep = terminalIdType(); - - top.translation = makeCopperName(q.lookupValue.fullName); -- Value right here? - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} - --- TODO: Distinct from pluckTerminalReference (since this can occur in any action block and --- reference any terminal), but maybe it shouldn't be? These productions do almost the same --- thing. Also having type classes would let us use a more specific type than generic TerminalId, --- and pluckTerminalReference wouldn't need to cheat with a fresh type. -abstract production terminalIdReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := if !top.frame.permitActions - then [err(top.location, "References to terminal identifiers can only be made in action blocks")] - else []; - - top.typerep = terminalIdType(); - - top.translation = s"Terminals.${makeCopperName(q.lookupValue.fullName)}.num()"; - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} - -abstract production lexerClassReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := if !top.frame.permitActions - then [err(top.location, "References to lexer class members can only be made in action blocks")] - else []; - - -- TODO: This should be a more specific type with type classes - top.typerep = listType(terminalIdType()); - - top.translation = makeCopperName(q.lookupValue.fullName); - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} - -abstract production parserAttributeReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := if !top.frame.permitActions - then [err(top.location, "References to parser attributes can only be made in action blocks")] - else []; - - top.typerep = q.lookupValue.typerep; - - top.translation = - s"""(${makeCopperName(q.lookupValue.fullName)} == null? (${top.typerep.transType})common.Util.error("Uninitialized parser attribute ${q.name}") : ${makeCopperName(q.lookupValue.fullName)})"""; - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} - -abstract production termAttrValueReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - - top.errors := []; -- Should only ever be in scope in action blocks - - top.typerep = q.lookupValue.typerep; - - -- Yeah, it's a big if/then/else block, but these are all very similar and related. - top.translation = - if q.name == "lexeme" then "new common.StringCatter(lexeme)" else - if q.name == "shiftable" then "shiftableList" else - if q.name == "line" then "virtualLocation.getLine()" else - if q.name == "column" then "virtualLocation.getColumn()" else - if q.name == "filename" then "new common.StringCatter(virtualLocation.getFileName())" else - error("unknown actionTerminalReference " ++ q.name); -- should never be called, but here for safety - top.lazyTranslation = top.translation; -- never, but okay! - - top.upSubst = top.downSubst; -} diff --git a/grammars/silver/modification/copper/LexerClass.sv b/grammars/silver/modification/copper/LexerClass.sv deleted file mode 100644 index 7a7bc1474..000000000 --- a/grammars/silver/modification/copper/LexerClass.sv +++ /dev/null @@ -1,97 +0,0 @@ -grammar silver:modification:copper; - -terminal Lexer_kwd 'lexer' lexer classes {KEYWORD}; -terminal Class_kwd 'class' lexer classes {KEYWORD}; -terminal Extends_kwd 'extends' lexer classes {KEYWORD}; - -concrete production lexerClassDclEmpty -top::AGDcl ::= 'lexer' 'class' id::Name ';' -{ - forwards to lexerClassDecl($1, $2, id, lexerClassModifiersNone(location=$4.location), $4, location=top.location); -} - -concrete production lexerClassDecl -top::AGDcl ::= 'lexer' 'class' id::Name modifiers::LexerClassModifiers ';' -{ - top.unparse = "lexer class " ++ id.name ++ modifiers.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ id.name; - - top.defs := [lexerClassDef(top.grammarName, id.location, fName)]; - - top.errors <- if length(getLexerClassDcl(fName, top.env)) > 1 - then [err(id.location, "Lexer class '" ++ fName ++ "' is already bound.")] - else []; - - top.errors := modifiers.errors; - - top.syntaxAst := [syntaxLexerClass(fName, - foldr(consLexerClassMod, nilLexerClassMod(), modifiers.lexerClassModifiers))]; -} - -nonterminal LexerClassModifiers with config, location, unparse, lexerClassModifiers, errors, env, grammarName, compiledGrammars, flowEnv; -closed nonterminal LexerClassModifier with config, location, unparse, lexerClassModifiers, errors, env, grammarName, compiledGrammars, flowEnv; - -monoid attribute lexerClassModifiers :: [SyntaxLexerClassModifier] with [], ++; - -propagate errors on LexerClassModifiers, LexerClassModifier; -propagate lexerClassModifiers on LexerClassModifiers; - -abstract production lexerClassModifiersNone -top::LexerClassModifiers ::= -{ - top.unparse = ""; -} -concrete production lexerClassModifierSingle -top::LexerClassModifiers ::= tm::LexerClassModifier -{ - top.unparse = tm.unparse; -} -concrete production lexerClassModifiersCons -top::LexerClassModifiers ::= h::LexerClassModifier ',' t::LexerClassModifiers -{ - top.unparse = h.unparse ++ " " ++ t.unparse; -} - -concrete production lexerClassModifierExtends -top::LexerClassModifier ::= 'extends' cls::LexerClasses -{ - top.unparse = "extends " ++ cls.unparse; - - top.lexerClassModifiers := [lexerClassExtends(cls.lexerClasses)]; -} - -concrete production lexerClassModifierDominates -top::LexerClassModifier ::= 'dominates' terms::TermPrecs -{ - top.unparse = "dominates " ++ terms.unparse; - - top.lexerClassModifiers := [lexerClassDominates(terms.precTermList)]; -} - -concrete production lexerClassModifierSubmitsTo -top::LexerClassModifier ::= 'submits' 'to' terms::TermPrecs -{ - top.unparse = "submits to " ++ terms.unparse; - - top.lexerClassModifiers := [lexerClassSubmits(terms.precTermList)]; -} - -concrete production lexerClassModifierDisambiguate -top::LexerClassModifier ::= 'disambiguate' acode::ActionCode_c -{ - top.unparse = "disambiguate " ++ acode.unparse; - - top.lexerClassModifiers := [lexerClassDisambiguate(acode.actionCode)]; - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local myFlowGraph :: ProductionGraph = - constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - - acode.env = newScopeEnv(disambiguationClassActionVars ++ acode.defs, top.env); - acode.frame = disambiguationContext(myFlowGraph); -} diff --git a/grammars/silver/modification/copper/Project.sv b/grammars/silver/modification/copper/Project.sv deleted file mode 100644 index 32b68f2b2..000000000 --- a/grammars/silver/modification/copper/Project.sv +++ /dev/null @@ -1,18 +0,0 @@ -grammar silver:modification:copper; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:concrete_syntax; -imports silver:definition:concrete_syntax:ast; -imports silver:definition:type; -imports silver:definition:type:syntax; - -imports silver:extension:list; - ---imports silver:analysis:typechecking:core; - -imports silver:translation:java:core; -imports silver:translation:java:type; - -imports silver:util; - diff --git a/grammars/silver/modification/copper/TerminalDcl.sv b/grammars/silver/modification/copper/TerminalDcl.sv deleted file mode 100644 index 436631ccf..000000000 --- a/grammars/silver/modification/copper/TerminalDcl.sv +++ /dev/null @@ -1,175 +0,0 @@ -grammar silver:modification:copper; - -terminal Dominates_t 'dominates' lexer classes {KEYWORD}; -terminal Submits_t 'submits' lexer classes {KEYWORD}; -terminal Classes_kwd 'classes' lexer classes {KEYWORD}; - -monoid attribute lexerClasses :: [String] with [], ++; -attribute lexerClasses occurs on TerminalModifier, TerminalModifiers; -propagate lexerClasses on TerminalModifiers, TerminalModifier; - -concrete production terminalModifierDominates -top::TerminalModifier ::= 'dominates' terms::TermPrecs -{ - top.unparse = "dominates { " ++ terms.unparse ++ " } "; - - top.terminalModifiers := [termDominates(terms.precTermList)]; - propagate errors; -} - -concrete production terminalModifierSubmitsTo -top::TerminalModifier ::= 'submits' 'to' terms::TermPrecs -{ - top.unparse = "submits to { " ++ terms.unparse ++ " } " ; - - top.terminalModifiers := [termSubmits(terms.precTermList)]; - propagate errors; -} - -concrete production terminalModifierClassSpec -top::TerminalModifier ::= 'lexer' 'classes' cl::LexerClasses -{ - top.unparse = "lexer classes { " ++ cl.unparse ++ " } " ; - - top.terminalModifiers := [termClasses(cl.lexerClasses)]; - propagate errors; -} - -concrete production terminalModifierActionCode -top::TerminalModifier ::= 'action' acode::ActionCode_c -{ - top.unparse = "action " ++ acode.unparse; - - top.terminalModifiers := [termAction(acode.actionCode)]; - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local myFlowGraph :: ProductionGraph = - constructAnonymousGraph(acode.flowDefs, top.env, myProds, myFlow); - - acode.frame = actionContext(myFlowGraph); - acode.env = newScopeEnv(terminalActionVars ++ acode.defs, top.env); - - propagate errors; -} - -monoid attribute precTermList :: [String] with [], ++; - -nonterminal TermPrecs with config, grammarName, unparse, location, precTermList, errors, env; -propagate errors, precTermList on TermPrecs; - -concrete production termPrecsOne -top::TermPrecs ::= t::QName -{ - forwards to termPrecs(termPrecList(t,termPrecListNull(location=t.location), location=t.location), location=top.location); -} - -concrete production termPrecsList -top::TermPrecs ::= '{' terms::TermPrecList '}' -{ - forwards to termPrecs(terms,location=top.location); -} - -abstract production termPrecs -top::TermPrecs ::= terms::TermPrecList -{ - top.unparse = s"{${terms.unparse}}"; -} - -nonterminal TermPrecList with config, grammarName, unparse, location, precTermList, errors, env; -propagate errors, precTermList on TermPrecList; - -abstract production termPrecList -top::TermPrecList ::= h::QName t::TermPrecList -{ - top.unparse = if t.unparse == "" - then h.unparse - else h.unparse ++ ", " ++ t.unparse; - - production fName::String = if null(h.lookupType.dcls) then h.lookupLexerClass.dcl.fullName else h.lookupType.dcl.fullName; - - top.precTermList <- [fName]; - - -- Since we're looking it up in two ways, do the errors ourselves - top.errors <- if null(h.lookupType.dcls) && null(h.lookupLexerClass.dcls) - then [err(h.location, "Undeclared terminal or lexer class '" ++ h.name ++ "'.")] - else if length(h.lookupType.dcls ++ h.lookupLexerClass.dcls) > 1 - then [err(h.location, "Ambiguous reference to terminal or lexer class '" ++ h.name ++ "'. Possibilities are:\n" ++ printPossibilities(h.lookupType.dcls ++ h.lookupLexerClass.dcls))] - else []; -} - -abstract production termPrecListNull -top::TermPrecList ::= -{ - top.unparse = ""; -} - -concrete production termPrecListOne -top::TermPrecList ::= t::QName -{ - forwards to termPrecList(t, termPrecListNull(location=top.location), location=top.location); -} - -concrete production termPrecListCons -top::TermPrecList ::= t::QName ',' terms_tail::TermPrecList -{ - forwards to termPrecList(t, terms_tail,location=top.location); -} - -nonterminal LexerClasses with location, config, unparse, lexerClasses, errors, env; -propagate errors, lexerClasses on LexerClasses; - -concrete production lexerClassesOne -top::LexerClasses ::= n::QName -{ - forwards to lexerClasses(lexerClassListMain(n, lexerClassListNull(location=top.location), location=top.location), location=top.location); -} - -concrete production lexerClassesList -top::LexerClasses ::= '{' cls::LexerClassList '}' -{ - forwards to lexerClasses(cls,location=top.location); -} - -abstract production lexerClasses -top::LexerClasses ::= cls::LexerClassList -{ - top.unparse = s"{${cls.unparse}}"; -} - -nonterminal LexerClassList with location, config, unparse, lexerClasses, errors, env; -propagate errors, lexerClasses on LexerClassList; - -concrete production lexerClassListOne -top::LexerClassList ::= n::QName -{ - forwards to lexerClassListMain(n,lexerClassListNull(location=n.location), location=n.location); -} - -concrete production lexerClassListCons -top::LexerClassList ::= n::QName ',' cl_tail::LexerClassList -{ - forwards to lexerClassListMain(n,cl_tail,location=top.location); -} - - -abstract production lexerClassListMain -top::LexerClassList ::= n::QName t::LexerClassList -{ - top.unparse = if t.unparse == "" - then n.unparse - else n.unparse ++ ", " ++ t.unparse; - - top.errors <- n.lookupLexerClass.errors; - - top.lexerClasses <- [n.lookupLexerClass.dcl.fullName]; -} - -abstract production lexerClassListNull -cl::LexerClassList ::= -{ - cl.unparse = ""; -} - diff --git a/grammars/silver/modification/copper_mda/Analysis.sv b/grammars/silver/modification/copper_mda/Analysis.sv deleted file mode 100644 index b76e7fe31..000000000 --- a/grammars/silver/modification/copper_mda/Analysis.sv +++ /dev/null @@ -1,70 +0,0 @@ -grammar silver:modification:copper_mda; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:concrete_syntax; -imports silver:definition:concrete_syntax:ast; -imports silver:modification:copper; - -import silver:driver:util only computeDependencies, RootSpec; - - -terminal CopperMDA 'copper_mda' lexer classes {KEYWORD}; - -concrete production copperMdaDcl -top::AGDcl ::= 'copper_mda' testname::Name '(' orig::QName ')' '{' m::ParserComponents '}' -{ - top.unparse = ""; - - propagate errors, moduleNames; - - top.errors <- orig.lookupValue.errors; - - local spec :: [ParserSpec] = - if !orig.lookupValue.found then [] - else findSpec(orig.lookupValue.fullName, - head(searchEnvTree(orig.lookupValue.dcl.sourceGrammar, top.compiledGrammars)).parserSpecs); - - top.errors <- if !orig.lookupValue.found || !null(spec) then [] - else [err(orig.location, orig.name ++ " is not a parser.")]; - - -- Ignoring prefixes and any SyntaxDcls generated by the ParserComponents for now... - top.mdaSpecs = - case spec of - | parserSpec(_,_,fn,snt,hg,csl,_,_,_) :: _ -> [mdaSpec(top.grammarName, top.grammarName ++":"++ testname.name, snt, hg, m.moduleNames, csl)] - | _ -> [] - end; -} - -function findSpec -[ParserSpec] ::= n::String s::[ParserSpec] -{ - return if null(s) then [] - else if n == head(s).fullName then [head(s)] - else findSpec(n, tail(s)); -} - -nonterminal MdaSpec with sourceGrammar, fullName, compiledGrammars,cstAst; - -abstract production mdaSpec -top::MdaSpec ::= sg::String fn::String snt::String hostgrams::[String] extgrams::[String] customStartLayout::Maybe<[String]> -{ - top.sourceGrammar = sg; - top.fullName = fn; - - -- TODO: see TODO s in ParserSpec - production hostmed :: ModuleExportedDefs = - moduleExportedDefs(error("no sl"), top.compiledGrammars, - computeDependencies(hostgrams ++ extgrams, top.compiledGrammars), hostgrams, []); - - production extmed :: ModuleExportedDefs = - moduleExportedDefs(error("no sl"), top.compiledGrammars, - computeDependencies(hostgrams ++ extgrams, top.compiledGrammars), extgrams, []); - - top.cstAst = - cstCopperMdaRoot(fn, snt, - foldr(consSyntax, nilSyntax(), hostmed.syntaxAst), - foldr(consSyntax, nilSyntax(), extmed.syntaxAst), - customStartLayout); -} - diff --git a/grammars/silver/modification/copper_mda/BuildProcess.sv b/grammars/silver/modification/copper_mda/BuildProcess.sv deleted file mode 100644 index f062ae2c1..000000000 --- a/grammars/silver/modification/copper_mda/BuildProcess.sv +++ /dev/null @@ -1,66 +0,0 @@ -grammar silver:modification:copper_mda; - -import silver:driver; -import silver:translation:java:driver; -import silver:translation:java:core only makeParserName, makeName; - -import silver:util:cmdargs; - -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - top.postOps <- map(generateMdaSpec(g.compiledGrammars, _, benv.silverGen ++ "src/"), - flatMap((.mdaSpecs), grammarsToTranslate)); - - -- TODO: consider examining all grammars, not just grammarsToTranslate? - -- I believe this choice was originally because we weren't serializing MdaSpecs to - -- interface files, but I think we could easily start doing that new with the new serialization code? - local targets :: [MdaSpec] = flatMap((.mdaSpecs), grammarsToTranslate); - - extraTopLevelDecls <- - if null(targets) then [] - else [" \n" ++ sflatMap(mdaBuildSpecTarget, targets) ++ " \n"]; - -- By adding the dependency here, the MDA check happens right after parsers are built normally. - extraGrammarsDeps <- - if null(targets) then [] else ["copper_mda"]; - -- By *also* adding it here, we do MDA checks even if --dont-translate is active - -- (that is, even if the `grammars` target isn't built.) - -- (don't worry: ant doesn't re-run the target.) - extraDistDeps <- - if null(targets) then [] else ["copper_mda"]; -} - -abstract production generateMdaSpec -top::DriverAction ::= grams::EnvTree spec::MdaSpec silvergen::String -{ - spec.compiledGrammars = grams; - - local ast :: SyntaxRoot = spec.cstAst; - local parserName :: String = makeParserName(spec.fullName); - local dir :: String = silvergen ++ grammarToPath(spec.sourceGrammar); - local copperFile :: String = dir ++ parserName ++ ".copper"; - - local err :: IO = - print("CST errors while testing MDA " ++ spec.fullName ++ ":\n" ++ - foldr(\ a::String b::String -> - a ++ "\n" ++ b, "", ast.cstErrors) ++ - "\n", top.ioIn); - - local printio::IO = print("MDA test file: " ++ spec.fullName ++ "\n", top.ioIn); - local writeio::IO = - writeFile(copperFile, ast.xmlCopper, - -- hack for when we're --dont-translate'ing, make sure the dir exists. - mkdir(dir, printio).io); - - top.io = if null(ast.cstErrors) then writeio else err; - top.code = if null(ast.cstErrors) then 0 else 1; - top.order = 5; -} - -function mdaBuildSpecTarget -String ::= spec::MdaSpec -{ - return " \n" ++ - " \n \n"; -} diff --git a/grammars/silver/modification/copper_mda/DocConfig.sv b/grammars/silver/modification/copper_mda/DocConfig.sv deleted file mode 100644 index 5261c1d07..000000000 --- a/grammars/silver/modification/copper_mda/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:copper_mda; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Copper MDA\nmenu_title: Copper MDA\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/copper_mda/Root.sv b/grammars/silver/modification/copper_mda/Root.sv deleted file mode 100644 index f6d1e4631..000000000 --- a/grammars/silver/modification/copper_mda/Root.sv +++ /dev/null @@ -1,63 +0,0 @@ -grammar silver:modification:copper_mda; - -import silver:driver:util; - -synthesized attribute mdaSpecs :: [MdaSpec] occurs on Root, AGDcls, AGDcl, RootSpec, Grammar; - -aspect production root -top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls -{ - top.mdaSpecs = ags.mdaSpecs; -} - -aspect production nilAGDcls -top::AGDcls ::= -{ - top.mdaSpecs = []; -} -aspect production consAGDcls -top::AGDcls ::= h::AGDcl t::AGDcls -{ - top.mdaSpecs = h.mdaSpecs ++ t.mdaSpecs; -} - -aspect default production -top::AGDcl ::= -{ - top.mdaSpecs = []; -} -aspect production appendAGDcl -top::AGDcl ::= ag1::AGDcl ag2::AGDcl -{ - top.mdaSpecs = ag1.mdaSpecs ++ ag2.mdaSpecs; -} - -aspect production grammarRootSpec -top::RootSpec ::= g::Grammar _ _ _ _ -{ - top.mdaSpecs = g.mdaSpecs; -} -aspect production interfaceRootSpec -top::RootSpec ::= _ _ _ -{ - top.mdaSpecs = []; -- TODO -} -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - top.mdaSpecs = []; -} - - -aspect production nilGrammar -top::Grammar ::= -{ - top.mdaSpecs = []; -} - -aspect production consGrammar -top::Grammar ::= h::Root t::Grammar -{ - top.mdaSpecs = h.mdaSpecs ++ t.mdaSpecs; -} - diff --git a/grammars/silver/modification/copper_mda/Syntax.sv b/grammars/silver/modification/copper_mda/Syntax.sv deleted file mode 100644 index aee7d9c43..000000000 --- a/grammars/silver/modification/copper_mda/Syntax.sv +++ /dev/null @@ -1,42 +0,0 @@ -grammar silver:modification:copper_mda; - -synthesized attribute markingTokens :: [Decorated SyntaxDcl]; -synthesized attribute bridgeProductions :: [Decorated SyntaxDcl]; - -attribute markingTokens, bridgeProductions occurs on Syntax, SyntaxDcl; - -aspect production nilSyntax -top::Syntax ::= -{ - top.markingTokens = []; - top.bridgeProductions = []; -} -aspect production consSyntax -top::Syntax ::= s1::SyntaxDcl s2::Syntax -{ - top.markingTokens = s1.markingTokens ++ s2.markingTokens; - top.bridgeProductions = s1.bridgeProductions ++ s2.bridgeProductions; -} - -aspect default production -top::SyntaxDcl ::= -{ - top.markingTokens = []; - top.bridgeProductions = []; -} - -aspect production syntaxTerminal -top::SyntaxDcl ::= n::String _ modifiers::SyntaxTerminalModifiers -{ - top.markingTokens = if modifiers.marking then [top] else []; -} - -aspect production syntaxProduction -top::SyntaxDcl ::= ns::NamedSignature modifiers::SyntaxProductionModifiers -{ - top.bridgeProductions = - if !null(lhsRef) && top.containingGrammar != head(lhsRef).containingGrammar - then [top] - else []; -} - diff --git a/grammars/silver/modification/copper_mda/SyntaxMdaRoot.sv b/grammars/silver/modification/copper_mda/SyntaxMdaRoot.sv deleted file mode 100644 index ca51a9421..000000000 --- a/grammars/silver/modification/copper_mda/SyntaxMdaRoot.sv +++ /dev/null @@ -1,140 +0,0 @@ -grammar silver:modification:copper_mda; - -import silver:util:raw:graph as g; - -abstract production cstCopperMdaRoot -top::SyntaxRoot ::= parsername::String startnt::String host::Syntax ext::Syntax customStartLayout::Maybe<[String]> -{ - -- Because there may be references between the grammars, we cannot do the - -- usual normalization. - - -- TODO: we could consider making host host-only, and ext have both... - host.cstEnv = directBuildTree(host.cstDcls ++ ext.cstDcls); - host.containingGrammar = "host"; - host.cstNTProds = error("TODO: this should only be used by normalize"); -- TODO - host.classTerminals = directBuildTree(host.classTerminalContribs ++ ext.classTerminalContribs); - host.superClasses = - directBuildTree( - g:toList( - g:transitiveClosure( - g:add( - host.superClassContribs ++ ext.superClassContribs, - g:empty(compareString))))); - host.subClasses = - directBuildTree( - g:toList( - g:transitiveClosure( - g:add( - map( - \ p::Pair -> pair(p.snd, p.fst), - host.superClassContribs ++ ext.superClassContribs), - g:empty(compareString))))); - host.parserAttributeAspects = - directBuildTree(host.parserAttributeAspectContribs ++ ext.parserAttributeAspectContribs); - host.layoutTerms = - -- ext shouldn't affect host layout, but include both so we only have to build this once - buildLayoutEnv( - map((.fullName), host.allTerminals ++ ext.allTerminals), - map((.fullName), host.allProductions ++ ext.allProductions ++ host.allNonterminals ++ ext.allNonterminals), - host.layoutContribs ++ ext.layoutContribs); - host.prefixesForTerminals = directBuildTree([]); - host.componentGrammarMarkingTerminals = directBuildTree([]); - - ext.cstEnv = host.cstEnv; - ext.containingGrammar = "ext"; - ext.cstNTProds = error("TODO: this should only be used by normalize"); -- TODO - ext.classTerminals = host.classTerminals; - ext.superClasses = host.superClasses; - ext.subClasses = host.subClasses; - ext.parserAttributeAspects = host.parserAttributeAspects; - ext.layoutTerms = host.layoutTerms; - ext.prefixesForTerminals = host.prefixesForTerminals; - ext.componentGrammarMarkingTerminals = host.componentGrammarMarkingTerminals; - - local startFound :: [Decorated SyntaxDcl] = searchEnvTree(startnt, host.cstEnv); - - top.cstErrors := host.cstErrors ++ ext.cstErrors; - top.cstErrors <- if !null(startFound) then [] - else ["Nonterminal " ++ startnt ++ " was referenced but " ++ - "this grammar was not included in this parser. (Referenced as parser's starting nonterminal)"]; - - -- The layout before and after the root nonterminal. By default, the layout of the root nonterminal. - production startLayout :: String = - implode("", - map(xmlCopperRef, - map(head, - lookupStrings( - fromMaybe(searchEnvTree(startnt, host.layoutTerms), customStartLayout), - host.cstEnv)))); - - top.xmlCopper = -"\n\n" ++ - -"\n" ++ - -" \n" ++ -" " ++ parsername ++ "\n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" " ++ xmlCopperRef(head(startFound)) ++ "\n" ++ -" " ++ startLayout ++ "\n" ++ -" \n\n" ++ - -" \n\n" ++ -" " ++ host.containingGrammar ++ "\n\n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ - host.xmlCopper ++ -" \n" ++ -" \n\n" ++ - -" \n" ++ -" " ++ host.containingGrammar ++ "\n\n" ++ -" \n" ++ - implode("", map(xmlCopperRef, ext.markingTokens)) ++ -" \n" ++ -" \n" ++ - implode("", map(xmlCopperRef, ext.bridgeProductions)) ++ -" \n" ++ -" \n" ++ - -- TODO: Workaround hack since host disambiguation functions are moved - -- to the extension grammar. - implode("", - map(xmlCopperRef, - map( - \ d::Decorated SyntaxDcl -> - decorate new(d) with { - containingGrammar = "ext"; - cstEnv = host.cstEnv; - cstNTProds = error("TODO: this should only be used by normalize"); -- TODO - classTerminals = host.classTerminals; - superClasses = host.superClasses; - subClasses = host.subClasses; - parserAttributeAspects = host.parserAttributeAspects; - layoutTerms = host.layoutTerms; - prefixesForTerminals = host.prefixesForTerminals; - componentGrammarMarkingTerminals = host.componentGrammarMarkingTerminals; - }, - host.disambiguationClasses))) ++ - implode("", map(xmlCopperRef, ext.disambiguationClasses)) ++ -" \n" ++ -" \n" ++ - ext.xmlCopper ++ --- All disambiguation classes go in the extension grammar for now, --- since they reference extension terminals. - implode("\n", map((.xmlCopper), host.disambiguationClasses)) ++ - implode("\n", map((.xmlCopper), ext.disambiguationClasses)) ++ -" \n" ++ -" \n\n" ++ - -"\n"; -} - - diff --git a/grammars/silver/modification/defaultattr/DefaultAttr.sv b/grammars/silver/modification/defaultattr/DefaultAttr.sv deleted file mode 100644 index 00c7a011a..000000000 --- a/grammars/silver/modification/defaultattr/DefaultAttr.sv +++ /dev/null @@ -1,99 +0,0 @@ -grammar silver:modification:defaultattr; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:definition:type:syntax; ---import silver:analysis:typechecking:core; -import silver:translation:java; - -import silver:definition:flow:driver only ProductionGraph, FlowType, constructDefaultProductionGraph; -- for the "oh no again!" hack below -import silver:driver:util only RootSpec; -- ditto - -terminal Default_kwd 'default' lexer classes {KEYWORD, RESERVED}; - -concrete production aspectDefaultProduction -top::AGDcl ::= 'aspect' 'default' 'production' - lhs::Name '::' te::TypeExpr '::=' body::ProductionBody -{ - top.unparse = "aspect default production\n" ++ lhs.unparse ++ "::" ++ te.unparse ++ " ::=\n" ++ body.unparse; - - top.defs := []; - - production namedSig :: NamedSignature = - namedSignature(top.grammarName ++ ":default" ++ te.typerep.typeName, [], - namedSignatureElement(lhs.name, te.typerep), - annotationsForNonterminal(te.typerep, top.env)); - - propagate errors, flowDefs; - - local fakedDefs :: [Def] = - [defaultLhsDef(top.grammarName, lhs.location, lhs.name, te.typerep)]; - - local sigDefs :: [Def] = - addNewLexicalTyVars_ActuallyVariables(top.grammarName, top.location, te.lexicalTypeVariables); - - -- oh no again! - local myFlow :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).grammarFlowTypes; - local myProds :: EnvTree = head(searchEnvTree(top.grammarName, top.compiledGrammars)).productionFlowGraphs; - - local myFlowGraph :: ProductionGraph = - constructDefaultProductionGraph(namedSig, body.flowDefs, top.env, myProds, myFlow); - - - body.env = newScopeEnv(fakedDefs ++ sigDefs, top.env); - body.frame = defaultAspectContext(namedSig, myFlowGraph); - - body.downSubst = emptySubst(); - - top.setupInh := body.setupInh; -- Probably should be empty? - top.initProd := "\t\t//ASPECT DEFAULT PRODUCTION for " ++ te.unparse ++ "\n" ++ body.translation; - top.valueWeaving := body.valueWeaving; -- Probably should be empty? -} - -function defaultLhsDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(defaultLhsDcl(sg,sl,fn,ty))); -} -abstract production defaultLhsDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = lhsReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- TODO: be smarter about the error message - top.defLHSDispatcher = defaultLhsDefLHS(_, location=_); -} - -abstract production defaultLhsDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.name = q.name; - top.unparse = q.unparse; - top.found = !existingProblems && top.defLHSattr.attrDcl.isSynthesized; - - local existingProblems :: Boolean = !top.defLHSattr.found || top.typerep.isError; - - top.errors := - if existingProblems || top.found then [] - else [err(q.location, "Cannot define inherited attribute '" ++ top.defLHSattr.name ++ "' on the lhs '" ++ q.name ++ "'")]; - - top.typerep = q.lookupValue.typerep; - - top.translation = makeNTClassName(top.frame.lhsNtName) ++ ".defaultSynthesizedAttributes"; -} - -abstract production defaultAspectContext -top::BlockContext ::= sig::NamedSignature g::ProductionGraph -{ - top.fullName = sig.fullName; - top.lhsNtName = sig.outputElement.typerep.typeName; - top.signature = sig; - top.flowGraph = g; -} - diff --git a/grammars/silver/modification/defaultattr/DocConfig.sv b/grammars/silver/modification/defaultattr/DocConfig.sv deleted file mode 100644 index 406ce4499..000000000 --- a/grammars/silver/modification/defaultattr/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:defaultattr; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Default Attribute\nmenu_title: Default Attribute\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/ffi/DocConfig.sv b/grammars/silver/modification/ffi/DocConfig.sv deleted file mode 100644 index eecb8ed5c..000000000 --- a/grammars/silver/modification/ffi/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:ffi; - -{@config - header:"---\nlayout: sv_wiki\ntitle: FFI\nmenu_title: FFI\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/ffi/FunctionDcl.sv b/grammars/silver/modification/ffi/FunctionDcl.sv deleted file mode 100644 index d6733e6a1..000000000 --- a/grammars/silver/modification/ffi/FunctionDcl.sv +++ /dev/null @@ -1,57 +0,0 @@ -grammar silver:modification:ffi; - -terminal FFI_kwd 'foreign' lexer classes {KEYWORD,RESERVED}; - -nonterminal FFIDefs with config, location, grammarName, errors, env, unparse; -nonterminal FFIDef with config, location, grammarName, errors, env, unparse; - -concrete production functionDclFFI -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody 'foreign' '{' ffidefs::FFIDefs '}' -{ - top.unparse = "function " ++ id.unparse ++ "\n" ++ ns.unparse ++ "\n" ++ body.unparse ++ " foreign {\n" ++ ffidefs.unparse ++ "}"; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - production namedSig :: NamedSignature = ns.namedSignature; - - top.errors <- ffidefs.errors; - - -- Quick copy & paste to make signatures look right. Otherwise they contain errorTypes for - -- type parameters - production attribute sigDefs :: [Def] with ++; - sigDefs := ns.defs; - ns.signatureName = fName; - ns.env = newScopeEnv(sigDefs, top.env); - production attribute allLexicalTyVars :: [String]; - allLexicalTyVars = makeSet(ns.lexicalTypeVariables); - sigDefs <- addNewLexicalTyVars(top.grammarName, top.location, allLexicalTyVars); - - -- TODO this is a BS use of forwarding and should be eliminated. body.env and .frame are all wrong locally... - - forwards to functionDcl($1, id, ns, body, location=top.location); -} - -concrete production ffidefsOne -top::FFIDefs ::= one::FFIDef -{ - top.unparse = one.unparse; - - top.errors := one.errors; -} - -concrete production ffidefsMany -top::FFIDefs ::= one::FFIDef more::FFIDefs -{ - top.unparse = one.unparse ++ more.unparse; - - top.errors := one.errors ++ more.errors; -} - -concrete production ffidef -top::FFIDef ::= name::String_t ':' 'return' code::String_t ';' -{ - top.unparse = name.lexeme ++ ": return " ++ code.lexeme ++ ";\n"; - - top.errors := []; - -- Up to each translation to do something with this. -} - diff --git a/grammars/silver/modification/ffi/Project.sv b/grammars/silver/modification/ffi/Project.sv deleted file mode 100644 index 77aef3dd2..000000000 --- a/grammars/silver/modification/ffi/Project.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:ffi; - -exports silver:modification:ffi:java with silver:translation:java:core; - -exports silver:modification:ffi:java with silver:translation:java:type; - diff --git a/grammars/silver/modification/ffi/Type.sv b/grammars/silver/modification/ffi/Type.sv deleted file mode 100644 index a238fbe45..000000000 --- a/grammars/silver/modification/ffi/Type.sv +++ /dev/null @@ -1,25 +0,0 @@ -grammar silver:modification:ffi; - -abstract production foreignType -top::Type ::= fn::String transType::String params::[Type] -{ - top.freeVariables = setUnionTyVarsAll(map((.freeVariables), params)); - top.substituted = foreignType(fn, transType, mapSubst(params, top.substitution)); - top.flatRenamed = foreignType(fn, transType, mapRenameSubst(params, top.substitution)); - top.typepp = fn ++ if !null(params) then "<" ++ implode(" ", mapTypePP(params, top.boundVariables)) ++ ">" else ""; - - -- Unification.sv - top.unify = - case top.unifyWith of - | foreignType(ofn, _, op) -> - if fn == ofn - then unifyAll( params, op ) - else errorSubst("Tried to unify conflicting foreign types " ++ fn ++ " and " ++ ofn) - | _ -> errorSubst("Tried to unify foreign type " ++ fn ++ " with " ++ prettyType(top.unifyWith)) - end; -} - --- What we get from the standard library's declaration, so we don't need to repeat it -global ioForeignType :: Type = - foreignType("core:IO", "common.IOToken", []); - diff --git a/grammars/silver/modification/ffi/java/FunctionDcl.sv b/grammars/silver/modification/ffi/java/FunctionDcl.sv deleted file mode 100644 index 32e1ac351..000000000 --- a/grammars/silver/modification/ffi/java/FunctionDcl.sv +++ /dev/null @@ -1,79 +0,0 @@ -grammar silver:modification:ffi:java; - -import silver:modification:ffi; -import silver:modification:ffi:util; -import silver:translation:java:core; -import silver:translation:java:type; -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; - -synthesized attribute ffiTranslationString :: [String] occurs on FFIDef, FFIDefs; - -aspect production ffidefsOne -top::FFIDefs ::= one::FFIDef -{ - top.ffiTranslationString = one.ffiTranslationString; -} - -aspect production ffidefsMany -top::FFIDefs ::= one::FFIDef more::FFIDefs -{ - top.ffiTranslationString = one.ffiTranslationString ++ more.ffiTranslationString; -} - -aspect production ffidef -top::FFIDef ::= name::String_t ':' 'return' code::String_t ';' -{ - top.ffiTranslationString = if name.lexeme == "\"java\"" then [cleanStringLexeme(code.lexeme)] else []; -} - -function strictChildAccessor -String ::= ns::NamedSignatureElement -{ - return "((" ++ ns.typerep.transType ++ ")common.Util.demand(c_" ++ ns.elementName ++ "))"; -} -function lazyChildAccessor -String ::= ns::NamedSignatureElement -{ - return "c_" ++ ns.elementName; -} - -function computeSigTranslation -String ::= str::String sig::NamedSignature -{ - return substituteAll( - substituteAll(str, - map(wrapStrictNotation, sig.inputNames), - map(strictChildAccessor, sig.inputElements)), - map(wrapLazyNotation, sig.inputNames), - map(lazyChildAccessor, sig.inputElements)); -} - - --- TODO: this needs clean up. --- We should forward either to normal function declaration or special one, rather than IFing EVERYTHING in here! -aspect production functionDclFFI -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody 'foreign' '{' ffidefs::FFIDefs '}' -{ - top.setupInh := if null(ffidefs.ffiTranslationString) - then forward.setupInh - else ""; -- hacky hacky! -- TODO should these be there, or empty? - top.initProd := if null(ffidefs.ffiTranslationString) - then forward.initProd - else ""; -- hacky hacky! -- TODO should these be there, or empty? - top.initValues := ""; - top.postInit := ""; - - top.genFiles := if null(ffidefs.ffiTranslationString) - then forward.genFiles - else - [pair("P" ++ id.name ++ ".java", - generateFunctionClassString(top.grammarName, id.name, namedSig, "return (" ++ namedSig.outputElement.typerep.transType ++ ")" ++ computeSigTranslation(head(ffidefs.ffiTranslationString), namedSig) ++ ";\n") - )]; - - top.errors <- if length(ffidefs.ffiTranslationString) > 1 - then [err(ffidefs.location, "There is more than one Java translation in this FFI function")] - else []; -} - diff --git a/grammars/silver/modification/ffi/java/Type.sv b/grammars/silver/modification/ffi/java/Type.sv deleted file mode 100644 index f7d9e1db6..000000000 --- a/grammars/silver/modification/ffi/java/Type.sv +++ /dev/null @@ -1,19 +0,0 @@ -grammar silver:modification:ffi:java; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:translation:java:type; -import silver:modification:ffi; - -aspect production foreignType -top::Type ::= fn::String transType::String params::[Type] -{ - top.transType = transType; - top.transClassType = transType; - top.transTypeRep = - s"new common.BaseTypeRep(\"${fn}\", new common.TypeRep[] {${implode(", ", map((.transTypeRep), params))}})"; - top.transFreshTypeRep = - s"new common.BaseTypeRep(\"${fn}\", new common.TypeRep[] {${implode(", ", map((.transFreshTypeRep), params))}})"; -} - diff --git a/grammars/silver/modification/impide/BuildProcess.sv b/grammars/silver/modification/impide/BuildProcess.sv deleted file mode 100644 index 98452416a..000000000 --- a/grammars/silver/modification/impide/BuildProcess.sv +++ /dev/null @@ -1,112 +0,0 @@ -grammar silver:modification:impide; - -import silver:driver; -import silver:translation:java:driver; -import silver:translation:java:core only makeParserName, makeName, makeClassName, makeNTClassName; - -import silver:util:cmdargs; - -{-- - The file where production compilation (used to be called buildWriteFile) is originally - defined in "silver/translation/java/driver/BuildProcess.sv"; here we're just aspecting - that, using '<-' to contribute things to the production attributes declared there. ---} - -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - -- The RootSpec representing the grammar actually being built (specified on the command line) - local builtGrammar :: [Decorated RootSpec] = searchEnvTree(buildGrammar, g.compiledGrammars); - - -- Empty if no ide decl in that grammar, otherwise has at least one spec... note that - -- we're going to go with assuming there's just one IDE declaration... - local ide :: IdeSpec = head(head(builtGrammar).ideSpecs); - local isIde :: Boolean = !null(builtGrammar) && !null(head(builtGrammar).ideSpecs); - - local pkgName :: String = makeName(ide.pluginGrammar); -- should be equal to buildGrammar - - -- $${jg}/ide/$${ide.pkg.name} - local ideGenPath :: String = s"${benv.silverGen}ide/${pkgName}"; - top.postOps <- if !isIde then [] else [generateNCS(g.compiledGrammars, ide, ideGenPath, pkgName)]; - - classpathCompiler <- if !isIde then [] else ["${sh}/jars/IDEPluginRuntime.jar"]; - - extraTopLevelDecls <- if !isIde then [] else [ - s""" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -${implode("", map(copyIdeResource, ide.ideResources))} - -""" - ]; - - extraDistDeps <- if !isIde then [] else ["ide"]; -- Here's where we demand that target be built ('dist' is a dummy target that just depends on 'jars' initially) - - -- We're not actually using the generated (by silver / ant) jar as an OSGi bundle - -- and if we ever do, we should probably put this as part of the normal build process instead. - -- extraManifestAttributes <- -} - - -function toUpperCase -String ::= original::String -{ - return error("Not Yet Implemented: toUpperCase"); -} foreign { - "java" : return "(new common.StringCatter(%original%.toString().toUpperCase()))"; -} - -function pkgToPath -String ::= pkg::String -{ - return substitute(".", "/", pkg); -} - -function copyIdeResource -String ::= r::Pair -{ - return s""" - - - - -"""; -} diff --git a/grammars/silver/modification/impide/BuildProcess2.sv b/grammars/silver/modification/impide/BuildProcess2.sv deleted file mode 100644 index 96ee18872..000000000 --- a/grammars/silver/modification/impide/BuildProcess2.sv +++ /dev/null @@ -1,39 +0,0 @@ -grammar silver:modification:impide; - -import silver:driver; -import silver:translation:java; -import silver:util:cmdargs; - -{-- - - @param grams Compiled grammars (used to generate parser) - - @param ide The ide specification to generate files for. - - @param ideGenPath The path to the ide generation directory - - (e.g. generated/ide/silver.composed.idetest) - - (contains: plugin feature updatesite) - -} -abstract production generateNCS -top::DriverAction ::= grams::EnvTree ide::IdeSpec ideGenPath::String pkgName::String -{ - ide.compiledGrammars = grams; - - local io0::IO = print("[IDE plugin] Generating IDE plugin.\n", top.ioIn); - local io1::IO = deleteTree(ideGenPath, io0); - local io2::IO = - mkdirs(s"${ideGenPath}/plugin/src/${pkgToPath(pkgName)}/", - ["imp/coloring", "eclipse/property", "eclipse/wizard/newproject", "eclipse/wizard/newfile"], io1); - local io3::IO = writeFiles(ideGenPath ++ "/plugin/", ide.pluginFiles, io2); - - top.io = io3; - - top.code = 0; - top.order = 7; -} - -function mkdirs -IO ::= path::String paths::[String] i::IO -{ - return if null(paths) then i - else mkdirs(path, tail(paths), - mkdir(path ++ head(paths), i).io); -} - diff --git a/grammars/silver/modification/impide/DocConfig.sv b/grammars/silver/modification/impide/DocConfig.sv deleted file mode 100644 index f3d39c81d..000000000 --- a/grammars/silver/modification/impide/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:impide; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Imp IDE\nmenu_title: Imp IDE\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/impide/Env.sv b/grammars/silver/modification/impide/Env.sv deleted file mode 100644 index 282291e15..000000000 --- a/grammars/silver/modification/impide/Env.sv +++ /dev/null @@ -1,107 +0,0 @@ -grammar silver:modification:impide; - --------------------------------------------------------------------------------- --- DclInfo.sv - --- We actually don't need to put font info on this, do we? cool! - -abstract production fontDcl -top::DclInfo ::= sg::String sl::Location fn::String -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = error("Internal compiler error: font style do not have types"); -} - --------------------------------------------------------------------------------- --- Defs.sv - -synthesized attribute fontDefList :: [EnvItem] occurs on Defs, Def; - -aspect production nilDefs -top::Defs ::= -{ - top.fontDefList = []; -} - -aspect production consDefs -top::Defs ::= e1::Def e2::Defs -{ - top.fontDefList = e1.fontDefList ++ e2.fontDefList; -} - -aspect default production -top::Def ::= -{ - top.fontDefList = []; -} - -abstract production fontStyleDef -top::Def ::= d::EnvItem -{ - top.dcl = d.dcl; - top.fontDefList = [d]; -} - --- TODO: we don't do any renaming of fonts or anything - -function fontDef -Def ::= sg::String sl::Location fn::String -{ - return fontStyleDef(defaultEnvItem(fontDcl(sg,sl,fn))); -} - --------------------------------------------------------------------------------- --- Env.sv - -synthesized attribute fontDefTree :: EnvScope occurs on Env; - -aspect production i_emptyEnv -top::Env ::= -{ - top.fontDefTree = emptyEnvScope(); -} - -aspect production i_appendEnv -top::Env ::= e1::Decorated Env e2::Decorated Env -{ - top.fontDefTree = appendEnvScope(e1.fontDefTree, e2.fontDefTree); -} - -aspect production i_newScopeEnv -top::Env ::= d::Defs e::Decorated Env -{ - top.fontDefTree = consEnvScope(buildTree(d.fontDefList), e.fontDefTree); -} - -aspect production i_occursEnv -top::Env ::= _ e::Decorated Env -{ - top.fontDefTree = e.fontDefTree; -} - -function getFontDcl -[DclInfo] ::= search::String e::Decorated Env -{ - return searchEnvScope(search, e.fontDefTree); -} - --------------------------------------------------------------------------------- --- QName.sv - -aspect production qNameId -top::QName ::= id::Name -{ - top.lookupFont = decorate customLookup("font style", getFontDcl(top.name, top.env), top.name, top.location) with {}; -} - -aspect production qNameCons -top::QName ::= id::Name ':' qn::QName -{ - top.lookupFont = decorate customLookup("font style", getFontDcl(top.name, top.env), top.name, top.location) with {}; -} - -synthesized attribute lookupFont :: Decorated QNameLookup occurs on QName; - diff --git a/grammars/silver/modification/impide/FontDecl.sv b/grammars/silver/modification/impide/FontDecl.sv deleted file mode 100644 index 6fce7835c..000000000 --- a/grammars/silver/modification/impide/FontDecl.sv +++ /dev/null @@ -1,69 +0,0 @@ -grammar silver:modification:impide; - -terminal ImpFont_t 'temp_imp_ide_font' lexer classes {KEYWORD,RESERVED}; -terminal Color_kwd 'color' lexer classes {KEYWORD}; -terminal Bold_kwd 'bold' lexer classes {KEYWORD}; -terminal Italic_kwd 'italic' lexer classes {KEYWORD}; - -concrete production fontDecl -top::AGDcl ::= 'temp_imp_ide_font' id::Name 'color' '(' r::Int_t ',' g::Int_t ',' b::Int_t ')' fontStyles::FontStyles ';' -{ - top.unparse = "temp_imp_ide_font " ++ id.name ++ " color(" ++ r.lexeme ++ ", " ++ g.lexeme ++ ", " ++ b.lexeme ++ ")" ++ fontStyles.unparse ++ ";\n"; - - production fName :: String = top.grammarName ++ ":" ++ id.name; - - top.errors := if length(getFontDcl(fName, top.env)) > 1 - then [err(id.location, "Font style '" ++ fName ++ "' is already bound.")] - else []; - - -- TODO: Add a way to set this via forward - top.syntaxAst := [syntaxFont( - fName, - font(makeColor(toInteger(r.lexeme),toInteger(g.lexeme),toInteger(b.lexeme)), - fontStyles.isBold, - fontStyles.isItalic) - )]; - - forwards to defsAGDcl([fontDef(top.grammarName, top.location, fName)], location=top.location); -} - -monoid attribute isBold :: Boolean with false, ||; -monoid attribute isItalic :: Boolean with false, ||; - -nonterminal FontStyles with isBold, isItalic, unparse; -propagate isBold, isItalic on FontStyles, FontStyle; - -concrete production consFontStylesDcl -top::FontStyles ::= h::FontStyle t::FontStyles -{ - top.unparse = h.unparse ++ t.unparse; -} -concrete production nilFontStylesDcl -top::FontStyles ::= -{ - top.unparse = ""; -} - -closed nonterminal FontStyle with unparse, isBold, isItalic; - -aspect default production -top::FontStyle ::= -{ - propagate isBold, isItalic; -} - -concrete production fontStyleBoldDcl -top::FontStyle ::= 'bold' -{ - top.unparse = " bold"; - top.isBold <- true; -} -concrete production fontStyleItalicDcl -top::FontStyle ::= 'italic' -{ - top.unparse = " italic"; - top.isItalic <- true; -} - --- temp_imp_ide_font KeywordFont color(255,0,0) bold; - diff --git a/grammars/silver/modification/impide/FontModifiers.sv b/grammars/silver/modification/impide/FontModifiers.sv deleted file mode 100644 index 54971177c..000000000 --- a/grammars/silver/modification/impide/FontModifiers.sv +++ /dev/null @@ -1,25 +0,0 @@ -grammar silver:modification:impide; - --- terminal's font in IDE -terminal Font_kwd 'font' lexer classes {KEYWORD}; - -concrete production terminalModifierFont -top::TerminalModifier ::= 'font' '=' id::QName -{ - top.unparse = "font = " ++ id.name; - - top.terminalModifiers := [termFont(id.lookupFont.fullName)]; - top.errors := id.lookupFont.errors; - propagate silver:modification:copper:lexerClasses; -} - --- Allows fonts on lexer classes, too! -concrete production lexerClassModifierFont -top::LexerClassModifier ::= 'font' '=' id::QName -{ - top.unparse = "font = " ++ id.name; - - top.lexerClassModifiers := [lexerClassFont(id.lookupFont.fullName)]; - top.errors := id.lookupFont.errors; -} - diff --git a/grammars/silver/modification/impide/IdeDecl.sv b/grammars/silver/modification/impide/IdeDecl.sv deleted file mode 100644 index d97721ab6..000000000 --- a/grammars/silver/modification/impide/IdeDecl.sv +++ /dev/null @@ -1,355 +0,0 @@ -grammar silver:modification:impide; - -import silver:modification:copper_mda only findSpec; -- TODO -import silver:driver:util only RootSpec; -import silver:extension:list; -import silver:analysis:typechecking:core; -import silver:modification:ffi; -import silver:definition:type; - -autocopy attribute startNTName :: String; - --- We're going to make this an especially annoying looking declaration --- to emphasize that this is currently a temporary hack just to get things --- moving. -terminal ImpIde_t 'temp_imp_ide_dcl' lexer classes {KEYWORD,RESERVED}; - -terminal ImpIde_ProdInfo_Name_t 'name' lexer classes {KEYWORD}; -terminal ImpIde_ProdInfo_Version_t 'version' lexer classes {KEYWORD}; - -terminal ImpIde_OptFunc_Builder 'builder' lexer classes {KEYWORD}; -terminal ImpIde_OptFunc_PostBuilder 'postbuilder' lexer classes {KEYWORD}; -terminal ImpIde_OptFunc_Exporter 'exporter' lexer classes {KEYWORD}; -terminal ImpIde_OptFunc_Folder 'folder' lexer classes {KEYWORD}; - -terminal ImpIde_IdeResource 'resource' lexer classes {KEYWORD}; - -terminal ImpIde_Wizard 'wizard' lexer classes {KEYWORD}; -terminal ImpIde_Wizard_StubGen 'stub generator' lexer classes {KEYWORD}; -terminal ImpIde_Wizard_NewFile 'new file' lexer classes {KEYWORD}; - - - -concrete production ideDcl -top::AGDcl ::= 'temp_imp_ide_dcl' parsername::QName fileextension::String_t stmts::IdeStmts -{ - top.unparse = "temp_imp_ide_dcl " ++ parsername.unparse ++ " " ++ fileextension.lexeme ++ "\n"; -- TODO not finished - - top.defs := []; - - top.errors := parsername.lookupValue.errors; - - -- lexeme starts with ", but also ensure first character is a dot. - top.errors <- - if startsWith("\".", fileextension.lexeme) then [] - else [err(fileextension.location, "File extension should begin with dot (like \".sv\")")]; - - -- This gets the compiler's representation of the grammar the parser is declared in - -- This should NOT be accessed unless we know the lookup for the name succeeded - -- since we're unconditionally calling 'head' here! - local attribute parsergrammar :: Decorated RootSpec; - parsergrammar = head(searchEnvTree(parsername.lookupValue.dcl.sourceGrammar, top.compiledGrammars)); - - -- This looks up the actual specification of the parser in that grammar. - local attribute spec :: [ParserSpec]; - spec = findSpec(parsername.lookupValue.fullName, parsergrammar.parserSpecs); - - stmts.startNTName = head(spec).startNT; - - -- If there were errors looking up the name, do nothing. If we couldn't find the - -- parser, then raise the error message noting that the name isn't a parser! - top.errors <- if !parsername.lookupValue.found || !null(spec) then [] - else [err(parsername.location, parsername.name ++ " is not a parser.")]; - - -- Strip off the quotes AND the initial dot - local fext :: String = substring(2, length(fileextension.lexeme) - 1, fileextension.lexeme); - - local ideName :: String = - if null(stmts.ideNames) then deriveLangNameFromGrammar(top.grammarName) else head(stmts.ideNames); - top.errors <- if length(stmts.ideNames) > 1 then [err(top.location, "Multiple name declarations")] else []; - local ideVersion :: String = - if null(stmts.ideVersions) then "1.0.0" else head(stmts.ideVersions); - top.errors <- if length(stmts.ideVersions) > 1 then [err(top.location, "Multiple version declarations")] else []; - - - top.ideSpecs = [ideSpec(top.grammarName, ideName, ideVersion, fext, stmts.ideFunctions, stmts.propDcls, stmts.wizards, head(spec), stmts.ideResources)]; - - top.errors <- stmts.errors; - - forwards to emptyAGDcl(location=top.location); -} - -function deriveLangNameFromGrammar -String ::= gram::String -{ - return toUpperCase(head(explode(":", gram))); -} - --- funcDcls, propDcls and optDcls are defined in ./IdeSpec.sv -nonterminal IdeStmts with env, location, errors, grammarName, ideFunctions, propDcls, wizards, startNTName, ideNames, ideVersions, ideResources; -nonterminal IdeStmt with env, location, errors, grammarName, ideFunctions, propDcls, wizards, startNTName, ideNames, ideVersions, ideResources; -nonterminal IdeStmtList with env, location, errors, grammarName, ideFunctions, propDcls, wizards, startNTName, ideNames, ideVersions, ideResources; - -synthesized attribute ideNames :: [String]; -synthesized attribute ideVersions :: [String]; -synthesized attribute ideFunctions :: [IdeFunction]; -synthesized attribute wizards :: [IdeWizardDcl]; -synthesized attribute propDcls :: [IdeProperty]; - -concrete production emptyIdeStmts -top::IdeStmts ::= ';' -{ - top.errors := []; - top.ideFunctions = []; - top.propDcls = []; - top.wizards = []; - top.ideNames = []; - top.ideVersions = []; - top.ideResources = []; -} - -concrete production listIdeStmts -top::IdeStmts ::= '{' stmtList::IdeStmtList '}' -{ - top.errors := stmtList.errors; - top.ideFunctions = stmtList.ideFunctions; - top.propDcls = stmtList.propDcls; - top.wizards = stmtList.wizards; - top.ideNames = stmtList.ideNames; - top.ideVersions = stmtList.ideVersions; - top.ideResources = stmtList.ideResources; -} - --- with optional ending ';' -concrete production listIdeStmts2 -top::IdeStmts ::= '{' stmtList::IdeStmtList '}' ';' -{ - top.errors := stmtList.errors; - top.ideFunctions = stmtList.ideFunctions; - top.propDcls = stmtList.propDcls; - top.wizards = stmtList.wizards; - top.ideNames = stmtList.ideNames; - top.ideVersions = stmtList.ideVersions; - top.ideResources = stmtList.ideResources; -} - -concrete production nilIdeStmtList -top::IdeStmtList ::= -{ - top.errors := []; - top.ideFunctions = []; - top.propDcls = []; - top.wizards = []; - top.ideNames = []; - top.ideVersions = []; - top.ideResources = []; -} - -concrete production consIdeStmtList -top::IdeStmtList ::= stmt::IdeStmt stmtList::IdeStmtList -{ - top.errors := stmt.errors ++ stmtList.errors; - top.ideFunctions = stmt.ideFunctions ++ stmtList.ideFunctions; - top.propDcls = stmt.propDcls ++ stmtList.propDcls; - top.wizards = stmt.wizards ++ stmtList.wizards; - top.ideNames = stmt.ideNames ++ stmtList.ideNames; - top.ideVersions = stmt.ideVersions ++ stmtList.ideVersions; - top.ideResources = stmt.ideResources ++ stmtList.ideResources; -} - -aspect default production -top::IdeStmt ::= -{ - top.ideFunctions = []; - top.propDcls = []; - top.wizards = []; - top.ideNames = []; - top.ideVersions = []; - top.ideResources = []; -} - --- Helpers for writing expected types -global t_iomsgs :: Type = nonterminalType("core:IOVal", [listType(nonterminalType("silver:langutil:Message", []))]); -global t_props :: Type = listType(nonterminalType("ide:IdeProperty", [])); -global t_io :: Type = ioForeignType; -global t_proj :: Type = foreignType("ide:IdeProject", "Object", []); -global t_loc :: Type = nonterminalType("core:Location", []); - -concrete production makeIdeStmt_Builder -top::IdeStmt ::= 'builder' builderName::QName ';' -{ - top.ideFunctions = [builderFunction(builderName.lookupValue.fullName)]; - top.errors := builderName.lookupValue.errors; - - -- IOVal<[Message]> ::= IdeProject [IdeProperty] IO - local expectedType :: Type = - functionType(t_iomsgs, [t_proj, t_props, t_io], []); - - local tc1 :: TypeCheck = check(freshenCompletely(builderName.lookupValue.typerep), expectedType); - tc1.downSubst = emptySubst(); - tc1.finalSubst = tc1.upSubst; - - top.errors <- - if !tc1.typeerror then [] - else [err(builderName.location, "Builder function should have type:\n\t" ++ tc1.rightpp - ++ "\nInstead it has the type:\n\t" ++ tc1.leftpp)]; -} - -concrete production makeIdeStmt_PostBuilder -top::IdeStmt ::= 'postbuilder' postbuilderName::QName ';' -{ - top.ideFunctions = [postbuilderFunction(postbuilderName.lookupValue.fullName)]; - top.errors := postbuilderName.lookupValue.errors; - - -- IOVal<[Message]> ::= IdeProject [IdeProperty] IO - local expectedType :: Type = - functionType(t_iomsgs, [t_proj, t_props, t_io], []); - - local tc1 :: TypeCheck = check(freshenCompletely(postbuilderName.lookupValue.typerep), expectedType); - tc1.downSubst = emptySubst(); - tc1.finalSubst = tc1.upSubst; - - top.errors <- - if !tc1.typeerror then [] - else [err(postbuilderName.location, "Post-builder function should have type:\n\t" ++ tc1.rightpp - ++ "\nInstead it has the type:\n\t" ++ tc1.leftpp)]; -} - -concrete production makeIdeStmt_Exporter -top::IdeStmt ::= 'exporter' exporterName::QName ';' -{ - top.ideFunctions = [exporterFunction(exporterName.lookupValue.fullName)]; - top.errors := exporterName.lookupValue.errors; - - -- IOVal<[Message]> ::= IdeProject [IdeProperty] IO - local expectedType :: Type = - functionType(t_iomsgs, [t_proj, t_props, t_io], []); - - local tc1 :: TypeCheck = check(freshenCompletely(exporterName.lookupValue.typerep), expectedType); - tc1.downSubst = emptySubst(); - tc1.finalSubst = tc1.upSubst; - - top.errors <- - if !tc1.typeerror then [] - else [err(exporterName.location, "Exporter function should have type:\n\t" ++ tc1.rightpp - ++ "\nInstead it has the type:\n\t" ++ tc1.leftpp)]; -} - -concrete production makeIdeStmt_Folder -top::IdeStmt ::= 'folder' folderName::QName ';' -{ - top.ideFunctions = [folderFunction(folderName.lookupValue.fullName)]; - top.errors := folderName.lookupValue.errors; - - -- [Location] ::= <> - local expectedType :: Type = - functionType(listType(t_loc), [nonterminalType(top.startNTName, [])], []); - - local tc1 :: TypeCheck = check(freshenCompletely(folderName.lookupValue.typerep), expectedType); - tc1.downSubst = emptySubst(); - tc1.finalSubst = tc1.upSubst; - - top.errors <- - if !tc1.typeerror then [] - else [err(folderName.location, "Folder function for this language should have type:\n\t" ++ tc1.rightpp - ++ "\nInstead it has the type:\n\t" ++ tc1.leftpp)]; -} - ---- Allows declarations of properties for the project. -concrete production makeIdeStmt_Porperty -top::IdeStmt ::= prop::Property -{ - top.propDcls = prop.propDcls; - top.errors := prop.errors; -} - -concrete production nameIdeStmt -top::IdeStmt ::= 'name' ideName::String_t ';' -{ - -- Strip off the quotes - local iName :: String = substring(1, length(ideName.lexeme) - 1, ideName.lexeme); - - top.errors := - if iName == "" then - [wrn(ideName.location, "The name of IDE product is empty. A default name will be used.")] -- TODO: this will basically never fire. move to top level declaration. - else if isDigit(substring(0,1,iName)) then - [err(ideName.location, "The name of IDE product cannot be started with digital.")] - else []; - - top.ideNames = [iName]; -} - -concrete production versionIdeStmt -top::IdeStmt ::= 'version' v::String_t ';' -{ - -- Strip off the quotes - local iV :: String = substring(1, length(v.lexeme) - 1, v.lexeme); - - top.errors := - if iV == "" then - [wrn(v.location, "The version of IDE product is empty. A default version number will be used.")] -- TODO: this will basically never fire. move to top level declaration. - else if !isLegalVersion(iV) then - [err(v.location, "The version of IDE product must comply to the format \"N+.N+\" or \"N+.N+.N+\".")] - else []; - - top.ideVersions = [iV]; -} - -concrete production resourceIdeStmt -top::IdeStmt ::= 'resource' id::Name path::String_t ';' -{ - top.errors := []; -- TODO: duplicate name check or something? - top.ideResources = [pair(id.name, substring(1, length(path.lexeme) - 1, path.lexeme))]; -} - --- Wizards - -concrete production newfileWizard_c -top::IdeStmt ::= 'wizard' 'new file' '{' generator::StubGenerator props::PropertyList '}' -{ - top.wizards = [newfileWizard(generator.funcDcl, props.propDcls)]; - top.errors := generator.errors ++ props.errors; -} - - -nonterminal StubGenerator with env, funcDcl, errors; - -synthesized attribute funcDcl :: String; - -concrete production makeStubGenerator -top::StubGenerator ::= 'stub generator' genName::QName ';' -{ - top.funcDcl = genName.lookupValue.fullName; - - -- String ::= [IdeProperty] - local stubGenTypeExpected :: Type = - functionType( - stringType(), -- return type - [listType(nonterminalType("ide:IdeProperty", []))], -- argument type list - []); - - local tc1 :: TypeCheck = check(freshenCompletely(genName.lookupValue.typerep), stubGenTypeExpected); - tc1.downSubst = emptySubst(); - tc1.finalSubst = tc1.upSubst; - - top.errors := - if !tc1.typeerror then [] - else [err(genName.location, "Stub generator should have type:\n\t" ++ tc1.rightpp - ++ "\nInstead it has the type:\n\t" ++ tc1.leftpp)]; -} - - -function isLegalVersion -Boolean ::= ver::String -{ - local parts::[String] = explode(".", ver); - - return (length(parts) == 2 || length(parts) == 3) && isAllDigital(parts); -} - -function isAllDigital -Boolean ::= parts::[String] -{ - return null(parts) || isDigit(head(parts)) && isAllDigital(tail(parts)); -} - diff --git a/grammars/silver/modification/impide/Project.sv b/grammars/silver/modification/impide/Project.sv deleted file mode 100644 index 6a8790d10..000000000 --- a/grammars/silver/modification/impide/Project.sv +++ /dev/null @@ -1,14 +0,0 @@ -grammar silver:modification:impide; - -imports silver:definition:core; -imports silver:definition:env; - -imports silver:definition:concrete_syntax; -imports silver:modification:copper; - ---imports silver:definition:concrete_syntax:ast; -imports silver:modification:impide:cstast; -imports silver:modification:impide:spec; - -imports silver:driver:util only grammarToPath; - diff --git a/grammars/silver/modification/impide/Properties.sv b/grammars/silver/modification/impide/Properties.sv deleted file mode 100644 index 229d7213f..000000000 --- a/grammars/silver/modification/impide/Properties.sv +++ /dev/null @@ -1,127 +0,0 @@ -grammar silver:modification:impide; - -terminal ImpIde_OptFunc_Property 'property' lexer classes {KEYWORD}; - -terminal ImpIde_PropType_string_t 'string' lexer classes {KEYWORD}; -terminal ImpIde_PropType_integer_t 'integer' lexer classes {KEYWORD}; -terminal ImpIde_PropType_path_t 'path' lexer classes {KEYWORD}; -terminal ImpIde_PropType_url_t 'url' lexer classes {KEYWORD}; - -terminal ImpIde_PropOption_Required_t 'required' lexer classes {KEYWORD}; -terminal ImpIde_PropOption_Display_t 'display' lexer classes {KEYWORD}; -terminal ImpIde_PropOption_Default_t 'default' lexer classes {KEYWORD}; - - -nonterminal PropertyList with propDcls, errors; -nonterminal Property with location, propDcls, errors; - -concrete production nilPropertyList -top::PropertyList ::= -{ - top.propDcls = []; - top.errors := []; -} - -concrete production consPropertyList -top::PropertyList ::= p::Property pList::PropertyList -{ - top.propDcls = p.propDcls ++ pList.propDcls; - top.errors := p.errors ++ pList.errors; -} - --- TODO: syntax? a bit inconsistent with rest of silver that we don't have at least commans between options -concrete production makeProperty -top::Property ::= 'property' pname::IdLower_t ptype::TypeName options::IdePropertyOptions ';' -{ - local optional::Boolean = null(options.propRequired); - local defaultVal::String = if null(options.propDefault) then "" else head(options.propDefault); - local displayName::String = if null(options.propDisplay) then pname.lexeme else head(options.propDisplay); - - top.propDcls = [makeIdeProperty(pname.lexeme, ptype.propType, optional, defaultVal, displayName)]; - - top.errors := []; -- TODO checking of default values corresponds to type? maybe... - top.errors <- if length(options.propRequired) > 1 then [err(top.location, "Multiple 'required' declarations")] else []; - top.errors <- if length(options.propDefault) > 1 then [err(top.location, "Multiple 'default' declarations")] else []; - top.errors <- if length(options.propDisplay) > 1 then [err(top.location, "Multiple 'display' declarations")] else []; -} - - -nonterminal TypeName with propType; - -synthesized attribute propType :: PropType; - -concrete production propType_String -top::TypeName ::= 'string' -{ - top.propType = stringPropType(); -} - -concrete production propType_Integer -top::TypeName ::= 'integer' -{ - top.propType = integerPropType(); -} - -concrete production propType_Path -top::TypeName ::= 'path' -{ - top.propType = pathPropType(); -} - -concrete production propType_URL -top::TypeName ::= 'url' -{ - top.propType = urlPropType(); -} - - -nonterminal IdePropertyOption with propRequired, propDefault, propDisplay; -nonterminal IdePropertyOptions with propRequired, propDefault, propDisplay; - -synthesized attribute propRequired :: [Boolean]; -synthesized attribute propDefault :: [String]; -synthesized attribute propDisplay :: [String]; - -concrete production nilPropertyOptions -top::IdePropertyOptions ::= -{ - top.propRequired = []; - top.propDefault = []; - top.propDisplay = []; -} - -concrete production consPropertyOptions -top::IdePropertyOptions ::= h::IdePropertyOption t::IdePropertyOptions -{ - top.propRequired = h.propRequired ++ t.propRequired; - top.propDefault = h.propDefault ++ t.propDefault; - top.propDisplay = h.propDisplay ++ t.propDisplay; -} - -aspect default production -top::IdePropertyOption ::= -{ - top.propRequired = []; - top.propDefault = []; - top.propDisplay = []; -} - -concrete production idePropertyOption_optional -top::IdePropertyOption ::= 'required' -{ - top.propRequired = [true]; -} - -concrete production idePropertyOption_defaultVal -top::IdePropertyOption ::= 'default' '=' str::String_t -{ - top.propDefault = [substring(1, length(str.lexeme) - 1, str.lexeme)]; -} - -concrete production idePropertyOption_displayName -top::IdePropertyOption ::= 'display' '=' str::String_t -{ - top.propDisplay = [substring(1, length(str.lexeme) - 1, str.lexeme)]; -} - - diff --git a/grammars/silver/modification/impide/Root.sv b/grammars/silver/modification/impide/Root.sv deleted file mode 100644 index 78f27ebfa..000000000 --- a/grammars/silver/modification/impide/Root.sv +++ /dev/null @@ -1,65 +0,0 @@ -grammar silver:modification:impide; - -import silver:driver; - -{-- - - This attribute will by accumulated up on each - -} -synthesized attribute ideSpecs :: [IdeSpec] occurs on RootSpec, Root, AGDcls, AGDcl, Grammar; - -aspect production root -top::Root ::= gdcl::GrammarDcl ms::ModuleStmts ims::ImportStmts ags::AGDcls -{ - top.ideSpecs = ags.ideSpecs; -} - -aspect production nilAGDcls -top::AGDcls ::= -{ - top.ideSpecs = []; -} -aspect production consAGDcls -top::AGDcls ::= h::AGDcl t::AGDcls -{ - top.ideSpecs = h.ideSpecs ++ t.ideSpecs; -} - -aspect default production -top::AGDcl ::= -{ - top.ideSpecs = []; -} -aspect production appendAGDcl -top::AGDcl ::= ag1::AGDcl ag2::AGDcl -{ - top.ideSpecs = ag1.ideSpecs ++ ag2.ideSpecs; -} - -aspect production grammarRootSpec -top::RootSpec ::= g::Grammar _ _ _ _ -{ - top.ideSpecs = g.ideSpecs; -} -aspect production interfaceRootSpec -top::RootSpec ::= _ _ _ -{ - top.ideSpecs = []; -- TODO: This, of course, means we're forgetting ide specs in interfaces -} -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - top.ideSpecs = []; -} - -aspect production nilGrammar -top::Grammar ::= -{ - top.ideSpecs = []; -} - -aspect production consGrammar -top::Grammar ::= h::Root t::Grammar -{ - top.ideSpecs = h.ideSpecs ++ t.ideSpecs; -} - diff --git a/grammars/silver/modification/impide/cstast/CstAst.sv b/grammars/silver/modification/impide/cstast/CstAst.sv deleted file mode 100644 index f6ff33734..000000000 --- a/grammars/silver/modification/impide/cstast/CstAst.sv +++ /dev/null @@ -1,22 +0,0 @@ -grammar silver:modification:impide:cstast; - -imports silver:definition:concrete_syntax:ast; -imports silver:definition:regex; -imports silver:definition:type; -imports silver:definition:env; - -imports silver:modification:impide:spec; - -aspect default production -top::SyntaxRoot ::= -{ - propagate fontList, classFontList; -} - -aspect production cstRoot -top::SyntaxRoot ::= parsername::String startnt::String s::Syntax customStartLayout::Maybe<[String]> terminalPrefixes::[Pair] componentGrammarMarkingTerminals::[Pair] -{ - -- 1) font information - top.fontList := s2.fontList; - top.classFontList := s2.classFontList; -} diff --git a/grammars/silver/modification/impide/cstast/LexerClassModifiers.sv b/grammars/silver/modification/impide/cstast/LexerClassModifiers.sv deleted file mode 100644 index 71f48a120..000000000 --- a/grammars/silver/modification/impide/cstast/LexerClassModifiers.sv +++ /dev/null @@ -1,42 +0,0 @@ -grammar silver:modification:impide:cstast; - ---import (see grammar-wide import in cstast.sv) - --- from TerminalModifiers -attribute fontAttr occurs on SyntaxLexerClassModifier, SyntaxLexerClassModifiers; - -aspect production consLexerClassMod -top::SyntaxLexerClassModifiers ::= h::SyntaxLexerClassModifier t::SyntaxLexerClassModifiers -{ - -- only the first non-zero font declaration is effective - top.fontAttr = - if h.fontAttr != "" - then h.fontAttr - else t.fontAttr; -} - -aspect production nilLexerClassMod -top::SyntaxLexerClassModifiers ::= -{ - top.fontAttr = ""; -} - -aspect default production -top::SyntaxLexerClassModifier ::= -{ - top.fontAttr = ""; -} - -abstract production lexerClassFont -top::SyntaxLexerClassModifier ::= fontName::String -{ - top.cstErrors := []; - - top.fontAttr = makeCopperName(fontName); -} - -aspect production lexerClassExtends -top::SyntaxLexerClassModifier ::= super::[String] -{ - top.fontAttr = dumbExtractFont(superRefs); -} \ No newline at end of file diff --git a/grammars/silver/modification/impide/cstast/Syntax.sv b/grammars/silver/modification/impide/cstast/Syntax.sv deleted file mode 100644 index f981475dd..000000000 --- a/grammars/silver/modification/impide/cstast/Syntax.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar silver:modification:impide:cstast; - ---import (see grammar-wide import in cstast.sv) - -monoid attribute fontList :: [Pair] with [], ++; -monoid attribute classFontList :: [Pair] with [], ++; - -attribute fontList, classFontList occurs on Syntax, SyntaxDcl, SyntaxRoot; -propagate fontList, classFontList on Syntax, SyntaxDcl; - -aspect production syntaxLexerClass -top::SyntaxDcl ::= n::String modifiers::SyntaxLexerClassModifiers -{ - top.classFontList <- - if modifiers.fontAttr == "" then [] - else [pair(n, modifiers.fontAttr)]; -} - -abstract production syntaxFont -top::SyntaxDcl ::= fontName::String fnt::Font -- TODO: we probably? need to factor out this data structure somehow? -{ - top.fontList <- [pair(makeCopperName(fontName), fnt)]; - - propagate cstErrors, prefixSeperator; - - top.fullName = fontName; - top.sortKey = "111"; -- Doesn't really matter, it doesn't show up in the copper XML - top.cstDcls := [pair(fontName, top)]; - top.cstNormalize := [top]; - - top.xmlCopper = ""; -} - diff --git a/grammars/silver/modification/impide/cstast/TerminalModifiers.sv b/grammars/silver/modification/impide/cstast/TerminalModifiers.sv deleted file mode 100644 index 64d7928ca..000000000 --- a/grammars/silver/modification/impide/cstast/TerminalModifiers.sv +++ /dev/null @@ -1,63 +0,0 @@ -grammar silver:modification:impide:cstast; - ---import (see grammar-wide import in cstast.sv) - -synthesized attribute fontAttr :: String; -synthesized attribute fontAttrFromClass :: String; - -attribute fontAttr occurs on SyntaxTerminalModifier, SyntaxTerminalModifiers; -attribute fontAttrFromClass occurs on SyntaxTerminalModifier, SyntaxTerminalModifiers; - -aspect production consTerminalMod -top::SyntaxTerminalModifiers ::= h::SyntaxTerminalModifier t::SyntaxTerminalModifiers -{ - -- only the first non-zero font declaration is effective - top.fontAttr = - if h.fontAttr != "" - then h.fontAttr - else t.fontAttr; - top.fontAttrFromClass = - if h.fontAttrFromClass != "" - then h.fontAttrFromClass - else t.fontAttrFromClass; -} - -aspect production nilTerminalMod -top::SyntaxTerminalModifiers ::= -{ - top.fontAttr = ""; - top.fontAttrFromClass = ""; -} - -aspect default production -top::SyntaxTerminalModifier ::= -{ - top.fontAttr = ""; - top.fontAttrFromClass = ""; -} - -aspect production termClasses -top::SyntaxTerminalModifier ::= cls::[String] -{ - top.fontAttrFromClass = dumbExtractFont(allClsRefs); - -} -function dumbExtractFont -String ::= d::[Decorated SyntaxDcl] -{ - return if null(d) then "" - else case d of - | syntaxLexerClass(_, mods)::rest -> if mods.fontAttr != "" then mods.fontAttr else dumbExtractFont(rest) - | _ -> error("We should only have lexer classes here") - end; -} - --- terminal's font in IDE -abstract production termFont -top::SyntaxTerminalModifier ::= fontName::String -{ - top.cstErrors := []; - - top.fontAttr = makeCopperName(fontName); -} - diff --git a/grammars/silver/modification/impide/spec/IdeFont.sv b/grammars/silver/modification/impide/spec/IdeFont.sv deleted file mode 100644 index 0bc3d5387..000000000 --- a/grammars/silver/modification/impide/spec/IdeFont.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar silver:modification:impide:spec; - --- TODO: er, this should probably be moved to :cstast. It's not used here! -nonterminal Font with getTextAttribute, pluginXmlSpec; - -synthesized attribute getTextAttribute :: String; -synthesized attribute pluginXmlSpec :: String; - - -abstract production font -top::Font ::= color::Color isBold::Boolean isItalic::Boolean -{ - top.getTextAttribute = - s"""TextAttributeProvider.getAttribute(display, ${toString(color.r)}, ${toString(color.g)}, ${toString(color.b)}, ${toString(isBold)}, ${toString(isItalic)})"""; - top.pluginXmlSpec = - s"""r="${toString(color.r)}" g="${toString(color.g)}" b="${toString(color.b)}" bold="${toString(isBold)}" italic="${toString(isItalic)}" """; -} - -nonterminal Color with r, g, b; - -synthesized attribute r :: Integer; -synthesized attribute g :: Integer; -synthesized attribute b :: Integer; - -abstract production makeColor -top::Color ::= r::Integer g::Integer b::Integer -{ - top.r = r; - top.g = g; - top.b = b; -} - - diff --git a/grammars/silver/modification/impide/spec/IdeFunction.sv b/grammars/silver/modification/impide/spec/IdeFunction.sv deleted file mode 100644 index 02789c7c6..000000000 --- a/grammars/silver/modification/impide/spec/IdeFunction.sv +++ /dev/null @@ -1,80 +0,0 @@ -grammar silver:modification:impide:spec; - -nonterminal IdeFunctions with package, visibleName, implang, bundle, pluginXml, pluginXmlActions, pluginXmlBuilders, markerFullName; - -synthesized attribute pluginXmlBuilders :: String; -autocopy attribute markerFullName :: String; - -abstract production consIdeFunction -top::IdeFunctions ::= h::IdeFunction t::IdeFunctions -{ - top.pluginXml = h.pluginXml ++ t.pluginXml; - top.pluginXmlActions = h.pluginXmlActions ++ t.pluginXmlActions; - top.pluginXmlBuilders = h.pluginXmlBuilders ++ t.pluginXmlBuilders; -} - -abstract production nilIdeFunction -top::IdeFunctions ::= -{ - top.pluginXml = ""; - top.pluginXmlActions = ""; - top.pluginXmlBuilders = ""; -} - -nonterminal IdeFunction with package, visibleName, implang, bundle, pluginXml, pluginXmlActions, pluginXmlBuilders, markerFullName; - -aspect default production -top::IdeFunction ::= -{ - top.pluginXml = ""; - top.pluginXmlActions = ""; - top.pluginXmlBuilders = ""; -} - -abstract production builderFunction -top::IdeFunction ::= fName::String -{ - top.pluginXmlBuilders = - s""" - """; -} - -abstract production postbuilderFunction -top::IdeFunction ::= fName::String -{ - top.pluginXmlBuilders = - s""" - """; -} - -abstract production exporterFunction -top::IdeFunction ::= fName::String -{ - top.pluginXmlActions = s""" - - - - - - - -"""; -} - -abstract production folderFunction -top::IdeFunction ::= fName::String -{ - top.pluginXml = s""" - - - - - -"""; -} - diff --git a/grammars/silver/modification/impide/spec/IdeProperty.sv b/grammars/silver/modification/impide/spec/IdeProperty.sv deleted file mode 100644 index 53036d5b3..000000000 --- a/grammars/silver/modification/impide/spec/IdeProperty.sv +++ /dev/null @@ -1,53 +0,0 @@ -grammar silver:modification:impide:spec; - -nonterminal IdeProperty with controlJavaTranslation, generatorJavaTranslation; - -synthesized attribute controlJavaTranslation :: String; -synthesized attribute generatorJavaTranslation :: String; - -abstract production makeIdeProperty -top::IdeProperty ::= propName::String propType::PropType optional::Boolean defaultVal::String displayName::String -{ - top.controlJavaTranslation = - s""" controls.add(new ${propType.propControl}(panel, "${propName}", "${displayName}", "${defaultVal}", ${if optional then "false" else "true"})); -"""; - -- TODO: honestly, we ought to just build this whole string statically, and do escaping here, too. - top.generatorJavaTranslation = - s""" sb.append("${propName}/${propType.strPropType} = ");sb.append(escape("${defaultVal}"));sb.append("\n"); -"""; -} - - -nonterminal PropType with strPropType, propControl; - -synthesized attribute strPropType :: String; -synthesized attribute propControl :: String; - -abstract production stringPropType -top::PropType ::= -{ - top.strPropType = "string"; - top.propControl = "TextPropertyControl"; -} - -abstract production pathPropType -top::PropType ::= -{ - top.strPropType = "path"; - top.propControl = "PathPropertyControl"; -} - -abstract production urlPropType -top::PropType ::= -{ - top.strPropType = "url"; - top.propControl = "URLPropertyControl"; -} - -abstract production integerPropType -top::PropType ::= -{ - top.strPropType = "integer"; - top.propControl = "IntegerPropertyControl"; -} - diff --git a/grammars/silver/modification/impide/spec/IdeSpec.sv b/grammars/silver/modification/impide/spec/IdeSpec.sv deleted file mode 100644 index d4666feaa..000000000 --- a/grammars/silver/modification/impide/spec/IdeSpec.sv +++ /dev/null @@ -1,403 +0,0 @@ -grammar silver:modification:impide:spec; - -synthesized attribute ideResources :: [Pair]; - -synthesized attribute pluginParserClass :: String; -synthesized attribute pluginGrammar :: String; -- TODO: replace with sourceGrammar? -synthesized attribute ideName :: String; -synthesized attribute ideVersion :: String; -synthesized attribute pluginFiles :: [Pair]; - --- These are for our spec's children: -synthesized attribute svIdeInterface :: String; -synthesized attribute pluginXml :: String; -synthesized attribute pluginXmlActions :: String; -synthesized attribute pluginXmlWizards :: String; - -autocopy attribute bundle :: String; -autocopy attribute implang :: String; -autocopy attribute visibleName :: String; -autocopy attribute package :: String; -autocopy attribute pluginPkgPath :: String; - -{-- - - Eclipse extensions have unique IDs. - - When these IDs are supplied to an tag, they are - - automatically prefixed with the bundle name. - - e.g. "SILVER_IDE.builder" for - - - - BUT, when the IDs are given to sub-tags underneath , you must - - evidently give the fully qualified name right off the bat. *sigh* eclipse. - -} -global extid_nature :: String = "nature"; -global extid_builder :: String = "builder"; -global extid_problem :: String = "builder.problem"; -global extid_perspective :: String = "perspective"; - -global extid_projectmenu :: String = "actions.projectmenu"; -global extid_action_nature :: String = "actions.nature"; -global extid_action_export :: String = "actions.export"; - -global extid_wizard_category :: String = "wizards.category"; -global extid_wizard_newproject :: String = "wizards.newproject"; -global extid_wizard_newfile :: String = "wizards.newfile"; - -global extid_properties :: String = "properties"; - -nonterminal IdeSpec with compiledGrammars, pluginParserClass, pluginGrammar, ideName, ideVersion, pluginFiles, ideResources; - -abstract production ideSpec -top::IdeSpec ::= - grammarName::String ideName::String ideVersion::String - ext::String ideFuncDcls::[IdeFunction] idePropDcls::[IdeProperty] wizards::[IdeWizardDcl] - pspec::ParserSpec ideResources::[Pair] -{ - top.ideName = ideName; - top.ideVersion = ideVersion; - top.pluginGrammar = grammarName; - top.pluginParserClass = makeParserName(pspec.fullName); - top.ideResources = ideResources; - - -- NOTE: currently, implang = ideName, but we may want to change this - -- always use implang as the imp registry language name, and ideName as the user-visible. - local implang :: String = ideName; - local package :: String = makeName(grammarName); - local bundle :: String = package; -- same for now - - local pluginPkgPath :: String = s"src/${grammarToPath(grammarName)}"; - - local ast :: SyntaxRoot = pspec.cstAst; - - local funcs :: IdeFunctions = foldr(consIdeFunction, nilIdeFunction(), ideFuncDcls); - funcs.bundle = bundle; - funcs.implang = implang; - funcs.visibleName = ideName; - funcs.package = package; - funcs.markerFullName = s"${bundle}.${extid_problem}"; - - local wizs :: IdeWizards = foldr(consIdeWizard, nilIdeWizard(), wizards); - wizs.bundle = bundle; - wizs.implang = implang; - wizs.visibleName = ideName; - wizs.package = package; - wizs.pluginPkgPath = pluginPkgPath; - - local tabs::[String] = - if null(idePropDcls) then [] else ["edu.umn.cs.melt.ide.eclipse.property.TabCommons"]; - - local sourceGrammarName :: String = makeName(pspec.sourceGrammar); - - top.pluginFiles = - [pair(s"${pluginPkgPath}SVIdeInterface.java", s""" -package ${package}; - -import java.io.IOException; -import java.io.Reader; -import java.util.Iterator; - -import common.ConsCell; -import common.Node; -import common.StringCatter; -import core.NIOVal; -import core.Pioval; - -import org.eclipse.jface.text.IRegion; -import org.eclipse.core.resources.IProject; - -import edu.umn.cs.melt.ide.eclipse.property.IPropertyPageTab; -import edu.umn.cs.melt.ide.silver.property.ui.IPropertyControlsProvider; -import edu.umn.cs.melt.ide.impl.SVDefault; -import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; -import edu.umn.cs.melt.ide.imp.services.IdeParseResult; - -public class SVIdeInterface extends SVDefault { - - public SVIdeInterface() {} -@Override - public String name() { return "${implang}"; } - @Override - public String pluginId() { return "${bundle}"; } - @Override - public String markerErrorName() { return "${bundle}.${extid_problem}"; } - @Override - public String getNatureId() { return "${bundle}.${extid_nature}"; } - @Override - public String fileExtension() { return "${ext}"; } - @Override - public IPropertyControlsProvider getProjectProperties() { - return new ${package}.eclipse.property.PropertyControlsProvider(); - } - @Override - public String getInitialProjectProperties() { - return ${package}.eclipse.wizard.newproject.PropertyGenerator.getAll(); - } - @Override - public IPropertyPageTab[] getPropertyTabs() { - return new IPropertyPageTab[] { - ${implode(", ", map(newTabClass, tabs))} - }; - } - - -${wizs.svIdeInterface} -} -"""), - pair("plugin.xml", s""" - - - - - - - - - - - - - - - - - - - ${funcs.pluginXmlBuilders} - - - - - - - - - - - - - - - - - - - - - - - - - - ${implode("\n ", map(getFontPluginXmlSpec, ast.fontList) ++ map(getClassPluginXmlSpec, ast.classFontList))} - - - - - - - - - - - - -${funcs.pluginXmlActions} - - - - - - - - - - -${wizs.pluginXmlWizards} - - - - - - - - - - - - - - - - - -${funcs.pluginXml} - - -"""), --- TODO: we could probably do away with the following Plugin generation by using --- context.getBundle().getHeaders().get("Silver-Eclipse-Grammar") --- and then using that to call Init and new SVIdeInterface. --- (Plus adding that line to the MANIFEST.MF, with the appropriate value...) - pair(s"${pluginPkgPath}Plugin.java", - s""" -package ${package}; - -import org.osgi.framework.BundleContext; -import org.osgi.framework.BundleActivator; - -import edu.umn.cs.melt.ide.impl.SVRegistry; - -public class Plugin implements BundleActivator { - - public void start(BundleContext context) throws Exception { - - Init.initAllStatics(); - Init.init(); - Init.postInit(); - - SVRegistry.register(new SVIdeInterface()); - } - - @Override - public void stop(BundleContext context) throws Exception { - } -} -"""), - pair(s"${pluginPkgPath}eclipse/property/PropertyControlsProvider.java", - getPropertyProvider(package, idePropDcls, "property")), - pair(s"${pluginPkgPath}eclipse/wizard/newproject/PropertyGenerator.java", - getPropertyGenerator(package, idePropDcls, "newproject")) - ] ++ - wizs.pluginFiles; -} - -function newTabClass -String ::= tab::String -{ - return "new " ++ tab ++ "()"; -} - - -function getPropertyProvider -String ::= pkgName::String propDcls :: [IdeProperty] pkgPart::String -{ - return s""" -package ${pkgName}.eclipse.${pkgPart}; - -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.swt.widgets.Composite; - -import edu.umn.cs.melt.ide.silver.property.ui.*; - -public class PropertyControlsProvider implements IPropertyControlsProvider { - - private List controls; - - @Override - public List getPropertyControls(Composite panel) { - if(controls == null) { - controls = new ArrayList(); - -${sflatMap((.controlJavaTranslation), propDcls)} - } - - return controls; - } - - @Override - public boolean validateAll() { - boolean valid = true; - - if(controls != null) { - for(PropertyControl control : controls) { - if(!control.validate()) { - valid = false; - } - } - } - - return valid; - } -} -"""; -- TODO: for validation, we may someday want to expose a silver function where we can write how to validate a property -} - -function getPropertyGenerator -String ::= pkgName::String propDcls::[IdeProperty] pkgFinalPart::String -{ - local pkgPart :: String = if pkgFinalPart == "" then "" else "." ++ pkgFinalPart; - - return s""" -package ${pkgName}.eclipse.wizard${pkgPart}; - -import java.util.ArrayList; -import java.util.List; - -public class PropertyGenerator { - - private static String properties = null; - - public static String getAll() { - if(properties==null) { - StringBuilder sb = new StringBuilder(); - -${sflatMap((.generatorJavaTranslation), propDcls)} - - properties = sb.toString(); - } - - return properties; - } - - - private static String escape(String str) { - char[] orig = str.toCharArray(); - List list = new ArrayList(); - for(char c : orig) { - if(c == '=' || c == '#' || c == '\\' || c == ':') { - list.add('\\'); - } - list.add(c); - } - - //Convert to a char array - char[] mod = new char[list.size()]; - for(int i = 0; i < mod.length; i++) { - mod[i] = list.get(i); - } - - return new String(mod); - } -} -"""; -} - -function getFontPluginXmlSpec -String ::= f::Pair -{ - return s""""""; -} -function getClassPluginXmlSpec -String ::= f::Pair -{ - return s""""""; -} diff --git a/grammars/silver/modification/impide/spec/IdeWizard.sv b/grammars/silver/modification/impide/spec/IdeWizard.sv deleted file mode 100644 index 060fbdb23..000000000 --- a/grammars/silver/modification/impide/spec/IdeWizard.sv +++ /dev/null @@ -1,55 +0,0 @@ -grammar silver:modification:impide:spec; - -nonterminal IdeWizards with pluginPkgPath, pluginFiles, package, visibleName, implang, bundle, svIdeInterface, pluginXmlWizards; - -abstract production consIdeWizard -top::IdeWizards ::= h::IdeWizardDcl t::IdeWizards -{ - top.svIdeInterface = h.svIdeInterface ++ t.svIdeInterface; - top.pluginXmlWizards = h.pluginXmlWizards ++ t.pluginXmlWizards; - top.pluginFiles = h.pluginFiles ++ t.pluginFiles; -} - -abstract production nilIdeWizard -top::IdeWizards ::= -{ - top.svIdeInterface = ""; - top.pluginXmlWizards = ""; - top.pluginFiles = []; -} - --- An IdeWizardDcl includes all the necessary information for generating a Wizard in IDE. - -nonterminal IdeWizardDcl with pluginPkgPath, pluginFiles, package, visibleName, implang, bundle, svIdeInterface, pluginXmlWizards; - -{-- - func: the full name of stub generator, having signature String ::= [IdeProperty] - props: a list of properties which the stub generator can access to ---} -abstract production newfileWizard -top::IdeWizardDcl ::= func::String props::[IdeProperty] -{ - top.svIdeInterface = s""" - @Override - public IPropertyControlsProvider getNewFileProperties() { - return new ${top.package}.eclipse.wizard.newfile.PropertyControlsProvider(); - } - @Override - public StringCatter fileStub(ConsCell properties) { - return (StringCatter)${makeClassName(func)}.invoke(properties); - } -"""; - - top.pluginXmlWizards = s""" - - -"""; - top.pluginFiles = [ - pair(s"${top.pluginPkgPath}eclipse/wizard/newfile/PropertyControlsProvider.java", - getPropertyProvider(top.package, props, "wizard.newfile"))]; -} - diff --git a/grammars/silver/modification/impide/spec/Project.sv b/grammars/silver/modification/impide/spec/Project.sv deleted file mode 100644 index 715422b35..000000000 --- a/grammars/silver/modification/impide/spec/Project.sv +++ /dev/null @@ -1,19 +0,0 @@ -grammar silver:modification:impide:spec; - ---imports silver:definition:core; -imports silver:definition:env; - -imports silver:definition:concrete_syntax; ---imports silver:modification:copper; - ---imports silver:definition:concrete_syntax:ast; ---imports silver:modification:impide:cstast; -imports silver:definition:concrete_syntax:ast; -imports silver:modification:impide:cstast; - ---imports silver:modification:impide only getPropertyGenerator, getPropertyProvider; - -imports silver:translation:java:core only makeClassName, makeParserName, makeName; - -imports silver:driver:util only grammarToPath; - diff --git a/grammars/silver/modification/lambda_fn/DclInfo.sv b/grammars/silver/modification/lambda_fn/DclInfo.sv deleted file mode 100644 index d7741fc5f..000000000 --- a/grammars/silver/modification/lambda_fn/DclInfo.sv +++ /dev/null @@ -1,22 +0,0 @@ -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - -abstract production lambdaParamDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = lambdaParamReference(_, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- should be impossible (never in scope at production level?) - top.defLHSDispatcher = errorDefLHS(_, location=_); -- ditto -} - -function lambdaParamDef -Def ::= sg::String sl::Location fn::String ty::Type -{ - return valueDef(defaultEnvItem(lambdaParamDcl(sg,sl,fn,ty))); -} - diff --git a/grammars/silver/modification/lambda_fn/DocConfig.sv b/grammars/silver/modification/lambda_fn/DocConfig.sv deleted file mode 100644 index 25b68b25c..000000000 --- a/grammars/silver/modification/lambda_fn/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:lambda_fn; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Lambda Functions\nmenu_title: Lambda Functions\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/lambda_fn/Lambda.sv b/grammars/silver/modification/lambda_fn/Lambda.sv deleted file mode 100644 index 66c027d22..000000000 --- a/grammars/silver/modification/lambda_fn/Lambda.sv +++ /dev/null @@ -1,66 +0,0 @@ -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - ---- Concrete Syntax for lambdas --------------------------------------------------------------------------------- - -terminal Lambda_kwd '\' lexer classes {KEYWORD,RESERVED}; -terminal Arrow_t '->' precedence = 0, lexer classes {SPECOP}; - --- Using ProductionRHS here, it is basicly just a list of names with type expressions --- It is also used for the parameter definitions in functions, so using it here for consistancy -concrete production lambda_c -top::Expr ::= '\' params::ProductionRHS '->' e::Expr -{ - top.unparse = "\\ " ++ params.unparse ++ " -> " ++ e.unparse; - - forwards to lambdap(params, e, location=top.location); -} - --- TODO: New type variables are not currently allowed on the params. This isn't a big deal for --- writing code since lambdas aren't generalized anyway, but it would be nice to have for generated --- code involving lambdas. This isn't implemented at the moment due to a mess right now with how --- this works for ProductionDcl --- Also issues here with lexically scoped type variables -abstract production lambdap -top::Expr ::= params::ProductionRHS e::Expr -{ - top.unparse = "\\ " ++ params.unparse ++ " -> " ++ e.unparse; - - propagate errors; - - top.typerep = functionType(e.typerep, map((.typerep), params.inputElements), []); - - e.downSubst = top.downSubst; - top.upSubst = e.upSubst; - - propagate flowDeps, flowDefs; - - e.env = newScopeEnv(params.lambdaDefs, top.env); -} - -monoid attribute lambdaDefs::[Def] with [], ++; -attribute lambdaDefs occurs on ProductionRHS, ProductionRHSElem; - -propagate lambdaDefs on ProductionRHS; - -aspect production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - production fName :: String = toString(genInt()) ++ ":" ++ id.name; --- production transName :: String = "lambda_param" ++ id.name ++ toString(genInt()); - top.lambdaDefs := [lambdaParamDef(top.grammarName, t.location, fName, t.typerep)]; -} - -abstract production lambdaParamReference -top::Expr ::= q::Decorated QName -{ - top.unparse = q.unparse; - propagate errors; - - top.typerep = q.lookupValue.typerep; - - top.upSubst = top.downSubst; - - -- TODO? - propagate flowDeps, flowDefs; -} diff --git a/grammars/silver/modification/lambda_fn/Project.sv b/grammars/silver/modification/lambda_fn/Project.sv deleted file mode 100644 index 57358624d..000000000 --- a/grammars/silver/modification/lambda_fn/Project.sv +++ /dev/null @@ -1,11 +0,0 @@ -grammar silver:modification:lambda_fn; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; ---imports silver:analysis:typechecking:core; - -exports silver:modification:lambda_fn:java with silver:translation:java:core; -exports silver:modification:lambda_fn:java with silver:translation:java:type; - diff --git a/grammars/silver/modification/lambda_fn/java/Lambda.sv b/grammars/silver/modification/lambda_fn/java/Lambda.sv deleted file mode 100644 index 11b79733e..000000000 --- a/grammars/silver/modification/lambda_fn/java/Lambda.sv +++ /dev/null @@ -1,83 +0,0 @@ -grammar silver:modification:lambda_fn:java; - -import silver:modification:lambda_fn; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:definition:type:syntax; - -import silver:translation:java:core; -import silver:translation:java:type; - -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - -aspect production lambdap -top::Expr ::= params::ProductionRHS e::Expr -{ - local finTy :: Type = finalType(top); - - -- If the type somehow contains a skolem (e.g. through scoped type variables), then there isn't - -- any better we can do with the result type, so leave it unfreshened. - top.translation = -s"""(new common.NodeFactory<${finTy.outputType.transType}>() { - @Override - public final ${finTy.outputType.transType} invoke(final Object[] args, final Object[] namedArgs) { -${params.lambdaTranslation} - return ${e.translation}; - } - - @Override - public final common.FunctionTypeRep getType() { -${makeTyVarDecls(5, finTy.freeVariables)} - return ${finTy.transTypeRep}; - } - - @Override - public final String toString() { - return "lambda at ${top.grammarName}:${top.location.unparse}"; - } - })"""; - top.lazyTranslation = top.translation; - - params.accessIndex = 0; -} - -synthesized attribute lambdaTranslation::String occurs on ProductionRHS, ProductionRHSElem; -inherited attribute accessIndex::Integer occurs on ProductionRHS, ProductionRHSElem; - -aspect production productionRHSCons -top::ProductionRHS ::= h::ProductionRHSElem t::ProductionRHS -{ - top.lambdaTranslation = h.lambdaTranslation ++ t.lambdaTranslation; - t.accessIndex = top.accessIndex + 1; - h.accessIndex = top.accessIndex; -} -aspect production productionRHSNil -top::ProductionRHS ::= -{ - top.lambdaTranslation = ""; -} - -aspect production productionRHSElem -top::ProductionRHSElem ::= id::Name '::' t::TypeExpr -{ - -- Args are unpacked as objects, they can either be an actual value or a Thunk. - -- We don't know which staticly, so they are just stored as Objects until use. - -- They are then demanded and converted to the correct type where they are needed. - top.lambdaTranslation = s"\t\t\t\t\tfinal Object ${makeLambdaParamValueName(fName)} = args[${toString(top.accessIndex)}];\n"; -} - -function makeLambdaParamValueName -String ::= s::String -{ - return "lambdaParam_" ++ makeIdName(s); -} - -aspect production lambdaParamReference -top::Expr ::= q::Decorated QName -{ - top.translation = s"((${top.typerep.transType})common.Util.demand(${makeLambdaParamValueName(q.lookupValue.fullName)}))"; - top.lazyTranslation = top.translation; -} - diff --git a/grammars/silver/modification/let_fix/DclInfo.sv b/grammars/silver/modification/let_fix/DclInfo.sv deleted file mode 100644 index dae5bac5d..000000000 --- a/grammars/silver/modification/let_fix/DclInfo.sv +++ /dev/null @@ -1,24 +0,0 @@ -grammar silver:modification:let_fix; - -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - -abstract production lexicalLocalDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type fi::ExprVertexInfo fd::[FlowVertex] -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - - top.refDispatcher = lexicalLocalReference(_, fi, fd, location=_); - top.defDispatcher = errorValueDef(_, _, location=_); -- should be impossible (never in scope at production level?) - top.defLHSDispatcher = errorDefLHS(_, location=_); -- ditto -} - -function lexicalLocalDef -Def ::= sg::String sl::Location fn::String ty::Type fi::ExprVertexInfo fd::[FlowVertex] -{ - return valueDef(defaultEnvItem(lexicalLocalDcl(sg,sl,fn,ty,fi,fd))); -} - diff --git a/grammars/silver/modification/let_fix/DocConfig.sv b/grammars/silver/modification/let_fix/DocConfig.sv deleted file mode 100644 index cb1aa491a..000000000 --- a/grammars/silver/modification/let_fix/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:let_fix; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Let Fix\nmenu_title: Let Fix\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/let_fix/Let.sv b/grammars/silver/modification/let_fix/Let.sv deleted file mode 100644 index 03bee5aa8..000000000 --- a/grammars/silver/modification/let_fix/Let.sv +++ /dev/null @@ -1,145 +0,0 @@ -grammar silver:modification:let_fix; - -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - ---- Concrete Syntax for lets --------------------------------------------------------------------------------- - -terminal Let_kwd 'let' lexer classes {KEYWORD,RESERVED}; -terminal In_kwd 'in' lexer classes {KEYWORD,RESERVED}; -terminal End_kwd 'end' lexer classes {KEYWORD,RESERVED}; - -concrete production letp_c -top::Expr ::= 'let' la::LetAssigns 'in' e::Expr 'end' -{ - top.unparse = "let " ++ la.unparse ++ " in " ++ e.unparse ++ " end"; - - forwards to letp(la.letAssignExprs, e, location=top.location); -} - -nonterminal LetAssigns with unparse, location, letAssignExprs; - -synthesized attribute letAssignExprs :: AssignExpr; - -concrete production assignsListCons -top::LetAssigns ::= ae::AssignExpr ',' list::LetAssigns -{ - top.unparse = ae.unparse ++ ", " ++ list.unparse; - top.letAssignExprs = appendAssignExpr(ae, list.letAssignExprs, location=top.location); -} -concrete production assignListSingle -top::LetAssigns ::= ae::AssignExpr -{ - top.unparse = ae.unparse; - top.letAssignExprs = ae; -} - --------------------------------------------------------------------------------- ---- Abstract Syntax for lets --------------------------------------------------------------------------------- - -abstract production letp -top::Expr ::= la::AssignExpr e::Expr -{ - top.unparse = "let " ++ la.unparse ++ " in " ++ e.unparse ++ " end"; - - propagate errors; - - top.typerep = e.typerep; - - la.downSubst = top.downSubst; - e.downSubst = la.upSubst; - top.upSubst = e.upSubst; - - -- Semantics for the moment is these are not mutually recursive, - -- so la does NOT get new environment, only e. Thus, la.defs can depend on downSubst... - e.env = newScopeEnv(la.defs, top.env); -} - -nonterminal AssignExpr with location, config, grammarName, env, compiledGrammars, - unparse, defs, errors, upSubst, - downSubst, finalSubst, frame; - -propagate errors, defs on AssignExpr; - -abstract production appendAssignExpr -top::AssignExpr ::= a1::AssignExpr a2::AssignExpr -{ - top.unparse = a1.unparse ++ ", " ++ a2.unparse; - - a1.downSubst = top.downSubst; - a2.downSubst = a1.upSubst; - top.upSubst = a2.upSubst; -} - --- TODO: Well, okay, so this isn't really abstract syntax... -concrete production assignExpr -top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr -{ - top.unparse = id.unparse ++ " :: " ++ t.unparse ++ " = " ++ e.unparse; - - -- Right now some things (pattern matching) abuse us by giving type variables - -- for `t`. So we want to do a little inference before we stuff this into - -- our DclInfo in `defs` because we expect variables in the env to have - -- explicit types. We can't use `finalSubst` here because that requires - -- having completed type inference which requires `defs` which we're defining. - local semiTy :: Type = performSubstitution(t.typerep, top.upSubst); - production fName :: String = toString(genInt()) ++ ":" ++ id.name; - - -- Using finalTy here, so our defs requires we have downSubst... - -- references to this def want to know if its decorated, to enable the - -- auto-undecorate feature, so that's why we bother substituting. - -- (er, except that we're starting with t, which is a Type... must be because we fake these - -- in e.g. the pattern matching code, so type variables might appear there?) - top.defs <- [lexicalLocalDef(top.grammarName, id.location, fName, semiTy, e.flowVertexInfo, e.flowDeps)]; - - -- TODO: At present, this isn't working properly, because the local scope is - -- whatever scope encloses the real local scope... hrmm! - top.errors <- - if length(getValueDclInScope(id.name, top.env)) > 1 - then [err(id.location, "Value '" ++ id.name ++ "' is already bound.")] - else []; - - e.downSubst = top.downSubst; - errCheck1.downSubst = e.upSubst; - top.upSubst = errCheck1.upSubst; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - - errCheck1 = check(e.typerep, t.typerep); - top.errors <- - if errCheck1.typeerror - then [err(id.location, "Value " ++ id.name ++ " declared with type " ++ errCheck1.rightpp ++ " but the expression being assigned to it has type " ++ errCheck1.leftpp)] - else []; -} - -abstract production lexicalLocalReference -top::Expr ::= q::Decorated QName fi::ExprVertexInfo fd::[FlowVertex] -{ - top.unparse = q.unparse; - top.errors := []; - - -- We're adding the "unusual" behavior that types like "Decorated Foo" in LETs - -- will auto-undecorate if you want a Foo. - - -- (The usual behavior is a declared Foo, but value is Decorated Foo, can - -- be used either way.) - - -- A note about possible unexpected behavior here: if q.lookupValue.typerep - -- is itself a ntOrDecType, which is only possible if for generated 'let' - -- expressions that use a type variable as their type, then this ntOrDecType - -- we're generating here means we're NOT propagating the information about the - -- "actual usage" backwards to expression. - -- i.e. "let x :: a = someLocal in wantsUndecorated(x) end" - -- will mean "let x = decorated version of someLocal in wantsUndecorated(x.undecorate())" - -- and not "let x = undecorated someLocal in wantsUndecorated(x)" - - top.typerep = - -- isDecorated should return true if it's a ntOrDecType. - if q.lookupValue.typerep.isDecorated - then ntOrDecType(q.lookupValue.typerep.decoratedType, freshType()) - else q.lookupValue.typerep; - - top.upSubst = top.downSubst; -} - diff --git a/grammars/silver/modification/let_fix/Project.sv b/grammars/silver/modification/let_fix/Project.sv deleted file mode 100644 index 7769e102e..000000000 --- a/grammars/silver/modification/let_fix/Project.sv +++ /dev/null @@ -1,11 +0,0 @@ -grammar silver:modification:let_fix; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; -imports silver:definition:type:syntax; ---imports silver:analysis:typechecking:core; - -exports silver:modification:let_fix:java with silver:translation:java:core; -exports silver:modification:let_fix:java with silver:translation:java:type; - diff --git a/grammars/silver/modification/let_fix/java/Let.sv b/grammars/silver/modification/let_fix/java/Let.sv deleted file mode 100644 index 4ecb6c97a..000000000 --- a/grammars/silver/modification/let_fix/java/Let.sv +++ /dev/null @@ -1,87 +0,0 @@ -grammar silver:modification:let_fix:java; - -import silver:modification:let_fix; - -import silver:definition:core; -import silver:definition:env; -import silver:definition:type; -import silver:definition:type:syntax; - -import silver:translation:java:core; -import silver:translation:java:type; - -import silver:definition:flow:ast only ExprVertexInfo, FlowVertex; - -aspect production letp -top::Expr ::= la::AssignExpr e::Expr -{ - local finTy :: Type = finalType(top); - - -- We need to create these nested locals, so we have no choice but to create a thunk object so we can declare these things. - local closureExpr :: String = - s"new common.Thunk<${finTy.transType}>(new common.Thunk.Evaluable() { public final ${finTy.transType} eval() { ${la.let_translation} return ${e.translation}; } })"; - --TODO: java lambdas are bugged - --s"new common.Thunk<${finTy.transType}>(() -> { ${la.let_translation} return ${e.translation};\n})"; - - top.translation = s"${closureExpr}.eval()"; - - top.lazyTranslation = - if top.frame.lazyApplication - then closureExpr - else top.translation; -} - -synthesized attribute let_translation :: String occurs on AssignExpr; - -function makeLocalValueName -String ::= s::String -{ - return "__SV_LOCAL_" ++ makeIdName(s); -} - -aspect production appendAssignExpr -top::AssignExpr ::= a1::AssignExpr a2::AssignExpr -{ - top.let_translation = a1.let_translation ++ a2.let_translation; -} - -aspect production assignExpr -top::AssignExpr ::= id::Name '::' t::TypeExpr '=' e::Expr -{ - -- We must use `finalSubst` in translation. - -- "let abuse" means type variables can appear in `t`, and if we don't - -- use `finalSubst` we get confusion with `ntOrDecType` not knowing - -- it's being used undecorated later. - local finalTy :: Type = performSubstitution(t.typerep, top.finalSubst); - top.let_translation = makeSpecialLocalBinding(fName, e.translation, finalTy.transType); -} - -function makeSpecialLocalBinding -String ::= fn::String et::String ty::String -{ - return s"final common.Thunk<${ty}> ${makeLocalValueName(fn)} = ${wrapThunkText(et, ty)};\n"; -} - -aspect production lexicalLocalReference -top::Expr ::= q::Decorated QName fi::ExprVertexInfo fd::[FlowVertex] -{ - -- To account for a magic case where we generate a let expression with a type - -- that is, for example, a ntOrDecType or something, - -- we do final subst on q.lookupValue ALSO here... - -- it could be isDecorated (ntOrDecType) that later gets specialized to undecorated - -- and therefore we must be careful not to try to undecorate it again! - local needsUndecorating :: Boolean = - performSubstitution(q.lookupValue.typerep, top.finalSubst).isDecorated && !finalType(top).isDecorated; - - top.translation = - if needsUndecorating - then "((" ++ finalType(top).transType ++ ")((common.DecoratedNode)" ++ makeLocalValueName(q.lookupValue.fullName) ++ ".eval()).undecorate())" - else "((" ++ finalType(top).transType ++ ")(" ++ makeLocalValueName(q.lookupValue.fullName) ++ ".eval()))"; - - top.lazyTranslation = - if !top.frame.lazyApplication then top.translation - else if needsUndecorating - then "common.Thunk.transformUndecorate(" ++ makeLocalValueName(q.lookupValue.fullName) ++ ")" - else makeLocalValueName(q.lookupValue.fullName); -} - diff --git a/grammars/silver/modification/primitivepattern/DocConfig.sv b/grammars/silver/modification/primitivepattern/DocConfig.sv deleted file mode 100644 index c3e828ca8..000000000 --- a/grammars/silver/modification/primitivepattern/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:primitivepattern; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Primitive Pattern\nmenu_title: Primitive Pattern\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/primitivepattern/PrimitiveMatch.sv b/grammars/silver/modification/primitivepattern/PrimitiveMatch.sv deleted file mode 100644 index eac176451..000000000 --- a/grammars/silver/modification/primitivepattern/PrimitiveMatch.sv +++ /dev/null @@ -1,454 +0,0 @@ -grammar silver:modification:primitivepattern; - -imports silver:definition:core; -imports silver:definition:env; -imports silver:definition:type; - -import silver:definition:type:syntax only typerepType, TypeExpr; -import silver:extension:patternmatching only Arrow_kwd, Vbar_kwd, ensureDecoratedExpr; -- TODO remove - -import silver:translation:java:core; -import silver:translation:java:type; - --- Actually only used for lists, in this file... TODO -import silver:modification:let_fix only makeSpecialLocalBinding, lexicalLocalDef, End_kwd; -import silver:definition:flow:ast only noVertex; - -import silver:extension:list; -- Oh no, this is a hack! TODO - -terminal Match_kwd 'match' lexer classes {KEYWORD,RESERVED}; -- temporary!!! - -nonterminal PrimPatterns with - config, grammarName, env, compiledGrammars, frame, - location, unparse, errors, - downSubst, upSubst, finalSubst, - scrutineeType, returnType, translation; -nonterminal PrimPattern with - config, grammarName, env, compiledGrammars, frame, - location, unparse, errors, - downSubst, upSubst, finalSubst, - scrutineeType, returnType, translation; - -autocopy attribute scrutineeType :: Type; -autocopy attribute returnType :: Type; - -propagate errors on PrimPatterns, PrimPattern; - -concrete production matchPrimitiveConcrete -top::Expr ::= 'match' e::Expr 'return' t::TypeExpr 'with' pr::PrimPatterns 'else' '->' f::Expr 'end' -{ - top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; - - forwards to matchPrimitive(e, t, pr, f, location=top.location); -} -abstract production matchPrimitive -top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr -{ - top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; - - e.downSubst = top.downSubst; - forward.downSubst = e.upSubst; - - -- ensureDecoratedExpr is currently wrapping 'e' in 'exprRef' which suppresses errors - -- TODO: the use of 'exprRef' should be reviewed, given that this error slipped through... - top.errors := e.errors ++ forward.errors; - - forwards to matchPrimitiveReal(ensureDecoratedExpr(e), t, pr, f, location=top.location); -} -{-- - - @param e The value to match against (should be DECORATED if it's nonterminal type at all) - - @param t The RETURN TYPE, explicitly. - - @param pr The cases of this match expression - - @param f The failure expression. (if the patterns don't match, evaluate to this.) - -} -abstract production matchPrimitiveReal -top::Expr ::= e::Expr t::TypeExpr pr::PrimPatterns f::Expr -{ - top.unparse = "match " ++ e.unparse ++ " return " ++ t.unparse ++ " with " ++ pr.unparse ++ " else -> " ++ f.unparse ++ "end"; - - propagate errors; - top.typerep = t.typerep; - - {-- - - Invariant: if we were given an undecorated expression, it should have been - - decorated by matchPrimitive before we got here, so we should either - - have a decorated expr, or some other type. - -} - local attribute scrutineeType :: Type; - scrutineeType = performSubstitution(e.typerep, e.upSubst); - - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - errCheck2 = check(f.typerep, t.typerep); - top.errors <- - if errCheck2.typeerror - then [err(top.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - -- ordinary threading: e, pr, f, errCheck2 - e.downSubst = top.downSubst; - pr.downSubst = e.upSubst; - f.downSubst = pr.upSubst; - errCheck2.downSubst = f.upSubst; - top.upSubst = errCheck2.upSubst; - - pr.scrutineeType = scrutineeType; - pr.returnType = t.typerep; - - local resultTransType :: String = performSubstitution(t.typerep, top.finalSubst).transType; - -- It is necessary to subst on scrutineeType here for the horrible reason that the type we're matching on - -- may not be determined until we get to the constructor list. e.g. 'case error("lol") of pair(x,_) -> x end' - -- which is legal, but if we don't do this will result in java translation errors (as the scrutinee will be - -- type 'a' which is Object, which doesn't have .childAsIs for 'x'.) - local scrutineeFinalType :: Type = performSubstitution(scrutineeType, top.finalSubst); - local scrutineeTransType :: String = scrutineeFinalType.transType; - - top.translation = - "new common.PatternLazy<" ++ scrutineeTransType ++ ", " ++ resultTransType ++ ">() { " ++ - "public final " ++ resultTransType ++ " eval(final common.DecoratedNode context, " ++ scrutineeTransType ++ " scrutineeIter) {" ++ - (if scrutineeFinalType.isDecorated - then - "while(true) {" ++ - "final " ++ scrutineeTransType ++ " scrutinee = scrutineeIter; " ++ -- our Lazy needs a final variable - "final common.Node scrutineeNode = scrutinee.undecorate(); " ++ - pr.translation ++ - "if(!scrutineeIter.undecorate().hasForward()) break;" ++ - "scrutineeIter = scrutineeIter.forward();" ++ - "}" - else - "final " ++ scrutineeTransType ++ " scrutinee = scrutineeIter; " ++ -- ditto - pr.translation) ++ - "return " ++ f.translation ++ ";" ++ - "}}.eval(context, (" ++ scrutineeTransType ++")" ++ e.translation ++ ")"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); - -- TODO there seems to be an opportunity here to avoid an anon class somehow... -} - -concrete production onePattern -top::PrimPatterns ::= p::PrimPattern -{ - top.unparse = p.unparse; - - top.translation = p.translation; - - p.downSubst = top.downSubst; - top.upSubst = p.upSubst; -} -concrete production consPattern -top::PrimPatterns ::= p::PrimPattern '|' ps::PrimPatterns -{ - top.unparse = p.unparse ++ " | " ++ ps.unparse; - - top.translation = p.translation ++ "\nelse " ++ ps.translation; - - p.downSubst = top.downSubst; - ps.downSubst = p.upSubst; - top.upSubst = ps.upSubst; -} - --- TODO: Long term, I'd like to switch to having a PrimRule and rename PrimPatterns PrimRules. --- However, we cannot do this yet, because the GADT case does CRAZY things with typing. --- (Ideally, we'd be able to do those crazy things with constraints added to the --- context there, instead...) - -concrete production prodPattern -top::PrimPattern ::= qn::QName '(' ns::VarBinders ')' '->' e::Expr -{ - top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; - - local isGadt :: Boolean = - case qn.lookupValue.typerep.outputType of - -- If the lookup is successful, and it's a production type, and it - -- constructs a nonterminal that either: - -- 1. has a non-type-variable parameter (e.g. Expr) - -- 2. has fewer free variables than parameters (e.g. Eq) - -- THEN it's a gadt. - | nonterminalType(_, tvs) -> !isOnlyTyVars(tvs) || length(tvs) != length(setUnionTyVarsAll(map((.freeVariables), tvs))) - | _ -> false - end; - - -- The reason we do it this way is because the threading of type information - -- around is very different, and I don't want to confuse myself while I'm writing - -- the code. After it works, perhaps these can be merged into one non-forwarding - -- production, once the code is understood fully. - forwards to if isGadt - then prodPatternGadt(qn, ns, e, location=top.location) - else prodPatternNormal(qn, ns, e, location=top.location); -} -abstract production prodPatternNormal -top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr -{ - top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; - - local chk :: [Message] = - if null(qn.lookupValue.dcls) || ns.varBinderCount == length(prod_type.inputTypes) then [] - else [err(qn.location, qn.name ++ " has " ++ toString(length(prod_type.inputTypes)) ++ " parameters but " ++ toString(ns.varBinderCount) ++ " patterns were provided")]; - - top.errors <- qn.lookupValue.errors; - - -- Turns the existential variables existential - local prod_type :: Type = - skolemizeProductionType(qn.lookupValue.typerep); - -- Note that we're going to check prod_type against top.scrutineeType shortly. - -- This is where the type variables become unified. - - ns.bindingTypes = prod_type.inputTypes; - ns.bindingIndex = 0; - ns.bindingNames = if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.inputNames; - ns.matchingAgainst = if null(qn.lookupValue.dcls) then nothing() else just(qn.lookupValue.dcl); - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(decoratedType(prod_type.outputType), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, qn.name ++ " has type " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - -- Thread NORMALLY! YAY! - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - e.env = newScopeEnv(ns.defs, top.env); - - top.translation = "if(scrutineeNode instanceof " ++ makeClassName(qn.lookupValue.fullName) ++ - ") { " ++ ns.translation ++ " return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ e.translation ++ "; }"; -} - -abstract production prodPatternGadt -top::PrimPattern ::= qn::Decorated QName ns::VarBinders e::Expr -{ - top.unparse = qn.unparse ++ "(" ++ ns.unparse ++ ") -> " ++ e.unparse; - - local chk :: [Message] = - if null(qn.lookupValue.dcls) || ns.varBinderCount == length(prod_type.inputTypes) then [] - else [err(qn.location, qn.name ++ " has " ++ toString(length(prod_type.inputTypes)) ++ " parameters but " ++ toString(ns.varBinderCount) ++ " patterns were provided")]; - - top.errors <- qn.lookupValue.errors; - - local prod_type :: Type = - fullySkolemizeProductionType(qn.lookupValue.typerep); -- that says FULLY. See the comments on that function. - - ns.bindingTypes = prod_type.inputTypes; - ns.bindingIndex = 0; - ns.bindingNames = if null(qn.lookupValue.dcls) then [] else qn.lookupValue.dcl.namedSignature.inputNames; - ns.matchingAgainst = if null(qn.lookupValue.dcls) then nothing() else just(qn.lookupValue.dcl); - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = composeSubst(errCheck2.upSubst, top.finalSubst); -- part of the - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = composeSubst(errCheck2.upSubst, top.finalSubst); -- threading hack - - errCheck1 = check(decoratedType(prod_type.outputType), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, qn.name ++ " has type " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - -- For GADTs, threading gets a bit weird. - -- TODO: we SHOULD check that the "base type" is accurate for the pattern / scrutineeType first. - -- but for now for simplicity, we avoid that. - -- So for now, we're just skipping over this case entirely: - top.upSubst = top.downSubst; - - -- AFTER everything is done elsewhere, we come back with finalSubst, and we produce the refinement, and thread THAT through everything. - errCheck1.downSubst = composeSubst(top.finalSubst, produceRefinement(top.scrutineeType, decoratedType(prod_type.outputType))); - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - -- Okay, now update the finalSubst.... - e.finalSubst = errCheck2.upSubst; - -- Here ends the hack - - e.env = newScopeEnv(ns.defs, top.env); - - top.translation = "if(scrutineeNode instanceof " ++ makeClassName(qn.lookupValue.fullName) ++ - ") { " ++ ns.translation ++ " return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ e.translation ++ "; }"; -} - --- TODO: We currently provide the below for ease of translation from complex case exprs, but --- we should really translate those to appropriate expressions, and not handle primitive types here - -abstract production integerPattern -top::PrimPattern ::= i::Int_t '->' e::Expr -{ - top.unparse = i.lexeme ++ " -> " ++ e.unparse; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(intType(), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, i.lexeme ++ " is an " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - top.translation = "if(scrutinee == " ++ i.lexeme ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ - e.translation ++ "; }"; -} -abstract production floatPattern -top::PrimPattern ::= f::Float_t '->' e::Expr -{ - top.unparse = f.lexeme ++ " -> " ++ e.unparse; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(floatType(), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, f.lexeme ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - top.translation = "if(scrutinee == " ++ f.lexeme ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ - e.translation ++ "; }"; -} -abstract production stringPattern -top::PrimPattern ::= i::String_t '->' e::Expr -{ - top.unparse = i.lexeme ++ " -> " ++ e.unparse; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(stringType(), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, i.lexeme ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - top.translation = "if(scrutinee.equals(" ++ i.lexeme ++ ")) { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ - e.translation ++ "; }"; -} -abstract production booleanPattern -top::PrimPattern ::= i::String '->' e::Expr -{ - top.unparse = i ++ " -> " ++ e.unparse; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(boolType(), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, i ++ " is a " ++ errCheck1.leftpp ++ " but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - top.translation = "if(scrutinee == " ++ i ++ ") { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ - e.translation ++ "; }"; -} -abstract production nilPattern -top::PrimPattern ::= e::Expr -{ - top.unparse = "nil() -> " ++ e.unparse; - - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - - errCheck1 = check(listType(freshType()), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, "nil matches lists but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - top.translation = "if(scrutinee.nil()) { return (" ++ performSubstitution(top.returnType, top.finalSubst).transType ++ ")" ++ - e.translation ++ "; }"; -} -abstract production conslstPattern -top::PrimPattern ::= h::Name t::Name e::Expr -{ - top.unparse = "cons(" ++ h.unparse ++ ", " ++ t.unparse ++ ") -> " ++ e.unparse; - - local h_fName :: String = toString(genInt()) ++ ":" ++ h.name; - local t_fName :: String = toString(genInt()) ++ ":" ++ t.name; - local attribute errCheck1 :: TypeCheck; errCheck1.finalSubst = top.finalSubst; - local attribute errCheck2 :: TypeCheck; errCheck2.finalSubst = top.finalSubst; - local elemType :: Type = freshType(); - - errCheck1 = check(listType(elemType), top.scrutineeType); - top.errors <- if errCheck1.typeerror - then [err(top.location, "cons matches lists but we're trying to match against " ++ errCheck1.rightpp)] - else []; - - errCheck2 = check(e.typerep, top.returnType); - top.errors <- if errCheck2.typeerror - then [err(e.location, "pattern expression should have type " ++ errCheck2.rightpp ++ " instead it has type " ++ errCheck2.leftpp)] - else []; - - errCheck1.downSubst = top.downSubst; - e.downSubst = errCheck1.upSubst; - errCheck2.downSubst = e.upSubst; - top.upSubst = errCheck2.upSubst; - - local consdefs :: [Def] = - [lexicalLocalDef(top.grammarName, top.location, h_fName, elemType, noVertex(), []), - lexicalLocalDef(top.grammarName, top.location, t_fName, top.scrutineeType, noVertex(), [])]; - - e.env = newScopeEnv(consdefs, top.env); - - top.translation = - let - elemTrans :: String = performSubstitution(elemType, top.finalSubst).transType, - listTrans :: String = performSubstitution(top.scrutineeType, top.finalSubst).transType - in - "if(!scrutineeIter.nil()) {" ++ - makeSpecialLocalBinding(h_fName, s"(${elemTrans})scrutinee.head()", elemTrans) ++ - makeSpecialLocalBinding(t_fName, s"(${listTrans})scrutinee.tail()", listTrans) ++ - "return " ++ e.translation ++ "; }" - end; -} - - diff --git a/grammars/silver/modification/primitivepattern/Types.sv b/grammars/silver/modification/primitivepattern/Types.sv deleted file mode 100644 index c8dd69e9a..000000000 --- a/grammars/silver/modification/primitivepattern/Types.sv +++ /dev/null @@ -1,277 +0,0 @@ -grammar silver:modification:primitivepattern; - -import silver:modification:ffi only foreignType; -- so we cover foreignType with the 'refine' hack below. TODO - -{-- - - Turns the existential variables of a production type into skolem constants, - - and freshen the rest. - - e.g. (?a -> ?b -> F ?a) becomes (?c -> !d -> F ?c) - - This is done so we can just unify the scrutinee type an go, no hairy details! - - (This is used for *non-gadt* productions.) - -} -function skolemizeProductionType -Type ::= te::Type -{ - local attribute existentialVars :: [TyVar]; - existentialVars = removeTyVars(te.freeVariables, te.outputType.freeVariables); - - local attribute skolemize :: Substitution; - skolemize = composeSubst( - zipVarsIntoSkolemizedSubstitution(existentialVars, freshTyVars(length(existentialVars))), - zipVarsIntoSubstitution(te.outputType.freeVariables, freshTyVars(length(te.outputType.freeVariables)))); - - return performRenaming(te, skolemize); -} - -{-- - - (This is used for *gadt* productions.) - - wat? why? well, one skolem constant is as good as another, and we're here INTRODUCING - - new variables, and we need to make them skolem constants. - - - - Here's the example, suppose we have 'arrow :: T -> T -> T>' - - and we do 'case (::TypeExpr) of arrow(...)' we're going to refine - - the c to A, but there's a HUGE HUGE PROBLEM THERE because we can't - - allow a and b to be unified together later on, because we have no idea what - - types they are! So a and b MUST wind up as different skolem constants, - - not as type variables, despite appearing in the 'output type'. - - - - So my solution right now is to skolemize the entire type, and I *think* this - - works just fine... for now. The reason is that we're going OutsideIn, so - - type checking should be 'completed'. That is, there should be - - *** NO TYPE VARIABLES AT ALL *** in the scrutineeType anymore. - - Either they got unified with some skolem constant, got unified with some type - - or an error should have been raised somewhere. (Even once we add real inference - - this should be the case, since all free type variables should end up unified with - - some skolem constant upon generalization of an expression...) - - - - TODO: what about nontermination / truely useless ones? - - case error("lol") of eq() -> "umm" | unit() -> "lol" end - - is a-okay with the type checker, but that's because of the TODO in prodPatternGadt. - - Could there be any other issues? - - - - And since we're just doing a 'refine' afterwards, well... one skolem constant - - is as good as another, as far as correctness goes, anyway... - -} -function fullySkolemizeProductionType -Type ::= te::Type -{ - local attribute skolemize :: Substitution; - skolemize = zipVarsIntoSkolemizedSubstitution(te.freeVariables, freshTyVars(length(te.freeVariables))); - - return performRenaming(te, skolemize); -} - - - ---- This is unification, EXCEPT that skolem constants behave like type variables! - -inherited attribute refineWith :: Type occurs on Type; -synthesized attribute refine :: Substitution occurs on Type; - -aspect production varType -top::Type ::= tv::TyVar -{ - top.refine = - case top.refineWith of - | varType(j) -> - if tyVarEqual(tv, j) - then emptySubst() - else subst(tv, top.refineWith) - | _ -> if containsTyVar(tv, top.refineWith.freeVariables) - then errorSubst("Infinite type! Tried to refine with " ++ prettyType(top.refineWith)) - else subst(tv, top.refineWith) - end; -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ - top.refine = - case top.refineWith of - | skolemType(j) -> - if tyVarEqual(tv, j) - then emptySubst() - else subst(tv, top.refineWith) - | _ -> if containsTyVar(tv, top.refineWith.freeVariables) - then errorSubst("Infinite type! Tried to refine with " ++ prettyType(top.refineWith)) - else subst(tv, top.refineWith) - end; -} - -aspect production errorType -top::Type ::= -{ - top.refine = emptySubst(); -} - -aspect production intType -top::Type ::= -{ - top.refine = - case top.refineWith of - | intType() -> emptySubst() - | _ -> errorSubst("Tried to refine Integer with " ++ prettyType(top.refineWith)) - end; -} - -aspect production boolType -top::Type ::= -{ - top.refine = - case top.refineWith of - | boolType() -> emptySubst() - | _ -> errorSubst("Tried to refine Boolean with " ++ prettyType(top.refineWith)) - end; -} - -aspect production floatType -top::Type ::= -{ - top.refine = - case top.refineWith of - | floatType() -> emptySubst() - | _ -> errorSubst("Tried to refine Float with " ++ prettyType(top.refineWith)) - end; -} - -aspect production stringType -top::Type ::= -{ - top.refine = - case top.refineWith of - | stringType() -> emptySubst() - | _ -> errorSubst("Tried to refine Boolean with " ++ prettyType(top.refineWith)) - end; -} - -aspect production terminalIdType -top::Type ::= -{ - top.refine = - case top.refineWith of - | terminalIdType() -> emptySubst() - | _ -> errorSubst("Tried to refine TerminalId with " ++ prettyType(top.refineWith)) - end; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - top.refine = - case top.refineWith of - | nonterminalType(ofn, op) -> - if fn == ofn - then refineAll( params, op ) - else errorSubst("Tried to refine conflicting nonterminal types " ++ fn ++ " and " ++ ofn) - | _ -> errorSubst("Tried to refine nonterminal type " ++ fn ++ " with " ++ prettyType(top.refineWith)) - end; -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.refine = - case top.refineWith of - | terminalType(ofn) -> - if fn == ofn - then emptySubst() - else errorSubst("Tried to refine conflicting terminal types " ++ fn ++ " and " ++ ofn) - | _ -> errorSubst("Tried to refine terminal type " ++ fn ++ " with " ++ prettyType(top.refineWith)) - end; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - top.refine = - case top.refineWith of - | decoratedType(ote) -> refine(te, ote) - | _ -> errorSubst("Tried to refine decorated type with " ++ prettyType(top.refineWith)) - end; -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.refine = - case top.refineWith of - | functionType(oo, op, onp) -> refineAll(out :: params ++ map((.argType), namedParams), oo :: op ++ map((.argType), onp)) - | _ -> errorSubst("Tried to refine function type with " ++ prettyType(top.refineWith)) - end; -} - -aspect production foreignType -top::Type ::= fn::String transType::String params::[Type] -{ - top.refine = - case top.refineWith of - | foreignType(ofn, _, op) -> - if fn == ofn - then refineAll( params, op ) - else errorSubst("Tried to refine conflicting foreign types " ++ fn ++ " and " ++ ofn) - | _ -> errorSubst("Tried to refine foreign type " ++ fn ++ " with " ++ prettyType(top.refineWith)) - end; -} - -{-- - - Produces substitutions that may involve skolem constants, as well as free variables - - for constructors. - - - - @param scrutineeType The decorated type of the value being examined. Should not be a type variable! - - @param constructorType The decorated type of the production's product (i.e. the type it constructs) - -} -function produceRefinement -Substitution ::= scrutineeType::Type constructorType::Type -{ - -- only do refinement if they're the same type constructor. - -- If you look at the type rules, you'll notice they're requiring "T" be the same, - -- and this refinement only happens on the parameters (i.e. fmgu(T p = T a)) - return case scrutineeType, constructorType of - | decoratedType(nonterminalType(n1, p1)), decoratedType(nonterminalType(n2,p2)) - -> if n1 == n2 then refineAll(p1,p2) else emptySubst() - | _, _ -> emptySubst() - end; -} - -function refine -Substitution ::= te1::Type te2::Type -{ - local attribute leftward :: Substitution; - leftward = te1.refine; - te1.refineWith = te2; - - local attribute rightward :: Substitution; - rightward = te2.refine; - te2.refineWith = te1; - - return if null(leftward.substErrors) - then leftward -- arbitrary choice if both work, but if they are confluent, it's okay - else rightward; -- arbitrary choice of errors. Non-confluent!! -} -function refineAll -Substitution ::= te1::[Type] te2::[Type] -{ - local attribute first :: Substitution; - first = refine(head(te1), head(te2)); - - return if null(te1) && null(te2) - then emptySubst() - else if null(te1) || null(te2) - then errorSubst("Internal error: refineing mismatching numbers") - else composeSubst(first, refineAll( mapSubst(tail(te1), first), - mapSubst(tail(te2), first) )); -} - - --------- -function isOnlyTyVars -Boolean ::= ls::[Type] -{ - return case ls of - | [] -> true - | varType(_) :: t -> isOnlyTyVars(t) - | skolemType(_) :: t -> isOnlyTyVars(t) - | _ -> false - end; -} - diff --git a/grammars/silver/modification/primitivepattern/VarBinders.sv b/grammars/silver/modification/primitivepattern/VarBinders.sv deleted file mode 100644 index 828a1af1f..000000000 --- a/grammars/silver/modification/primitivepattern/VarBinders.sv +++ /dev/null @@ -1,178 +0,0 @@ -grammar silver:modification:primitivepattern; - -import silver:translation:java:core; -import silver:translation:java:type; - -import silver:modification:let_fix only makeSpecialLocalBinding, lexicalLocalDef; - -import silver:definition:flow:ast only hasVertex, noVertex, PatternVarProjection, patternVarProjection, anonVertexType, ExprVertexInfo, FlowVertex; --- also unfortunately placed references to flowEnv - -nonterminal VarBinders with - config, grammarName, env, compiledGrammars, frame, - location, unparse, errors, defs, - bindingTypes, bindingIndex, translation, varBinderCount, - finalSubst, flowProjections, bindingNames, flowEnv, matchingAgainst; -nonterminal VarBinder with - config, grammarName, env, compiledGrammars, frame, - location, unparse, errors, defs, - bindingType, bindingIndex, translation, - finalSubst, flowProjections, bindingName, flowEnv, matchingAgainst; - -propagate errors, defs on VarBinders, VarBinder; - ---- Types of each child -inherited attribute bindingTypes :: [Type]; -inherited attribute bindingType :: Type; ---- Index of each child -inherited attribute bindingIndex :: Integer; ---- Names of each child (for flow analysis) -inherited attribute bindingNames :: [String]; -inherited attribute bindingName :: String; ---- Extractions of decoration sites from children -synthesized attribute flowProjections :: [PatternVarProjection]; - --- The name of the production we're matching against -autocopy attribute matchingAgainst :: Maybe; - -synthesized attribute varBinderCount :: Integer; - - -concrete production oneVarBinder -top::VarBinders ::= v::VarBinder -{ - top.unparse = v.unparse; - - top.translation = v.translation; - top.varBinderCount = 1; - top.flowProjections = v.flowProjections; - - v.bindingIndex = top.bindingIndex; - v.bindingType = - if null(top.bindingTypes) - then errorType() - else head(top.bindingTypes); - v.bindingName = - if null(top.bindingNames) - then "__NONAME" - else head(top.bindingNames); -} -concrete production consVarBinder -top::VarBinders ::= v::VarBinder ',' vs::VarBinders -{ - top.unparse = v.unparse ++ ", " ++ vs.unparse; - - top.translation = v.translation ++ vs.translation; - top.varBinderCount = 1 + vs.varBinderCount; - top.flowProjections = v.flowProjections ++ vs.flowProjections; - - v.bindingIndex = top.bindingIndex; - vs.bindingIndex = top.bindingIndex + 1; - - v.bindingType = - if null(top.bindingTypes) - then errorType() - else head(top.bindingTypes); - vs.bindingTypes = - if null(top.bindingTypes) - then [] - else tail(top.bindingTypes); - v.bindingName = - if null(top.bindingNames) - then "__NONAME" - else head(top.bindingNames); - vs.bindingNames = - if null(top.bindingNames) - then [] - else tail(top.bindingNames); -} -concrete production nilVarBinder -top::VarBinders ::= -{ - top.unparse = ""; - - top.translation = ""; - top.varBinderCount = 0; - top.flowProjections = []; -} - -concrete production varVarBinder -top::VarBinder ::= n::Name -{ - top.unparse = n.unparse; - - -- top.bindingType comes straight from the type in the production signature. - -- Consequently, the child is only auto-decorated if - -- top.bindingType.isDecorable, and never otherwise. - -- (We *DO NOT* want to substitute first... because that will turn the type - -- variables into concrete types! and type variables in a production are - -- NOT automatically decorated!) - local ty :: Type = - if top.bindingType.isDecorable - then decoratedType(top.bindingType) - else top.bindingType; - - production fName :: String = "__pv" ++ toString(genInt()) ++ ":" ++ n.name; - - -- If it's decorable, then we do projections through the production - -- if it's not, then we treat it like a generic reference. - top.flowProjections = - if top.bindingType.isDecorable - then [patternVarProjection(top.bindingName, top.bindingType.typeName, fName)] - else []; - -- because we don't have an 'anonEq' (the nonterminal stitch point gets generated for us by the above contribution) we won't be reported as missing in this production. Checks for presence in remote productions have to be done explicitly - - -- Recall that we emit (vertex, [reference set]) for expressions with a vertex. - -- and the correct value is computed based on how this gets used. - -- (e.g. if 'new' - local vt :: ExprVertexInfo = - if top.bindingType.isDecorable - then hasVertex(anonVertexType(fName)) - else noVertex(); - local deps :: [FlowVertex] = - if top.bindingType.isDecorable - then depsForTakingRef(anonVertexType(fName), ty.typeName, top.flowEnv) - else []; - - top.defs <- [lexicalLocalDef(top.grammarName, n.location, fName, ty, vt, deps)]; - - -- finalSubst is not necessary, downSubst would work fine, but is not threaded through here. - -- the point is that 'ty' for Pair would currently show Pair - -- since top.bindingType comes straight from the production's type in the environment. - -- we need to do some substitution to connect it with the real types. - -- (in the env above its okay, since that must always be consulted with the current substitution, - -- but here we're rendering the translation. it's the end of the line.) - local actualTy :: Type = performSubstitution(ty, top.finalSubst); - - top.translation = - makeSpecialLocalBinding(fName, - "(" ++ actualTy.transType ++ ")scrutinee." ++ - (if top.bindingType.isDecorable - then "childDecorated(" - else "childAsIs(") ++ - toString(top.bindingIndex) ++ ")", - actualTy.transType); - - -- We prevent this to prevent newbies from thinking patterns are "typecase" - -- (Types have to be upper case) - top.errors <- - if !isUpper(substring(0,1,n.name)) then [] - else [err(top.location, "Pattern variables must start with a lower case letter")]; - - -- We prevent this to avoid people possibly forgetting the parens, e.g. writing 'nothing' - -- One thing we could do is specifically raise this error, only if it's the production would be the right type. - -- this would allow us to match 'left' and 'right' on a Pair, for example, but error on Either - top.errors <- - case getValueDcl(n.name, top.env) of - | prodDcl(_,_,_,_) :: _ -> [err(top.location, "Pattern variables cannot have the same name as productions (to avoid confusion)")] - | _ -> [] - end; -} -concrete production ignoreVarBinder -top::VarBinder ::= '_' -{ - top.unparse = "_"; - top.flowProjections = []; - top.translation = ""; -} - diff --git a/grammars/silver/modification/typedecl/DocConfig.sv b/grammars/silver/modification/typedecl/DocConfig.sv deleted file mode 100644 index cfa5e18ed..000000000 --- a/grammars/silver/modification/typedecl/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:modification:typedecl; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Type Declarations\nmenu_title: Type Declarations\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/modification/typedecl/TypeDecl.sv b/grammars/silver/modification/typedecl/TypeDecl.sv deleted file mode 100644 index f82842ad9..000000000 --- a/grammars/silver/modification/typedecl/TypeDecl.sv +++ /dev/null @@ -1,57 +0,0 @@ -grammar silver:modification:typedecl; - -imports silver:definition:core; -imports silver:definition:type; -imports silver:definition:type:syntax; -imports silver:definition:env; - -terminal Type_t 'type' lexer classes {KEYWORD}; - -concrete production typeDecl -top::AGDcl ::= 'type' id::Name tl::BracketedOptTypeExprs '=' te::TypeExpr ';' -{ - top.unparse = "type " ++ id.unparse ++ tl.unparse ++ "=" ++ te.unparse ++ ";"; - - production attribute fName :: String; - fName = top.grammarName ++ ":" ++ id.name; - - top.defs := [typeAliasDef(top.grammarName, id.location, fName, tl.freeVariables, te.typerep)]; - - propagate errors; - top.errors <- tl.errorsTyVars; - - tl.initialEnv = top.env; - tl.env = tl.envBindingTyVars; - te.env = tl.envBindingTyVars; - - -- Redefinition check of the name - top.errors <- - if length(getTypeDclAll(fName, top.env)) > 1 - then [err(id.location, "Type '" ++ fName ++ "' is already bound.")] - else []; - - top.errors <- - if isLower(substring(0,1,id.name)) - then [err(id.location, "Types must be capitalized. Invalid nonterminal name " ++ id.name)] - else []; -} - - - -function typeAliasDef -Def ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - return typeDef(defaultEnvItem(typeDcl(sg,sl,fn,bound,ty))); -} -abstract production typeDcl -top::DclInfo ::= sg::String sl::Location fn::String bound::[TyVar] ty::Type -{ - top.sourceGrammar = sg; - top.sourceLocation = sl; - top.fullName = fn; - - top.typerep = ty; - top.dclBoundVars = bound; -} - - diff --git a/grammars/silver/reflect/AST.sv b/grammars/silver/reflect/AST.sv index 0d963fcfd..47e40c6a5 100644 --- a/grammars/silver/reflect/AST.sv +++ b/grammars/silver/reflect/AST.sv @@ -1,9 +1,5 @@ grammar silver:reflect; -exports core:reflect; - -import core:monad; - -- left(error message) or right(result) synthesized attribute serialize::Either; @@ -13,7 +9,7 @@ aspect production nonterminalAST top::AST ::= prodName::String children::ASTs annotations::NamedASTs { top.serialize = - do (bindEither, returnEither) { + do { childrenSerialize::[String] <- children.serialize; annotationSerialize::[String] <- annotations.serialize; return s"${prodName}(${implode(", ", childrenSerialize ++ annotationSerialize)})"; @@ -24,7 +20,7 @@ aspect production terminalAST top::AST ::= terminalName::String lexeme::String location::Location { top.serialize = - do (bindEither, returnEither) { + do { locationSerialize::String <- serialize(new(location)); return s"terminal(${terminalName}, \"${escapeString(lexeme)}\", ${locationSerialize})"; }; @@ -34,7 +30,7 @@ aspect production listAST top::AST ::= vals::ASTs { top.serialize = - do (bindEither, returnEither) { + do { serializeVals::[String] <- vals.serialize; return s"[${implode(", ", serializeVals)}]"; }; @@ -82,7 +78,7 @@ top::ASTs ::= h::AST t::ASTs { top.astsLength = 1 + t.astsLength; top.serialize = - do (bindEither, returnEither) { + do { hSerialize::String <- h.serialize; tSerialize::[String] <- t.serialize; return hSerialize :: tSerialize; @@ -103,7 +99,7 @@ top::NamedASTs ::= h::NamedAST t::NamedASTs { top.astsLength = 1 + t.astsLength; top.serialize = - do (bindEither, returnEither) { + do { hSerialize::String <- h.serialize; tSerialize::[String] <- t.serialize; return hSerialize :: tSerialize; @@ -123,7 +119,7 @@ aspect production namedAST top::NamedAST ::= n::String v::AST { top.serialize = - do (bindEither, returnEither) { + do { vSerialize::String <- v.serialize; return s"${n}=${vSerialize}"; }; diff --git a/grammars/silver/reflect/Util.sv b/grammars/silver/reflect/Util.sv index a4358f3dd..342ae837d 100644 --- a/grammars/silver/reflect/Util.sv +++ b/grammars/silver/reflect/Util.sv @@ -1,40 +1,10 @@ grammar silver:reflect; +exports silver:reflect:util; + imports silver:reflect:concretesyntax; import silver:langutil; -function reflect -AST ::= x::a -{ - return error("Foreign function"); -} foreign { - "java" : return "(common.Reflection.reflect(%x%))"; -} - -function reflectTypeName -Maybe ::= x::a -{ - return error("Foreign function"); -} foreign { - "java" : return "(common.Reflection.reflectTypeName(%x%))"; -} - -function nativeToString -String ::= x::a -{ - return error("Foreign function"); -} foreign { - "java" : return "(new common.StringCatter(%x%.toString()))"; -} - -function applyAST -Either ::= fn::AST args::[Maybe] namedArgs::[Pair>] -{ - return error("Foreign function"); -} foreign { - "java" : return "(common.Reflection.applyAST(%fn%, %args%, %namedArgs%))"; -} - function serialize Either ::= x::a { @@ -58,3 +28,9 @@ Either ::= fileName::String text::String then left(messagesToString(parseTree.errors)) else right(parseTree.ast); } + +function deserialize +runtimeTypeable a => Either ::= fileName::String text::String +{ + return bind(deserializeAST(fileName, text), reify); +} diff --git a/grammars/silver/reflect/nativeserialize/Util.sv b/grammars/silver/reflect/nativeserialize/Util.sv new file mode 100644 index 000000000..56fb8ef02 --- /dev/null +++ b/grammars/silver/reflect/nativeserialize/Util.sv @@ -0,0 +1,16 @@ + +function nativeSerialize +Either ::= x::a +{ + return error("Not impl"); +} foreign { + "java" : return "common.Reflection.nativeSerialize(%x%)"; +} + +function nativeDeserialize +runtimeTypeable a => Either ::= x::ByteArray +{ + return error("Not impl"); +} foreign { + "java" : return "common.Reflection.nativeDeserialize(%@0@%, %x%)"; +} diff --git a/grammars/silver/reflect/util/Util.sv b/grammars/silver/reflect/util/Util.sv new file mode 100644 index 000000000..a2b436cfd --- /dev/null +++ b/grammars/silver/reflect/util/Util.sv @@ -0,0 +1,53 @@ +grammar silver:reflect:util; + +import silver:langutil; + +function reflect +AST ::= x::a +{ + return error("Foreign function"); +} foreign { + "java" : return "(common.Reflection.reflect((originCtx!=null)?originCtx.rulesAsSilverList():null, %x%))"; +} + +function reify +runtimeTypeable a => Either ::= x::AST +{ + return error("Foreign function"); +} foreign { + "java" : return "common.Reflection.reifyChecked((originCtx!=null)?originCtx.rulesAsSilverList():null, %@0@%, (silver.core.NAST)%x%)"; +} + +function reflectTypeName +Maybe ::= x::a +{ + return error("Foreign function"); +} foreign { + "java" : return "(common.Reflection.reflectTypeName(%x%))"; +} + +function nativeToString +String ::= x::a +{ + return error("Foreign function"); +} foreign { + "java" : return "(new common.StringCatter(%x%.toString()))"; +} + +function applyAST +Either ::= fn::AST args::[Maybe] namedArgs::[Pair>] +{ + return error("Foreign function"); +} foreign { + "java" : return "(common.Reflection.applyAST(originCtx, %fn%, %args%, %namedArgs%))"; +} + +function reifyUnchecked +runtimeTypeable a => a ::= x::AST +{ + return + case reify(x) of + | left(msg) -> error(msg) + | right(a) -> a + end; +} diff --git a/grammars/silver/regex/AbstractSyntax.sv b/grammars/silver/regex/AbstractSyntax.sv new file mode 100644 index 000000000..2a775ef60 --- /dev/null +++ b/grammars/silver/regex/AbstractSyntax.sv @@ -0,0 +1,161 @@ +grammar silver:regex; + +imports silver:core hiding empty, alt; +imports silver:rewrite; +imports silver:langutil; +imports silver:langutil:pp; + +synthesized attribute altPP::Document; +synthesized attribute seqPP::Document; +synthesized attribute basePP::Document; +implicit synthesized attribute classPP::Maybe; + +nonterminal Regex with pp, altPP, seqPP, basePP, classPP, compareTo, isEqual; + +propagate isEqual, compareTo on Regex; + +aspect default production +top::Regex ::= +{ + top.pp = top.altPP; + top.altPP = top.seqPP; + top.seqPP = top.basePP; + top.basePP = parens(top.pp); + implicit top.classPP =; +} + +abstract production char +top::Regex ::= c::Integer +{ + production char::String = charsToString([c]); + top.basePP = escapeRegexChar(char); + top.classPP = escapeRegexClassChar(char); +} + +abstract production wildChar +top::Regex ::= +{ + top.basePP = pp"."; +} + +abstract production charRange +top::Regex ::= l::Integer u::Integer +{ + production lChar::String = charsToString([l]); + production uChar::String = charsToString([u]); + top.basePP = pp"[${escapeRegexChar(lChar)}-${escapeRegexChar(uChar)}]"; + top.classPP = pp"${escapeRegexClassChar(lChar)}-${escapeRegexClassChar(uChar)}"; +} + +abstract production negChars +top::Regex ::= r::Regex +{ + top.basePP = + case top.classPP of + | just(cpp) -> pp"[^${cpp}]" + | nothing() -> pp"(^${r.basePP})" -- Not possible to represent with syntax + end; +} + +abstract production empty +top::Regex ::= +{ + top.basePP = pp"[]"; + top.classPP = pp""; +} + +abstract production epsilon +top::Regex ::= +{ + top.basePP = pp""; +} + +abstract production alt +top::Regex ::= r1::Regex r2::Regex +{ + top.basePP = + case top.classPP of + | just(cpp) -> pp"[${cpp}]" + | nothing() -> pp"(${r1.altPP}|${r2.altPP})" + end; + top.altPP = + case top.classPP of + | just(cpp) -> pp"[${cpp}]" + | nothing() -> pp"${r1.altPP}|${r2.altPP}" + end; + top.classPP = cat(r1.classPP, r2.classPP); +} + +abstract production seq +top::Regex ::= r1::Regex r2::Regex +{ + top.seqPP = cat(r1.seqPP, r2.seqPP); +} + +abstract production star +top::Regex ::= r::Regex +{ + top.basePP = pp"${r.basePP}*"; +} + +abstract production plus +top::Regex ::= r::Regex +{ + top.pp = top.altPP; + top.altPP = top.seqPP; + top.seqPP = top.basePP; + top.basePP = pp"${r.basePP}+"; + forwards to seq(r, star(r)); +} + +abstract production opt +top::Regex ::= r::Regex +{ + top.pp = top.altPP; + top.altPP = top.seqPP; + top.seqPP = top.basePP; + top.basePP = pp"${r.basePP}?"; + forwards to alt(r, epsilon()); +} + +------------------------------------------------ + +{-- + - Returns a regex that matches a string literal. + - (i.e. no interpretation of special characters.) + -} +function regexLiteral +Regex ::= s::String +{ + return + if s == "" then epsilon() + else foldr1(seq, map(char, stringToChars(s))); +} + +function escapeRegexChar +Document ::= char::String +{ + return + case char of + | "+" -> pp"\\+" + | "*" -> pp"\\*" + | "?" -> pp"\\?" + | "|" -> pp"\\|" + | "[" -> pp"\\[" + | "(" -> pp"\\(" + | ")" -> pp"\\)" + | "." -> pp"\\." + | _ -> text(escapeString(char)) + end; +} + +function escapeRegexClassChar +Document ::= char::String +{ + return + case char of + | "-" -> pp"\\-" + | "]" -> pp"\\]" + | _ -> text(escapeString(char)) + end; +} diff --git a/grammars/silver/regex/Arbitrary.sv b/grammars/silver/regex/Arbitrary.sv new file mode 100644 index 000000000..c21ecfeb9 --- /dev/null +++ b/grammars/silver/regex/Arbitrary.sv @@ -0,0 +1,120 @@ +grammar silver:regex; + +import silver:util:random; + +-- Generate a random string matching a regex +implicit synthesized attribute genArbMatch::RandomGen; + +-- The desired probability of generating an additional repeat from a Kleene * +restricted inherited attribute starProb::Float; + +-- The total number of alternatives up to (altCountIn) and including (altCount) this regex +-- in this chain/tree of alts. +-- Invariant: any Regex that is not the direct child of an alt should have altCountIn == 0. +restricted inherited attribute altCountIn::Integer; +restricted synthesized attribute altCount::Integer; + +-- All the "leaf" regexes of some chain/tree of alts +type GenInhs = {starProb, altCountIn}; +restricted synthesized attribute altOptions::[Decorated Regex with GenInhs]; + +attribute genArbMatch, starProb, altCountIn, altCount, altOptions occurs on Regex; +-- propagate starProb on Regex; -- TODO: Make propagate work with restricted attributes + +aspect default production +top::Regex ::= +{ + top.altCount = top.altCountIn + 1; + top.altOptions = [top]; +} + +aspect production empty +top::Regex ::= +{ + top.genArbMatch = error("Can't generate a match for empty regex"); + top.altCount = top.altCountIn; + top.altOptions = []; +} + +aspect production epsilon +top::Regex ::= +{ + top.genArbMatch = ""; +} + +aspect production char +top::Regex ::= c::Integer +{ + top.genArbMatch = char; +} + +aspect production wildChar +top::Regex ::= +{ + -- Generate any ASCII char, excluding \x00 and \n + top.genArbMatch = + let i::Integer = randomRange(1, 126) + in charsToString([if i < newlineChar then i else i + 1]) + end; + top.altCount = top.altCountIn + 126; +} + +aspect production charRange +top::Regex ::= l::Integer u::Integer +{ + top.genArbMatch = charsToString([randomRange(l, u)]); + top.altCount = top.altCountIn + u - l + 1; +} + +global asciiChars::[String] = map(\ c::Integer -> charsToString([c]), range(1, 128)); +aspect production negChars +top::Regex ::= r::Regex +{ + -- Generate any ASCII char that doesn't match r + production validAsciiChars::[String] = filter(\ c::String -> !(c =~ r), asciiChars); + top.genArbMatch = head(drop(randomRange(0, length(validAsciiChars) - 1), validAsciiChars)); + top.altCount = top.altCountIn + length(validAsciiChars); + r.altCountIn = 0; +} + +aspect production seq +top::Regex ::= r1::Regex r2::Regex +{ + top.genArbMatch = r1.genArbMatch ++ r2.genArbMatch; + + r1.starProb = top.starProb; + r2.starProb = top.starProb; + r1.altCountIn = 0; + r2.altCountIn = 0; +} + +aspect production alt +top::Regex ::= r1::Regex r2::Regex +{ + -- Choose some "leaf" regex in this chain of alts, weighted by the number of alternatives in each leaf. + top.genArbMatch = + -- Invariant: this should only be computed at the *top* of a chain/tree of alts, + -- i.e. when top.altCountIn == 0. + if top.altCountIn != 0 then error("genArbMatch on alt when top.altCount != 0") else + let i::Integer = randomRange(0, top.altCount - 1) + in head(dropWhile(\ r::Decorated Regex with GenInhs -> !(r.altCountIn <= i && i < r.altCount), top.altOptions)).genArbMatch + end; + + thread altCountIn, altCount on top, r1, r2, top; + top.altOptions = r1.altOptions ++ r2.altOptions; + + r1.starProb = top.starProb; + r2.starProb = top.starProb; +} + +aspect production star +top::Regex ::= r::Regex +{ + top.genArbMatch = + if random < top.starProb + then r.genArbMatch ++ top.genArbMatch + else ""; + + r.starProb = top.starProb; + r.altCountIn = 0; +} diff --git a/grammars/silver/regex/Matching.sv b/grammars/silver/regex/Matching.sv new file mode 100644 index 000000000..85b03bbba --- /dev/null +++ b/grammars/silver/regex/Matching.sv @@ -0,0 +1,115 @@ +grammar silver:regex; + +synthesized attribute nullable::Boolean occurs on Regex; + +inherited attribute wrt::Integer occurs on Regex; -- Char +synthesized attribute deriv::Regex occurs on Regex; + +-- Simplification using strategy attributes +strategy attribute simpl = + innermost( + rule on Regex of + | seq(empty(), r) -> empty() + | seq(r, empty()) -> empty() + | seq(epsilon(), r) -> r + | seq(r, epsilon()) -> r + | alt(empty(), r) -> r + | alt(r, empty()) -> r + | alt(epsilon(), r) when r.nullable -> r + | alt(r, epsilon()) when r.nullable -> r + | alt(r1, r2) when r1 == r2 -> r1 + | star(empty()) -> epsilon() + | star(epsilon()) -> epsilon() + end) + occurs on Regex; + +strategy attribute simplDeriv = deriv <* simpl occurs on Regex; + +function matchStep +Regex ::= r::Regex c::Integer +{ + r.wrt = c; + return r.simplDeriv; +} + +function matches +Boolean ::= r::Regex s::String +{ + return foldl(matchStep, r, stringToChars(s)).nullable; +} + +propagate simpl, simplDeriv on Regex; + +aspect production empty +top::Regex ::= +{ + top.nullable = false; + top.deriv = empty(); +} + +aspect production epsilon +top::Regex ::= +{ + top.nullable = true; + top.deriv = empty(); +} + +aspect production char +top::Regex ::= c::Integer +{ + top.nullable = false; + top.deriv = if c == top.wrt then epsilon() else empty(); +} + +global newlineChar::Integer = head(stringToChars("\n")); +aspect production wildChar +top::Regex ::= +{ + top.nullable = false; + top.deriv = if top.wrt != newlineChar then epsilon() else empty(); +} + +aspect production charRange +top::Regex ::= l::Integer u::Integer +{ + top.nullable = false; + top.deriv = if l <= top.wrt && top.wrt <= u then epsilon() else empty(); +} + +aspect production negChars +top::Regex ::= r::Regex +{ + top.nullable = false; + top.deriv = if r.deriv.nullable then empty() else epsilon(); + r.wrt = top.wrt; +} + +aspect production seq +top::Regex ::= r1::Regex r2::Regex +{ + top.nullable = r1.nullable && r2.nullable; + top.deriv = + alt( + seq(r1.deriv, r2), + if r1.nullable then r2.deriv else empty()); + r1.wrt = top.wrt; + r2.wrt = top.wrt; +} + +aspect production alt +top::Regex ::= r1::Regex r2::Regex +{ + top.nullable = r1.nullable || r2.nullable; + top.deriv = alt(r1.deriv, r2.deriv); + r1.wrt = top.wrt; + r2.wrt = top.wrt; +} + +aspect production star +top::Regex ::= r::Regex +{ + top.nullable = true; + top.deriv = seq(r.deriv, top); + r.wrt = top.wrt; +} + diff --git a/grammars/silver/regex/concrete_syntax/Regex.sv b/grammars/silver/regex/concrete_syntax/Regex.sv new file mode 100644 index 000000000..d026a9717 --- /dev/null +++ b/grammars/silver/regex/concrete_syntax/Regex.sv @@ -0,0 +1,220 @@ +grammar silver:regex:concrete_syntax; + +imports silver:langutil; +imports silver:langutil:lsp as lsp; +imports silver:regex as abs; + +lexer class Operator extends lsp:Regexp; +lexer class Escape extends lsp:Regexp; + +terminal Plus_t '+' lexer classes { Operator }; +terminal Kleene_t '*' lexer classes { Operator }; +terminal Optional_t '?' lexer classes { Operator }; +terminal Choice_t '|' lexer classes { Operator }; +terminal Range_t '-' lexer classes { Operator }; +terminal RegexNot_t '^' lexer classes { Operator }; +terminal RegexLBrack_t '[' lexer classes { Operator }; +terminal RegexRBrack_t ']' lexer classes { Operator }; +terminal RegexLParen_t '(' lexer classes { Operator }; +terminal RegexRParen_t ')' lexer classes { Operator }; +terminal RegexWildcard_t '.' lexer classes { Operator }; +terminal RegexChar_t /./ lexer classes { Escape }; +terminal EscapedChar_t /\\./ lexer classes { Escape }; + +-- Disambiguate these, rather than using lexical precedence, +-- so we can avoid superfluous escapes (e.g. /--.*/). +-- This is the behavior of most regex libraries. +disambiguate RegexChar_t, Plus_t { pluck Plus_t; } +disambiguate RegexChar_t, Kleene_t { pluck Kleene_t; } +disambiguate RegexChar_t, Optional_t { pluck Optional_t; } +disambiguate RegexChar_t, Choice_t { pluck Choice_t; } +disambiguate RegexChar_t, Range_t { pluck Range_t; } +disambiguate RegexChar_t, RegexNot_t { pluck RegexNot_t; } +disambiguate RegexChar_t, RegexLBrack_t { pluck RegexLBrack_t; } +disambiguate RegexChar_t, RegexRBrack_t { pluck RegexRBrack_t; } +disambiguate RegexChar_t, RegexLParen_t { pluck RegexLParen_t; } +disambiguate RegexChar_t, RegexRParen_t { pluck RegexRParen_t; } +disambiguate RegexChar_t, RegexWildcard_t { pluck RegexWildcard_t; } + +{-- + - A basic regular expression. + - + - At lowest precedence, a regex consists of a series of choices (a|b|c) + -} +nonterminal Regex with unparse, ast; + +concrete production regexEpsilon +top::Regex ::= +{ + top.unparse = ""; + abstract abs:epsilon; +} + +concrete production regexSeq +top::Regex ::= h::RegexSeq +{ + top.unparse = h.unparse; + top.ast = h.ast; +} + +concrete production regexChoice +top::Regex ::= h::RegexSeq '|' t::Regex +{ + top.unparse = h.unparse ++ "|" ++ t.unparse; + abstract abs:alt; +} + + +{-- + - A sequence of regular expressions. + -} +nonterminal RegexSeq with unparse, ast; + +concrete production regexSeqSnoc +top::RegexSeq ::= h::RegexSeq t::RegexRepetition +{ + top.unparse = h.unparse ++ t.unparse; + abstract abs:seq; +} + +concrete production regexSeqOne +top::RegexSeq ::= t::RegexRepetition +{ + top.unparse = t.unparse; + top.ast = t.ast; +} + + +{-- + - A RegexItem with an optional repetition operator (*+?) + -} +nonterminal RegexRepetition with unparse, ast; + +concrete production regexKleene +top::RegexRepetition ::= i::RegexItem '*' +{ + top.unparse = i.unparse ++ "*"; + abstract abs:star; +} + +concrete production regexPlus +top::RegexRepetition ::= i::RegexItem '+' +{ + top.unparse = i.unparse ++ "+"; + abstract abs:plus; +} + +concrete production regexOptional +top::RegexRepetition ::= i::RegexItem '?' +{ + top.unparse = i.unparse ++ "?"; + abstract abs:opt; +} + +concrete production regexOnce +top::RegexRepetition ::= i::RegexItem +{ + top.unparse = i.unparse; + top.ast = i.ast; +} + + +{-- + - A single matched entity (char, wildcard, set, group) + -} +nonterminal RegexItem with unparse, ast; -- characters or sequences/sets + +concrete production regexCharItem +top::RegexItem ::= char::RegexChar +{ + top.unparse = char.unparse; + abstract abs:char; +} + +concrete production regexWildcard +top::RegexItem ::= '.' +{ + top.unparse = "."; + abstract abs:wildChar; +} + +concrete production regexSet +top::RegexItem ::= '[' g::RegexCharSet ']' +{ + top.unparse = "[" ++ g.unparse ++ "]"; + top.ast = g.ast; +} + +concrete production regexSetInverted +top::RegexItem ::= '[' '^' g::RegexCharSet ']' +{ + top.unparse = "[^" ++ g.unparse ++ "]"; + abstract abs:negChars; +} + +concrete production regexGroup +top::RegexItem ::= '(' r::Regex ')' +{ + top.unparse = "(" ++ r.unparse ++ ")"; + top.ast = r.ast; +} + + +{-- + - A list of options or ranges within a regexSet. + -} +nonterminal RegexCharSet with unparse, ast; + +concrete production regexCharSetSnoc +top::RegexCharSet ::= h::RegexCharSet t::RegexCharSetItem +{ + top.unparse = h.unparse ++ t.unparse; + abstract abs:alt; +} + +concrete production regexCharSetOne +top::RegexCharSet ::= t::RegexCharSetItem +{ + top.unparse = t.unparse; + top.ast = t.ast; +} + + +{-- + - An option or range within a regexSet. + -} +nonterminal RegexCharSetItem with unparse, ast; + +concrete production regexSetChar +top::RegexCharSetItem ::= char::RegexChar +{ + top.unparse = char.unparse; + abstract abs:char; +} + +concrete production regexSetRange +top::RegexCharSetItem ::= l::RegexChar '-' u::RegexChar +{ + top.unparse = l.unparse ++ "-" ++ u.unparse; + abstract abs:charRange; +} + + +{-- + - A character, escaped or otherwise. + -} +nonterminal RegexChar with unparse, ast; + +concrete production regexChar +top::RegexChar ::= char::RegexChar_t +{ + top.unparse = char.lexeme; + top.ast = head(stringToChars(char.lexeme)); +} + +concrete production regexEscapedChar +top::RegexChar ::= esc::EscapedChar_t +{ + top.unparse = esc.lexeme; + top.ast = head(stringToChars(unescapeString(esc.lexeme))); +} diff --git a/grammars/silver/rewrite/AST.sv b/grammars/silver/rewrite/AST.sv index d8aad7a06..7a29220fe 100644 --- a/grammars/silver/rewrite/AST.sv +++ b/grammars/silver/rewrite/AST.sv @@ -36,7 +36,7 @@ aspect production nonterminalAST top::AST ::= prodName::String children::ASTs annotations::NamedASTs { top.allResult = - do (bindMaybe, returnMaybe) { + do { childrenResult::ASTs <- children.allResult; annotationsResult::NamedASTs <- annotations.allResult; return nonterminalAST(prodName, childrenResult, annotationsResult); @@ -60,8 +60,8 @@ top::AST ::= prodName::String children::ASTs annotations::NamedASTs | nothing(), nothing() -> nothing() end; top.traversalResult = - do (bindMaybe, returnMaybe) { - if prodName != top.productionName then nothing(); + do { + if prodName != top.productionName then nothing() else just(unit()); childrenResult::ASTs <- children.traversalResult; annotationsResult::NamedASTs <- annotations.traversalResult; return nonterminalAST(prodName, childrenResult, annotationsResult); @@ -71,11 +71,7 @@ top::AST ::= prodName::String children::ASTs annotations::NamedASTs aspect production terminalAST top::AST ::= terminalName::String lexeme::String location::Location { - top.allResult = - do (bindMaybe, returnMaybe) { - locationResult::Location <- rewriteWith(top.givenStrategy, location); - return terminalAST(terminalName, lexeme, locationResult); - }; + top.allResult = map(terminalAST(terminalName, lexeme, _), rewriteWith(top.givenStrategy, location)); -- Exactly one rewritable child top.someResult = top.allResult; top.oneResult = top.allResult; @@ -84,12 +80,12 @@ top::AST ::= terminalName::String lexeme::String location::Location aspect production listAST top::AST ::= vals::ASTs { - local h::AST = case vals of consAST(h, _) -> h end; - local t::AST = case vals of consAST(_, t) -> listAST(t) end; + local h::AST = case vals of consAST(h, _) -> h | _ -> error("not consAST") end; + local t::AST = case vals of consAST(_, t) -> listAST(t) | _ -> error("not consAST") end; top.allResult = case vals of | consAST(_, _) -> - do (bindMaybe, returnMaybe) { + do { hResult::AST <- decorate top.givenStrategy with { term = h; }.result; tResult::AST <- decorate top.givenStrategy with { term = t; }.result; return @@ -106,9 +102,9 @@ top::AST ::= vals::ASTs case decorate top.givenStrategy with { term = h; }.result, decorate top.givenStrategy with { term = t; }.result of | just(hResult), just(listAST(tResult)) -> just(listAST(consAST(hResult, tResult))) - | just(hResult), nothing() -> just(listAST(consAST(hResult, case vals of consAST(_, t) -> t end))) + | just(hResult), nothing() -> just(listAST(consAST(hResult, case vals of consAST(_, t) -> t | _ -> error("not consAST") end))) | nothing(), just(listAST(tResult)) -> just(listAST(consAST(h, tResult))) - | nothing(), _ -> nothing() + | _, _ -> nothing() end | nilAST() -> nothing() end; @@ -117,7 +113,7 @@ top::AST ::= vals::ASTs | consAST(_, _) -> case decorate top.givenStrategy with { term = h; }.result, decorate top.givenStrategy with { term = t; }.result of - | just(hResult), _ -> just(listAST(consAST(hResult, case vals of consAST(_, t) -> t end))) + | just(hResult), _ -> just(listAST(consAST(hResult, case vals of consAST(_, t) -> t | _ -> error("not consAST") end))) | nothing(), just(listAST(tResult)) -> just(listAST(consAST(h, tResult))) | nothing(), _ -> nothing() end @@ -127,7 +123,7 @@ top::AST ::= vals::ASTs top.consListCongruenceResult = case vals of | consAST(_, _) -> - do (bindMaybe, returnMaybe) { + do { hResult::AST <- decorate top.headStrategy with { term = h; }.result; tResult::AST <- decorate top.tailStrategy with { term = t; }.result; return @@ -154,7 +150,7 @@ aspect production consAST top::ASTs ::= h::AST t::ASTs { top.allResult = - do (bindMaybe, returnMaybe) { + do { hResult::AST <- decorate top.givenStrategy with { term = h; }.result; tResult::ASTs <- t.allResult; return consAST(hResult, tResult); @@ -173,7 +169,7 @@ top::ASTs ::= h::AST t::ASTs | nothing(), nothing() -> nothing() end; top.traversalResult = - do (bindMaybe, returnMaybe) { + do { hResult::AST <- decorate head(top.childStrategies) with { term = h; }.result; tResult::ASTs <- t.traversalResult; return consAST(hResult, tResult); @@ -202,7 +198,7 @@ top::NamedASTs ::= h::NamedAST t::NamedASTs { top.bindings = h.binding :: t.bindings; top.allResult = - do (bindMaybe, returnMaybe) { + do { hResult::NamedAST <- h.allResult; tResult::NamedASTs <- t.allResult; return consNamedAST(hResult, tResult); @@ -221,7 +217,7 @@ top::NamedASTs ::= h::NamedAST t::NamedASTs | nothing(), nothing() -> nothing() end; top.traversalResult = - do (bindMaybe, returnMaybe) { + do { hResult::NamedAST <- h.traversalResult; tResult::NamedASTs <- t.traversalResult; return consNamedAST(hResult, tResult); @@ -250,7 +246,7 @@ top::NamedAST ::= n::String v::AST { top.binding = pair(n, v); top.allResult = - do (bindMaybe, returnMaybe) { + do { vResult::AST <- decorate top.givenStrategy with { term = v; }.result; return namedAST(n, vResult); }; @@ -261,11 +257,11 @@ top::NamedAST ::= n::String v::AST top.traversalResult = -- Look up and apply all strategies for the annotation -- (it's easier to just handle duplicates than to disallow them.) - mapMaybe( + map( namedAST(n, _), foldl( \ ma::Maybe s::Strategy -> - bindMaybe(ma, \ a::AST -> decorate s with { term = a; }.result), + bind(ma, \ a::AST -> decorate s with { term = a; }.result), just(v), - lookupAllBy(stringEq, n, top.annotationStrategies))); + lookupAll(n, top.annotationStrategies))); } diff --git a/grammars/silver/rewrite/ASTExpr.sv b/grammars/silver/rewrite/ASTExpr.sv index cd4f77b79..6f65a9a70 100644 --- a/grammars/silver/rewrite/ASTExpr.sv +++ b/grammars/silver/rewrite/ASTExpr.sv @@ -100,7 +100,7 @@ top::ASTExpr ::= n::String top.value = fromMaybe( error("Unbound variable " ++ n), - lookupBy(stringEq, n, top.substitutionEnv)); + lookup(n, top.substitutionEnv)); } abstract production missingArgASTExpr @@ -166,7 +166,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | integerAST(x), integerAST(y) -> booleanAST(x > y) | floatAST(x), floatAST(y) -> booleanAST(x > y) | stringAST(x), stringAST(y) -> booleanAST(x > y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -179,7 +179,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | integerAST(x), integerAST(y) -> booleanAST(x < y) | floatAST(x), floatAST(y) -> booleanAST(x < y) | stringAST(x), stringAST(y) -> booleanAST(x < y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -192,7 +192,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | integerAST(x), integerAST(y) -> booleanAST(x >= y) | floatAST(x), floatAST(y) -> booleanAST(x >= y) | stringAST(x), stringAST(y) -> booleanAST(x >= y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -205,7 +205,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | integerAST(x), integerAST(y) -> booleanAST(x <= y) | floatAST(x), floatAST(y) -> booleanAST(x <= y) | stringAST(x), stringAST(y) -> booleanAST(x <= y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -219,7 +219,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | floatAST(x), floatAST(y) -> booleanAST(x == y) | stringAST(x), stringAST(y) -> booleanAST(x == y) | booleanAST(x), booleanAST(y) -> booleanAST(x == y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -233,7 +233,7 @@ top::ASTExpr ::= a::ASTExpr b::ASTExpr | floatAST(x), floatAST(y) -> booleanAST(x != y) | stringAST(x), stringAST(y) -> booleanAST(x != y) | booleanAST(x), booleanAST(y) -> booleanAST(x != y) - | _, _ -> error("Invalid values") + | x, y -> error("TODO: overloaded operator") -- Figure out how to handle this, applyAST doesn't work with type classes end; } @@ -249,6 +249,16 @@ top::ASTExpr ::= c::ASTExpr t::ASTExpr e::ASTExpr end; } +abstract production noteAttachmentASTExpr +top::ASTExpr ::= a::ASTExpr b::ASTExpr +{ + top.pp = pp"attachNote ${a.pp} on {${b.pp}} end"; + top.value = case reify(a.value) of + | right(note) -> attachNote note on b.value end + | left(msg) -> error("Invalid value for noteAttachmentASTExpr's note: " ++ msg) + end; +} + abstract production plusASTExpr top::ASTExpr ::= a::ASTExpr b::ASTExpr { @@ -431,8 +441,8 @@ top::ASTExpr ::= s::ASTExpr e::ASTExpr top.value = case st.result of - | just(a) -> AST { core:just(${a}) } - | nothing() -> AST { core:nothing() } + | just(a) -> AST { silver:core:just(${a}) } + | nothing() -> AST { silver:core:nothing() } end; } diff --git a/grammars/silver/rewrite/ASTPattern.sv b/grammars/silver/rewrite/ASTPattern.sv index 8b6a0e80c..63ceb607d 100644 --- a/grammars/silver/rewrite/ASTPattern.sv +++ b/grammars/silver/rewrite/ASTPattern.sv @@ -11,10 +11,10 @@ top::ASTPattern ::= prodName::String children::ASTPatterns annotations::NamedAST { top.pp = pp"${text(prodName)}(${ppImplode(pp", ", children.pps ++ annotations.pps)})"; - children.matchWith = case top.matchWith of nonterminalAST(_, c, _) -> c end; - annotations.matchWith = case top.matchWith of nonterminalAST(_, _, a) -> a.bindings end; + children.matchWith = case top.matchWith of nonterminalAST(_, c, _) -> c | _ -> error("not nonterminalAST") end; + annotations.matchWith = case top.matchWith of nonterminalAST(_, _, a) -> a.bindings | _ -> error("not nonterminalAST") end; top.substitution = - do (bindMaybe, returnMaybe) { + do { case top.matchWith of | nonterminalAST(otherProdName, _, _) when prodName == otherProdName -> just(unit()) | _ -> nothing() @@ -30,10 +30,10 @@ top::ASTPattern ::= h::ASTPattern t::ASTPattern { top.pp = pp"(${h.pp} :: ${t.pp})"; - h.matchWith = case top.matchWith of listAST(consAST(h, _)) -> h end; - t.matchWith = case top.matchWith of listAST(consAST(_, t)) -> listAST(t) end; + h.matchWith = case top.matchWith of listAST(consAST(h, _)) -> h | _ -> error("not listAST(consAST(_, _))") end; + t.matchWith = case top.matchWith of listAST(consAST(_, t)) -> listAST(t) | _ -> error("not listAST(consAST(_, _))") end; top.substitution = - do (bindMaybe, returnMaybe) { + do { case top.matchWith of | listAST(consAST(_, _)) -> just(unit()) | _ -> nothing() @@ -132,10 +132,10 @@ top::ASTPatterns ::= h::ASTPattern t::ASTPatterns top.pps = h.pp :: t.pps; top.astPatterns = h :: t.astPatterns; - h.matchWith = case top.matchWith of consAST(h, _) -> h end; - t.matchWith = case top.matchWith of consAST(_, t) -> t end; + h.matchWith = case top.matchWith of consAST(h, _) -> h | _ -> error("not consAST") end; + t.matchWith = case top.matchWith of consAST(_, t) -> t | _ -> error("not consAST") end; top.substitution = - do (bindMaybe, returnMaybe) { + do { case top.matchWith of | consAST(_, _) -> just(unit()) | _ -> nothing() @@ -175,7 +175,7 @@ top::NamedASTPatterns ::= h::NamedASTPattern t::NamedASTPatterns { top.pps = h.pp :: t.pps; top.substitution = - do (bindMaybe, returnMaybe) { + do { hSubstitution::[Pair] <- h.substitution; tSubstitution::[Pair] <- t.substitution; return hSubstitution ++ tSubstitution; @@ -212,6 +212,6 @@ top::NamedASTPattern ::= n::String v::ASTPattern v.matchWith = fromMaybe( error("Unexpected annotation " ++ n), - lookupBy(stringEq, n, top.matchWith)); + lookup(n, top.matchWith)); top.substitution = v.substitution; } diff --git a/grammars/silver/rewrite/Strategy.sv b/grammars/silver/rewrite/Strategy.sv index 7f1d4bade..13882630f 100644 --- a/grammars/silver/rewrite/Strategy.sv +++ b/grammars/silver/rewrite/Strategy.sv @@ -1,11 +1,16 @@ grammar silver:rewrite; -- Some of these Strategy productions have very generic names that conflict with core. --- Users must explicitly import core hiding these names, or perform a qualified import, +-- Users must explicitly import silver:core hiding these names, or perform a qualified import, -- e.g. import silver:rewrite as s; -imports core hiding all, repeat; -imports core:monad; +imports silver:core hiding all, fail, id, one, repeat, sequence; + +function rewriteWith +runtimeTypeable a => Maybe ::= s::Strategy x::a +{ + return map(reifyUnchecked, decorate s with {term = reflect(x);}.result); +} inherited attribute term::AST; synthesized attribute result::Maybe; @@ -33,7 +38,7 @@ top::Strategy ::= s1::Strategy s2::Strategy top.pp = pp"(${s1.pp} <* ${s2.pp})"; s1.term = top.term; s2.term = s1.result.fromJust; - top.result = bindMaybe(s1.result, \ AST -> s2.result); + top.result = bind(s1.result, \ AST -> s2.result); } abstract production choice @@ -113,11 +118,7 @@ top::Strategy ::= pattern::ASTPattern result::ASTExpr top.pp = pp"rule(${pattern.pp} -> ${result.pp})"; pattern.matchWith = top.term; result.substitutionEnv = pattern.substitution.fromJust; - top.result = - do (bindMaybe, returnMaybe) { - pattern.substitution; - return result.value; - }; + top.result = do { pattern.substitution; return result.value; }; } abstract production require @@ -127,7 +128,7 @@ top::Strategy ::= pattern::ASTPattern cond::ASTExpr pattern.matchWith = top.term; cond.substitutionEnv = pattern.substitution.fromJust; top.result = - do (bindMaybe, returnMaybe) { + do { pattern.substitution; case cond.value of | booleanAST(b) -> if b then just(unit()) else nothing() @@ -156,7 +157,7 @@ abstract production printTerm top::Strategy ::= { top.pp = pp"print()"; - top.result = unsafeTrace(just(top.term), print(show(80, top.term.pp) ++ "\n\n", unsafeIO())); + top.result = unsafeTrace(just(top.term), printT(show(80, top.term.pp) ++ "\n\n", unsafeIO())); } -- Utilities diff --git a/grammars/silver/support/monto/Error.sv b/grammars/silver/support/monto/Error.sv deleted file mode 100644 index 63ec6e075..000000000 --- a/grammars/silver/support/monto/Error.sv +++ /dev/null @@ -1,24 +0,0 @@ -grammar silver:support:monto; - -import silver:json; -import silver:support:monto:products; - -nonterminal ServiceError with json; - -abstract production errorUnmetDependency -top::ServiceError ::= identifier::ProductIdentifier -{ - top.json = jsonObject( - [ pair("type", jsonString("unmet_dependency")) - , pair("value", identifier.json) - ]); -} - -abstract production errorOther -top::ServiceError ::= msg::String -{ - top.json = jsonObject( - [ pair("type", jsonString("other")) - , pair("value", jsonString(msg)) - ]); -} diff --git a/grammars/silver/support/monto/Notice.sv b/grammars/silver/support/monto/Notice.sv deleted file mode 100644 index 6595a394b..000000000 --- a/grammars/silver/support/monto/Notice.sv +++ /dev/null @@ -1,15 +0,0 @@ -grammar silver:support:monto; - -import silver:json; -import silver:support:monto:products; - -nonterminal ServiceNotice with json; - -abstract production noticeUnusedDependency -top::ServiceNotice ::= identifier::ProductIdentifier -{ - top.json = jsonObject( - [ pair("type", jsonString("unused_dependency")) - , pair("value", identifier.json) - ]); -} diff --git a/grammars/silver/support/monto/Provider.sv b/grammars/silver/support/monto/Provider.sv deleted file mode 100644 index 321334a95..000000000 --- a/grammars/silver/support/monto/Provider.sv +++ /dev/null @@ -1,15 +0,0 @@ -grammar silver:support:monto; - -import silver:json; -import silver:support:monto:products; - -synthesized attribute descriptor :: ProductDescriptor; -synthesized attribute processService :: (Pair [ServiceNotice]> ::= String [Product]); -nonterminal ServiceProvider with descriptor, processService; - -abstract production serviceProvider -top::ServiceProvider ::= descriptor::ProductDescriptor func::(Pair [ServiceNotice]> ::= String [Product]) -{ - top.descriptor = descriptor; - top.processService = func; -} diff --git a/grammars/silver/support/monto/Request.sv b/grammars/silver/support/monto/Request.sv deleted file mode 100644 index 591967a7a..000000000 --- a/grammars/silver/support/monto/Request.sv +++ /dev/null @@ -1,19 +0,0 @@ -grammar silver:support:monto; - -import silver:json; -import silver:support:monto:products; - -synthesized attribute requestIdentifier :: ProductIdentifier; -synthesized attribute requestProducts :: [Product]; -nonterminal BrokerRequest with json, requestIdentifier, requestProducts; - -abstract production brokerRequest -top::BrokerRequest ::= request::ProductIdentifier products::[Product] -{ - top.json = jsonObject( - [ pair("products", jsonArray(map((.json), products))) - , pair("request", request.json) - ]); - top.requestIdentifier = request; - top.requestProducts = products; -} diff --git a/grammars/silver/support/monto/Run.sv b/grammars/silver/support/monto/Run.sv deleted file mode 100644 index 829f6f9e1..000000000 --- a/grammars/silver/support/monto/Run.sv +++ /dev/null @@ -1,9 +0,0 @@ -grammar silver:support:monto; - -function runService -IO ::= service::Service port::Integer ioIn::IO -{ - return error("Foreign function"); -} foreign { - "java": return "common.Util.io(%ioIn%, monto.Server.run(%service%, %port%))"; -} diff --git a/grammars/silver/support/monto/Service.sv b/grammars/silver/support/monto/Service.sv deleted file mode 100644 index a034b1c89..000000000 --- a/grammars/silver/support/monto/Service.sv +++ /dev/null @@ -1,40 +0,0 @@ -grammar silver:support:monto; - -import silver:json; -import silver:support:monto:negotiation; -import silver:support:monto:products; - --- These are "stringly typed" to minimize the effort to FFI to them; you should --- always use the strongly typed service production. -synthesized attribute doNegotiation :: (Pair ::= ServiceBrokerNegotiation); -synthesized attribute onRequest :: (Pair ::= ProductIdentifier [Product]); - -nonterminal Service with doNegotiation, onRequest; - -abstract production service -top::Service ::= negotiation::ServiceNegotiation providers::[ServiceProvider] -{ - top.doNegotiation = \sbn::ServiceBrokerNegotiation -> - pair(negotiation.json.jsonString, negotiationsCompatible(negotiation, sbn)); - top.onRequest = \pi::ProductIdentifier deps::[Product] -> - case find(\p::ServiceProvider -> productDescriptorEq(p.descriptor, asDescriptor(pi)), providers) of - | just(p) -> - let tmp::Pair = case p.processService(pi.productPath, deps) of - | pair(left(errs), ntcs) -> pair(jsonObject( - [ pair("errors", jsonArray(map((.json), errs))) - , pair("notices", jsonArray(map((.json), ntcs))) - ]), 500) - | pair(right(prd), ntcs) -> pair(jsonObject( - [ pair("product", prd.json) - , pair("notices", jsonArray(map((.json), ntcs))) - ]), 200) - end in pair(tmp.fst.jsonString, tmp.snd) end - | nothing() -> pair(pi.json.jsonString, 400) - end; -} - -function asDescriptor -ProductDescriptor ::= pi::ProductIdentifier -{ - return productDescriptor(pi.productName, pi.productLang); -} diff --git a/grammars/silver/support/monto/negotiation/Negotiations.sv b/grammars/silver/support/monto/negotiation/Negotiations.sv deleted file mode 100644 index 1c80911aa..000000000 --- a/grammars/silver/support/monto/negotiation/Negotiations.sv +++ /dev/null @@ -1,33 +0,0 @@ -grammar silver:support:monto:negotiation; - -import silver:json; -import silver:support:monto:products; - -synthesized attribute montoVersion :: ProtocolVersion; - -nonterminal ServiceBrokerNegotiation with montoVersion, json; - -abstract production serviceBrokerNegotiation -top::ServiceBrokerNegotiation ::= monto::ProtocolVersion broker::SoftwareVersion extensions::[String] -{ - top.montoVersion = monto; - top.json = jsonObject( - [ pair("monto", top.montoVersion.json) - , pair("broker", broker.json) - , pair("extensions", jsonArray(map(jsonString, extensions))) - ]); -} - -nonterminal ServiceNegotiation with montoVersion, json; - -abstract production serviceNegotiation -top::ServiceNegotiation ::= monto::ProtocolVersion service::SoftwareVersion extensions::[String] products::[ProductDescriptor] -{ - top.montoVersion = monto; - top.json = jsonObject( - [ pair("monto", top.montoVersion.json) - , pair("service", service.json) - , pair("extensions", jsonArray(map(jsonString, extensions))) - , pair("products", jsonArray(map((.json), products))) - ]); -} diff --git a/grammars/silver/support/monto/negotiation/Semver.sv b/grammars/silver/support/monto/negotiation/Semver.sv deleted file mode 100644 index eb2f37554..000000000 --- a/grammars/silver/support/monto/negotiation/Semver.sv +++ /dev/null @@ -1,14 +0,0 @@ -grammar silver:support:monto:negotiation; - -function negotiationsCompatible -Boolean ::= us::ServiceNegotiation them::ServiceBrokerNegotiation -{ - return semverCompatible(us.montoVersion, them.montoVersion); -} - -function semverCompatible -Boolean ::= us::ProtocolVersion them::ProtocolVersion -{ - -- TODO Do this more correctly... - return us.major == them.major; -} diff --git a/grammars/silver/support/monto/negotiation/Versions.sv b/grammars/silver/support/monto/negotiation/Versions.sv deleted file mode 100644 index 4f8d54797..000000000 --- a/grammars/silver/support/monto/negotiation/Versions.sv +++ /dev/null @@ -1,46 +0,0 @@ -grammar silver:support:monto:negotiation; - -import silver:json; - -synthesized attribute major :: Integer; -synthesized attribute minor :: Integer; -synthesized attribute patch :: Integer; - -nonterminal ProtocolVersion with major, minor, patch, json; - -abstract production protocolVersion -top::ProtocolVersion ::= major::Integer minor::Integer patch::Integer -{ - top.major = major; - top.minor = minor; - top.patch = patch; - top.json = jsonObject( - [ pair("major", jsonInteger(top.major)) - , pair("minor", jsonInteger(top.minor)) - , pair("patch", jsonInteger(top.patch)) - ]); -} - -nonterminal SoftwareVersion with json; - -abstract production softwareVersion -top::SoftwareVersion ::= id::String name::Maybe vendor::Maybe major::Maybe minor::Maybe patch::Maybe -{ - local opts :: [Pair] = catMaybes( - [ pairMaybe("name", jsonString, name) - , pairMaybe("vendor", jsonString, vendor) - , pairMaybe("major", jsonInteger, major) - , pairMaybe("minor", jsonInteger, minor) - , pairMaybe("patch", jsonInteger, patch) - ]); - top.json = jsonObject(pair("id", jsonString(id)) :: opts); -} - -function pairMaybe -Maybe> ::= a::a f::(c ::= b) b::Maybe -{ - return case b of - | just(x) -> just(pair(a, f(x))) - | nothing() -> nothing() - end; -} diff --git a/grammars/silver/support/monto/products/Directory.sv b/grammars/silver/support/monto/products/Directory.sv deleted file mode 100644 index 01b3d57eb..000000000 --- a/grammars/silver/support/monto/products/Directory.sv +++ /dev/null @@ -1,48 +0,0 @@ -grammar silver:support:monto:products; - -import silver:json; - -abstract production directoryProduct -top::ProductValue ::= entries::[DirectoryEntry] -{ - forwards to productValue("directory", jsonArray(map((.json), entries))); -} - -nonterminal DirectoryEntry with json; - -abstract production directoryEntry -top::DirectoryEntry ::= name::String absolutePath::String entryType::DirectoryEntryType -{ - top.json = jsonObject(obj); - local obj :: [Pair] = - [ pair("name", jsonString(name)) - , pair("absolute_path", jsonString(absolutePath)) - , pair("type", entryType.json) - ]; -} - -nonterminal DirectoryEntryType with json; - -abstract production directoryEntryFile -top::DirectoryEntryType ::= -{ - top.json = jsonString("file"); -} - -abstract production directoryEntryDirectory -top::DirectoryEntryType ::= -{ - top.json = jsonString("directory"); -} - -abstract production directoryEntrySymlink -top::DirectoryEntryType ::= -{ - top.json = jsonString("symlink"); -} - -abstract production directoryEntryOther -top::DirectoryEntryType ::= -{ - top.json = jsonString("other"); -} diff --git a/grammars/silver/support/monto/products/Errors.sv b/grammars/silver/support/monto/products/Errors.sv deleted file mode 100644 index f40e882e5..000000000 --- a/grammars/silver/support/monto/products/Errors.sv +++ /dev/null @@ -1,61 +0,0 @@ -grammar silver:support:monto:products; - -import silver:json; -import silver:langutil; - -abstract production errorsProduct -top::ProductValue ::= errs::[Error] -{ - forwards to productValue("errors", jsonArray(map((.json), errs))); -} - -nonterminal Error with json; - -abstract production byteRangeError -top::Error ::= startByte::Integer endByte::Integer message::String severity::ErrorSeverity -{ - top.json = jsonObject(obj); - local obj :: [Pair] = - [ pair("start_byte", jsonInteger(startByte)) - , pair("end_byte", jsonInteger(endByte)) - , pair("message", jsonString(message)) - , pair("severity", severity.json) - ]; -} - -abstract production messageError -top::Error ::= msg::Message -{ - top.json = jsonObject(obj); - local severity :: ErrorSeverity = case msg.severity of - | 0 -> severityInfo() - | 1 -> severityWarning() - | _ -> severityError() - end; - local obj :: [Pair] = - [ pair("start_byte", jsonInteger(msg.where.index)) - , pair("end_byte", jsonInteger(msg.where.endIndex)) - , pair("message", jsonString(msg.output)) - , pair("severity", severity.json) - ]; -} - -nonterminal ErrorSeverity with json; - -abstract production severityError -top::ErrorSeverity ::= -{ - top.json = jsonString("error"); -} - -abstract production severityWarning -top::ErrorSeverity ::= -{ - top.json = jsonString("warning"); -} - -abstract production severityInfo -top::ErrorSeverity ::= -{ - top.json = jsonString("info"); -} diff --git a/grammars/silver/support/monto/products/Highlighting.sv b/grammars/silver/support/monto/products/Highlighting.sv deleted file mode 100644 index b494f1e44..000000000 --- a/grammars/silver/support/monto/products/Highlighting.sv +++ /dev/null @@ -1,96 +0,0 @@ -grammar silver:support:monto:products; - -import silver:json; - -abstract production highlightingProduct -top::ProductValue ::= tokens::[HighlightToken] -{ - forwards to productValue("highlighting", jsonArray(map((.json), tokens))); -} - -nonterminal HighlightToken with json; - -abstract production highlightToken -top::HighlightToken ::= startByte::Integer endByte::Integer color::Color -{ - top.json = jsonObject(obj); - local obj :: [Pair] = - [ pair("start_byte", jsonInteger(startByte)) - , pair("end_byte", jsonInteger(endByte)) - , pair("color", color.json) - ]; -} - -nonterminal Color with json; - -abstract production commentColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("comment")) - ]); -} - -abstract production functionColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("function")) - ]); -} - -abstract production identifierColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("identifier")) - ]); -} - -abstract production keywordColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("keyword")) - ]); -} - -abstract production literalColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("literal")) - ]); -} - -abstract production punctuationColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("punctuation")) - ]); -} - -abstract production typeColor -top::Color ::= -{ - top.json = jsonObject( - [ pair("type", jsonString("token")) - , pair("value", jsonString("type")) - ]); -} - -abstract production paletteColor -top::Color ::= idx::Integer -{ - top.json = jsonObject( - [ pair("type", jsonString("palette")) - , pair("value", jsonInteger(idx)) - ]); -} diff --git a/grammars/silver/support/monto/products/Product.sv b/grammars/silver/support/monto/products/Product.sv deleted file mode 100644 index 8b1674569..000000000 --- a/grammars/silver/support/monto/products/Product.sv +++ /dev/null @@ -1,86 +0,0 @@ -grammar silver:support:monto:products; - -import silver:json; - -synthesized attribute productName :: String; -nonterminal ProductValue with json, productName; - -abstract production productValue -top::ProductValue ::= name::String json::Json -{ - top.json = json; - top.productName = name; -} - -synthesized attribute productLang :: String; -synthesized attribute productPath :: String; -synthesized attribute productValue :: ProductValue; -nonterminal Product with json, productName, productLang, productPath, productValue; - -abstract production product -top::Product ::= value::ProductValue metas::[MetaItem] language::String path::String -{ - top.productName = top.productValue.productName; - top.productLang = language; - top.productPath = path; - top.productValue = value; - top.json = jsonObject( - [ pair("contents", value.json) - , pair("language", jsonString(top.productLang)) - , pair("name", jsonString(top.productName)) - , pair("meta", jsonArray(map((.json), metas))) - , pair("path", jsonString(top.productPath)) - ]); -} - -nonterminal ProductDescriptor with json, productName, productLang; - -abstract production productDescriptor -top::ProductDescriptor ::= name::String language::String -{ - top.productName = name; - top.productLang = language; - top.json = jsonObject( - [ pair("name", jsonString(top.productName)) - , pair("language", jsonString(top.productLang)) - ]); -} - -function productDescriptorEq -Boolean ::= l::ProductDescriptor r::ProductDescriptor -{ - return l.productName == r.productName && l.productLang == r.productLang; -} - -nonterminal ProductIdentifier with json, productName, productLang, productPath; - -abstract production productIdentifier -top::ProductIdentifier ::= name::String language::String path::String -{ - top.productName = name; - top.productLang = language; - top.productPath = path; - top.json = jsonObject( - [ pair("name", jsonString(top.productName)) - , pair("language", jsonString(top.productLang)) - , pair("path", jsonString(top.productPath)) - ]); -} - -nonterminal MetaItem with json; - -abstract production metaItem -top::MetaItem ::= service::String metaType::String value::String -{ - top.json = jsonObject( - [ pair("service", jsonString(service)) - , pair("type", jsonString(metaType)) - , pair("value", jsonString(value)) - ]); -} - -abstract production metaDegraded -top::MetaItem ::= service::String value::String -{ - forwards to metaItem(service, "degraded", value); -} diff --git a/grammars/silver/support/monto/products/Source.sv b/grammars/silver/support/monto/products/Source.sv deleted file mode 100644 index 1ebd3eb64..000000000 --- a/grammars/silver/support/monto/products/Source.sv +++ /dev/null @@ -1,9 +0,0 @@ -grammar silver:support:monto:products; - -import silver:json; - -abstract production sourceProduct -top::ProductValue ::= source::String -{ - forwards to productValue("source", jsonString(source)); -} diff --git a/grammars/silver/support/monto/utils/GetProduct.sv b/grammars/silver/support/monto/utils/GetProduct.sv deleted file mode 100644 index 09846a6c5..000000000 --- a/grammars/silver/support/monto/utils/GetProduct.sv +++ /dev/null @@ -1,15 +0,0 @@ -grammar silver:support:monto:utils; - -import silver:support:monto; -import silver:support:monto:products; - -function getProduct -Maybe ::= ident::ProductIdentifier deps::[Product] -{ - return if null(deps) then - nothing() - else if identEq(ident, head(deps)) then - just(head(deps)) - else - getProduct(ident, tail(deps)); -} diff --git a/grammars/silver/support/monto/utils/IdentEq.sv b/grammars/silver/support/monto/utils/IdentEq.sv deleted file mode 100644 index 1096e4c02..000000000 --- a/grammars/silver/support/monto/utils/IdentEq.sv +++ /dev/null @@ -1,9 +0,0 @@ -grammar silver:support:monto:utils; - -import silver:support:monto:products; - -function identEq -Boolean ::= ident::ProductIdentifier prod::Product -{ - return prod.productName == ident.productName && prod.productLang == ident.productLang && prod.productPath == ident.productPath; -} diff --git a/grammars/silver/support/monto/utils/SimpleService.sv b/grammars/silver/support/monto/utils/SimpleService.sv deleted file mode 100644 index 89ef23205..000000000 --- a/grammars/silver/support/monto/utils/SimpleService.sv +++ /dev/null @@ -1,15 +0,0 @@ -grammar silver:support:monto:utils; - -import silver:support:monto; -import silver:support:monto:negotiation; - -function simpleService -Service ::= version::SoftwareVersion providers::[ServiceProvider] -{ - local sn :: ServiceNegotiation = serviceNegotiation( - protocolVersion(3, 0, 0), - version, - [], - map((.descriptor), providers)); - return service(sn, providers); -} diff --git a/grammars/silver/testing/DocConfig.sv b/grammars/silver/testing/DocConfig.sv deleted file mode 100644 index dad83c2b9..000000000 --- a/grammars/silver/testing/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:testing; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Testing\nmenu_title: Testing\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/testing/TestSuite.sv b/grammars/silver/testing/TestSuite.sv index c499decce..abd2f742c 100644 --- a/grammars/silver/testing/TestSuite.sv +++ b/grammars/silver/testing/TestSuite.sv @@ -1,5 +1,9 @@ grammar silver:testing ; +imports silver:reflect; +imports silver:langutil; +imports silver:langutil:pp; + nonterminal Test with msg, pass, ioIn, ioOut ; @@ -12,8 +16,8 @@ synthesized attribute numTests :: Integer ; synthesized attribute numPassed :: Integer ; synthesized attribute numFailed :: Integer ; -synthesized attribute ioOut :: IO ; -inherited attribute ioIn :: IO ; +synthesized attribute ioOut :: IOToken ; +inherited attribute ioIn :: IOToken ; abstract production defTest t::Test ::= @@ -108,4 +112,17 @@ Boolean ::= f::(Boolean ::=) times::Integer f() && repeatTestTimes(f, times - 1); } +-- TODO: Consider replacing this with the regular Show type class, +-- would require some way of propagating the pp attribute. +class ShowTestValue a { + showTestValue :: (String ::= a); +} + +instance ShowTestValue a { + showTestValue = \ x::a -> show(80, reflect(x)); +} + +instance ShowTestValue a => ShowTestValue Decorated a with i { + showTestValue = \ x::Decorated a with i -> showTestValue(new(x)); +} diff --git a/grammars/silver/testing/bin/Main.sv b/grammars/silver/testing/bin/Main.sv index 5e1e6625a..3c081d9ce 100644 --- a/grammars/silver/testing/bin/Main.sv +++ b/grammars/silver/testing/bin/Main.sv @@ -3,13 +3,13 @@ grammar silver:testing:bin ; import silver:testing; function main -IOVal ::= args::[String] ioIn::IO +IOVal ::= args::[String] ioIn::IOToken { return -- if true then printDirs(initDirs, ioIn) else -- uncomment above line to just experiment with the traverse function -- when used for printing directories. ioval( - print( "============================================================\n" ++ + printT( "============================================================\n" ++ (if runTests.iovalue.numFailed == 0 then "All tests passed. \n" else toString(runTests.iovalue.numFailed) ++ @@ -24,7 +24,7 @@ IOVal ::= args::[String] ioIn::IO ( startDir.iovalue, initDirs, runTest, dirSkip, ioval(startDir.io, testingResults(0) ) ); - local startDir :: IOVal = cwd(ioIn) ; + local startDir :: IOVal = cwdT(ioIn) ; local attribute initDirs :: [ String ] ; initDirs = map(cleanDirName, args) ; -- was explode(" ",args)) ; diff --git a/grammars/silver/testing/bin/ParsingTests.sv b/grammars/silver/testing/bin/ParsingTests.sv index c2640cdf7..c09812c78 100644 --- a/grammars/silver/testing/bin/ParsingTests.sv +++ b/grammars/silver/testing/bin/ParsingTests.sv @@ -7,14 +7,14 @@ import silver:testing; abstract production parseOnlyTestAfterCPP t::Test ::= fn::String parseF::(ParseResult ::= String String) { - local exists::IOVal = isFile(fn, t.ioIn); + local exists::IOVal = isFileT(fn, t.ioIn); local cppCommand :: String = -- "cpp -P " ++ fn ++ " | tail -n +3 > " ++ fn ++ ".cpp" ; "cpp " ++ fn ++ " > " ++ fn ++ ".cpp" ; -- even the -P option to cpp leaves 2 blanks lines, so we also -- use tail to remove these blank lines - local mkCPPfile::IOVal = system (cppCommand, exists.io ) ; - local text::IOVal = readFile(fn++".cpp", mkCPPfile.io); + local mkCPPfile::IOVal = systemT (cppCommand, exists.io ) ; + local text::IOVal = readFileT(fn++".cpp", mkCPPfile.io); local pr::ParseResult = parseF(text.iovalue,fn) ; t.pass = exists.iovalue && mkCPPfile.iovalue == 0 && pr.parseSuccess ; @@ -40,8 +40,8 @@ t::Test ::= fn::String parseF::(ParseResult ::= String String) abstract production parseOnlyTest t::Test ::= fn::String parseF::(ParseResult ::= String String) { - local exists::IOVal = isFile(fn, t.ioIn); - local text::IOVal = readFile(fn, exists.io); + local exists::IOVal = isFileT(fn, t.ioIn); + local text::IOVal = readFileT(fn, exists.io); local pr::ParseResult = parseF(text.iovalue,fn) ; local result :: Maybe @@ -69,8 +69,8 @@ t::Test ::= fn::String parseF::(ParseResult ::= String String) abstract production parseFailTest t::Test ::= fn::String parseF::(ParseResult ::= String String) { - local exists::IOVal = isFile(fn, t.ioIn); - local text::IOVal = readFile(fn, exists.io); + local exists::IOVal = isFileT(fn, t.ioIn); + local text::IOVal = readFileT(fn, exists.io); local pr::ParseResult = parseF(text.iovalue,fn) ; t.pass = exists.iovalue && ! pr.parseSuccess ; diff --git a/grammars/silver/testing/bin/PrintDirs.sv b/grammars/silver/testing/bin/PrintDirs.sv index d31a8d5f6..e68ca14ac 100644 --- a/grammars/silver/testing/bin/PrintDirs.sv +++ b/grammars/silver/testing/bin/PrintDirs.sv @@ -1,7 +1,7 @@ grammar silver:testing:bin; function printDirs -IO ::= paths::[String] ioIn::IO +IOToken ::= paths::[String] ioIn::IOToken { return traverseDirectoriesAndPerform ( "", paths, printNL, doNotSkip, ioval(ioIn,0) ).io ; @@ -9,11 +9,11 @@ IO ::= paths::[String] ioIn::IO function printNL IOVal ::= absoluteFilePath::String ioIn::IOVal -{ return ioval( print (absoluteFilePath ++ "\n", ioIn.io), ioIn.iovalue ) ; +{ return ioval( printT (absoluteFilePath ++ "\n", ioIn.io), ioIn.iovalue ) ; } function doNotSkip -IOVal ::= absoluteFilePath::String ioIn::IO +IOVal ::= absoluteFilePath::String ioIn::IOToken { return ioval(ioIn,false) ; } diff --git a/grammars/silver/testing/bin/RunTest.sv b/grammars/silver/testing/bin/RunTest.sv index 28ef2fce6..d25b4b767 100644 --- a/grammars/silver/testing/bin/RunTest.sv +++ b/grammars/silver/testing/bin/RunTest.sv @@ -11,18 +11,18 @@ tr::TestingResults ::= nf::Integer function runTest IOVal ::= absoluteFilePath::String ioIn::IOVal { - local isDir :: IOVal = isDirectory( absoluteFilePath, ioIn.io ); - local isF :: IOVal = isFile(absoluteFilePath, ioIn.io); - local skip :: IOVal = isFile(dirNameInFilePath(absoluteFilePath) ++ + local isDir :: IOVal = isDirectoryT( absoluteFilePath, ioIn.io ); + local isF :: IOVal = isFileT(absoluteFilePath, ioIn.io); + local skip :: IOVal = isFileT(dirNameInFilePath(absoluteFilePath) ++ "/tests.skip", ioIn.io); - local text :: IOVal = readFile(absoluteFilePath, isF.io); + local text :: IOVal = readFileT(absoluteFilePath, isF.io); local parseResult :: ParseResult = parse(text.iovalue, absoluteFilePath); local r_cst :: Run = parseResult.parseTree ; local parseFailure :: IOVal = ioval ( - print ("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" ++ + printT ("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" ++ "Parsing of .test file \n " ++ absoluteFilePath ++ "\n" ++ "failed.\n" ++ parseResult.parseErrors ++ "\n" ++ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" , @@ -47,7 +47,7 @@ IOVal ::= absoluteFilePath::String ioIn::IOVal then ioIn -- nothing to do else if ! isF.iovalue - then ioval (exit ( 4, print("\n\nFile \"" ++ absoluteFilePath ++ + then ioval (exitT ( 4, printT("\n\nFile \"" ++ absoluteFilePath ++ "\" not found.\n", isF.io ) ) , testingResults(999) ) else @@ -69,7 +69,7 @@ String ::= dn::String function dirSkip -IOVal ::= absoluteFilePath::String ioIn::IO +IOVal ::= absoluteFilePath::String ioIn::IOToken { - return isFile ( absoluteFilePath ++ "/tests.skip", ioIn ) ; + return isFileT ( absoluteFilePath ++ "/tests.skip", ioIn ) ; } diff --git a/grammars/silver/testing/bin/TestSpecLang.sv b/grammars/silver/testing/bin/TestSpecLang.sv index 974f1dc39..423c8fb37 100644 --- a/grammars/silver/testing/bin/TestSpecLang.sv +++ b/grammars/silver/testing/bin/TestSpecLang.sv @@ -55,15 +55,15 @@ r::Run ::= f::OptionalFail run_kwd::'run' ':' rest::CommandAlt_t concrete production run r::Run ::= f::OptionalFail 'run' c::Command_t { - local msgBefore :: IO = - print ("............................................................\n" ++ + local msgBefore :: IOToken = + printT ("............................................................\n" ++ "Test \n " ++ r.testFileName ++ " in directory \n " ++ prettyDirName(r.testFileDir) ++ "\n", r.ioInput.io ) ; local cmd :: String = substring(1,length(c.lexeme)-1,c.lexeme) ; local cmdResult :: IOVal - = system ("cd " ++ r.testFileDir ++ ";" ++ + = systemT ("cd " ++ r.testFileDir ++ ";" ++ "rm -f " ++ r.testFileName ++ ".output ; " ++ cmd ++ " >& " ++ r.testFileName ++ ".output" , msgBefore ) ; @@ -71,8 +71,8 @@ r::Run ::= f::OptionalFail 'run' c::Command_t r.ioResult = if (cmdResult.iovalue == 0 && ! f.fail) || (cmdResult.iovalue != 0 && f.fail) - then ioval( print( "passed (rc = 0).\n", cmdResult.io), 0 ) - else ioval( print( "failed (rc = " ++ toString(cmdResult.iovalue) ++ ").\n", + then ioval( printT( "passed (rc = 0).\n", cmdResult.io), 0 ) + else ioval( printT( "failed (rc = " ++ toString(cmdResult.iovalue) ++ ").\n", cmdResult.io), 1 ) ; } @@ -80,8 +80,8 @@ r::Run ::= f::OptionalFail 'run' c::Command_t concrete production runTestSuite ts::Run ::= 'test' 'suite' jar::Jar_t { - local msgBefore :: IO = - print ("............................................................\n" ++ + local msgBefore :: IOToken = + printT ("............................................................\n" ++ "Test Suite jar \"" ++ jar.lexeme ++ "\" in \n " ++ ts.testFileName ++ " in directory \n " ++ prettyDirName(ts.testFileDir) ++ "\n", ts.ioInput.io ) ; @@ -89,14 +89,14 @@ ts::Run ::= 'test' 'suite' jar::Jar_t -- probably should check that jar file by this name exists local testSuiteResults :: IOVal - = system ("cd " ++ ts.testFileDir ++ ";" ++ + = systemT ("cd " ++ ts.testFileDir ++ ";" ++ "rm -f " ++ ts.testFileName ++ ".output ; " ++ - " java -jar " ++ jar.lexeme ++ + " java -Xss6M -jar " ++ jar.lexeme ++ " >& " ++ ts.testFileName ++ ".output" , msgBefore ) ; - local afterMsg :: IO - = print ( if testSuiteResults.iovalue == 0 + local afterMsg :: IOToken + = printT ( if testSuiteResults.iovalue == 0 then "all tests passed (rc = 0).\n" else toString(testSuiteResults.iovalue) ++ if testSuiteResults.iovalue == 1 @@ -113,24 +113,24 @@ ts::Run ::= 'test' 'suite' jar::Jar_t {- function runCommandOnFile -IO ::= absoluteFilePath::String ioIn::IO +IOToken ::= absoluteFilePath::String ioIn::IOToken { return runCommandOnFileRC(absoluteFilePath, ioval(ioIn,0) ) .io ; } function runCommandOnFile -IO ::= absoluteFilePath::String ioIn::IO +IOToken ::= absoluteFilePath::String ioIn::IOToken { - local isDir :: IOVal = isDirectory( absoluteFilePath, ioIn ); - local isF :: IOVal = isFile(absoluteFilePath, ioIn); - local text :: IOVal = readFile(absoluteFilePath, isF.io); + local isDir :: IOVal = isDirectoryT( absoluteFilePath, ioIn ); + local isF :: IOVal = isFileT(absoluteFilePath, ioIn); + local text :: IOVal = readFileT(absoluteFilePath, isF.io); local parseResult :: ParseResult = parse(text.iovalue, absoluteFilePath); local r_cst :: Run = parseResult.parseTree ; - local parseFailure :: IO = - print ("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" ++ + local parseFailure :: IOToken = + printT ("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" ++ "Parsing of .test file \n " ++ absoluteFilePath ++ "\n" ++ "failed.\n" ++ parseResult.parseErrors ++ "\n" ++ "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n" , @@ -143,9 +143,9 @@ IO ::= absoluteFilePath::String ioIn::IO local testResult :: IOVal = r_cst.ioResult ; - local attribute msgAfter :: IO ; + local attribute msgAfter :: IOToken ; msgAfter = - print ((if testResult.iovalue == 0 + printT ((if testResult.iovalue == 0 then "passed (rc == 0)." else "failed (rc == " ++ toString(testResult.iovalue) ++ ").") ++ "\n" , diff --git a/grammars/silver/testing/bin/Traverse.sv b/grammars/silver/testing/bin/Traverse.sv index 4517a1eea..db2a9e16e 100644 --- a/grammars/silver/testing/bin/Traverse.sv +++ b/grammars/silver/testing/bin/Traverse.sv @@ -3,7 +3,7 @@ grammar silver:testing:bin ; function traverseDirectoriesAndPerform IOVal ::= startDir::String paths::[String] f::(IOVal ::= String IOVal) - skipDir::(IOVal ::= String IO) + skipDir::(IOVal ::= String IOToken) ioIn::IOVal { return @@ -17,8 +17,7 @@ IOVal ::= startDir::String paths::[String] = if ! headIsDir.iovalue || skipIt.iovalue then ioval ( headIsDir.io, [ ] ) else ioval ( dirContents.io, -- add sorted list of dir contents to list - sortBy ( stringLte, - prependAll ( head(paths), dirContents.iovalue ) ) ) ; + sort ( prependAll ( head(paths), dirContents.iovalue ) ) ) ; {- Thought links were a bug, they seem not to be. It maybe the size of the Java stack that was the problem. @@ -30,7 +29,7 @@ IOVal ::= startDir::String paths::[String] -} local headIsLink::IOVal = ioval(ioIn.io, false) ; -- maybe add later. - local headIsDir::IOVal = isDirectory( head(paths), headIsLink.io ); + local headIsDir::IOVal = isDirectoryT( head(paths), headIsLink.io ); local skipIt::IOVal = if endsWith("/generated",head(paths)) @@ -43,7 +42,7 @@ IOVal ::= startDir::String paths::[String] then skipDir(head(paths), headIsDir.io) else ioval(headIsDir.io, false) ; - local dirContents::IOVal< [String] > = listContents ( head(paths), + local dirContents::IOVal< [String] > = listContentsT ( head(paths), headIsDir.io ) ; } diff --git a/grammars/silver/testing/bin/silver-compile b/grammars/silver/testing/bin/silver-compile index 26aa83d51..a9391cc53 100755 --- a/grammars/silver/testing/bin/silver-compile +++ b/grammars/silver/testing/bin/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # This script will use Silver to build an AG evaluator for the # tutorials grammar and run ant to compile the resulting Java code. @@ -9,7 +9,7 @@ ##export GRAMMAR_PATH="../../../../../../grammars" export SILVER_HOME="../../../.." -java -Xss8M -Xmx1000M -jar $SILVER_HOME/jars/silver.composed.Default.jar \ +java -Xss8M -Xmx1000M -jar $SILVER_HOME/jars/silver.compiler.composed.Default.jar \ $@ \ silver:testing:bin \ && ant diff --git a/grammars/silver/translation/java/Project.sv b/grammars/silver/translation/java/Project.sv deleted file mode 100644 index 0be19b3ba..000000000 --- a/grammars/silver/translation/java/Project.sv +++ /dev/null @@ -1,7 +0,0 @@ -grammar silver:translation:java; - -exports silver:translation:java:core; -exports silver:translation:java:type; - -exports silver:translation:java:driver; - diff --git a/grammars/silver/translation/java/core/Annotation.sv b/grammars/silver/translation/java/core/Annotation.sv deleted file mode 100644 index 8beb116a3..000000000 --- a/grammars/silver/translation/java/core/Annotation.sv +++ /dev/null @@ -1,23 +0,0 @@ -grammar silver:translation:java:core; - -aspect production annotationDcl -top::AGDcl ::= 'annotation' a::QName tl::BracketedOptTypeExprs '::' te::TypeExpr ';' -{ - local className :: String = "A" ++ a.name; - --- We report the trans type, despite the fact that the attribute may be parameterized! --- It should be fine, though. If we're a tv, then it's 'Object' and anything --- else will be a subtype... - - top.genFiles := [pair(className ++ ".java", s""" - -package ${makeName(top.grammarName)}; - -public interface ${className} { - - public ${te.typerep.transType} getAnno_${makeIdName(fName)}(); - -} -""")]; -} - diff --git a/grammars/silver/translation/java/core/AspectDcl.sv b/grammars/silver/translation/java/core/AspectDcl.sv deleted file mode 100644 index c4ca51abf..000000000 --- a/grammars/silver/translation/java/core/AspectDcl.sv +++ /dev/null @@ -1,17 +0,0 @@ -grammar silver:translation:java:core; - -aspect production aspectProductionDcl -top::AGDcl ::= 'aspect' 'production' id::QName ns::AspectProductionSignature body::ProductionBody -{ - top.setupInh := body.setupInh; - top.initProd := s"\t\t//ASPECT PRODUCTION ${id.name} ${ns.unparse}\n${body.translation}"; - top.valueWeaving := body.valueWeaving; -} - -aspect production aspectFunctionDcl -top::AGDcl ::= 'aspect' 'function' id::QName ns::AspectFunctionSignature body::ProductionBody -{ - top.setupInh := body.setupInh; - top.initProd := s"\t\t//ASPECT FUNCTION ${id.name} ${ns.unparse}\n${body.translation}"; - top.valueWeaving := body.valueWeaving; -} diff --git a/grammars/silver/translation/java/core/BlockContext.sv b/grammars/silver/translation/java/core/BlockContext.sv deleted file mode 100644 index ff547c385..000000000 --- a/grammars/silver/translation/java/core/BlockContext.sv +++ /dev/null @@ -1,49 +0,0 @@ -grammar silver:translation:java:core; - -attribute className, prodLocalCountName occurs on BlockContext; - -{-- - - The name of the class for the current context. - - e.g. "silver.definition.core.PbaseExpr" - - - - Valid to access only within productions/functions. - - (used by equations. exprs, but only in flowDefs, which shouldn't be accessed outside productions) - - (Also used by production action blocks to access children) - -} -synthesized attribute className :: String; -{-- - - The name to access a production's local count. - - e.g. "silver.definition.core.Init.count_local__ON__silver_definition_core_PbaseExpr" - - - - Also valid only in prod/func. Used only by local declarations. - -} -synthesized attribute prodLocalCountName :: String; - -aspect default production -top::BlockContext ::= -{ - top.className = error("context does not have a className"); - top.prodLocalCountName = error("prodLocalCountName in context without locals"); -} - -aspect production functionContext -top::BlockContext ::= sig::NamedSignature _ -{ - top.className = makeClassName(top.fullName); - top.prodLocalCountName = - makeName(top.sourceGrammar) ++ ".Init.count_local__ON__" ++ makeIdName(top.fullName); -} - -aspect production productionContext -top::BlockContext ::= sig::NamedSignature _ -{ - top.className = makeClassName(top.fullName); - top.prodLocalCountName = - makeName(top.sourceGrammar) ++ ".Init.count_local__ON__" ++ makeIdName(top.fullName); -} - -aspect production globalExprContext -top::BlockContext ::= _ -{ -} - diff --git a/grammars/silver/translation/java/core/BuiltInFunctions.sv b/grammars/silver/translation/java/core/BuiltInFunctions.sv deleted file mode 100644 index cae83aee9..000000000 --- a/grammars/silver/translation/java/core/BuiltInFunctions.sv +++ /dev/null @@ -1,119 +0,0 @@ -grammar silver:translation:java:core; - -aspect production errorLength -top::Expr ::= e::Decorated Expr -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production stringLength -top::Expr ::= e::Decorated Expr -{ - top.translation = s"Integer.valueOf(((common.StringCatter)${e.translation}).length())"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production toBooleanFunction -top::Expr ::= 'toBoolean' '(' e::Expr ')' -{ - top.translation = case finalType(e) of - | boolType() -> e.translation - | intType() -> s"Boolean.valueOf(${e.translation} != 0)" - | floatType() -> s"Boolean.valueOf(${e.translation} != 0.0)" - | stringType() -> s"Boolean.valueOf(${e.translation}.toString())" - | t -> error("INTERNAL ERROR: no toBoolean translation for type " ++ prettyType(t)) - end; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production toIntegerFunction -top::Expr ::= 'toInteger' '(' e::Expr ')' -{ - top.translation = case finalType(e) of - | boolType() -> s"Integer.valueOf(${e.translation}? 1 : 0)" - | intType() -> e.translation - | floatType() -> s"Integer.valueOf(((Float)${e.translation}).intValue())" - | stringType() -> s"Integer.valueOf(${e.translation}.toString())" - | t -> error("INTERNAL ERROR: no toInteger translation for type " ++ prettyType(t)) - end; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production toFloatFunction -top::Expr ::= 'toFloat' '(' e::Expr ')' -{ - top.translation = case finalType(e) of - | boolType() -> s"Float.valueOf(${e.translation}? 1.0f : 0.0f)" - | intType() -> s"Float.valueOf(((Integer)${e.translation}).floatValue())" - | floatType() -> e.translation - | stringType() -> s"Float.valueOf(${e.translation}.toString())" - | t -> error("INTERNAL ERROR: no toFloat translation for type " ++ prettyType(t)) - end; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production toStringFunction -top::Expr ::= 'toString' '(' e::Expr ')' -{ - top.translation = s"new common.StringCatter(String.valueOf(${e.translation}))"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production reifyFunctionLiteral -top::Expr ::= 'reify' -{ - local resultType::Type = - case finalType(top).outputType of - | nonterminalType("core:Either", [stringType(), a]) -> a - | _ -> error("Unexpected final type for reify!") - end; - - -- In the unusual case that we have skolems in the result type, we can't generalize them, but - -- we also can't do any better, so leave the runtime result TypeRep unfreshened. - -- There is a similar problem with lambdas. - top.translation = -s"""(new common.NodeFactory() { - @Override - public final core.NEither invoke(final Object[] args, final Object[] namedArgs) { - assert args != null && args.length == 1; - assert namedArgs == null || namedArgs.length == 0; - -${makeTyVarDecls(5, resultType.freeVariables)} - common.TypeRep resultType = ${resultType.transTypeRep}; - - return common.Reflection.reifyChecked(resultType, (core.reflect.NAST)common.Util.demand(args[0])); - } - - @Override - public final common.FunctionTypeRep getType() { -${makeTyVarDecls(5, finalType(top).freeVariables)} - return ${finalType(top).transTypeRep}; - } - - @Override - public final String toString() { - return "reify"; - } - })"""; - - top.lazyTranslation = top.translation; -} - -aspect production newFunction -top::Expr ::= 'new' '(' e::Expr ')' -{ - top.translation = s"((${finalType(top).transType})${e.translation}.undecorate())"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production terminalConstructor -top::Expr ::= 'terminal' '(' t::TypeExpr ',' es::Expr ',' el::Expr ')' -{ - top.translation = s"new ${makeTerminalName(t.typerep.typeName)}(${es.translation}, (core.NLocation)${el.translation})"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} diff --git a/grammars/silver/translation/java/core/DclInfo.sv b/grammars/silver/translation/java/core/DclInfo.sv deleted file mode 100644 index 7c4576f00..000000000 --- a/grammars/silver/translation/java/core/DclInfo.sv +++ /dev/null @@ -1,50 +0,0 @@ -grammar silver:translation:java:core; - -import silver:util; - -attribute attrOccursIndexName, attrOccursIndex occurs on DclInfo; - -{-- - - The name of the occurs variable. e.g. silver_def_core_pp__ON__silver_def_core_Expr - -} -synthesized attribute attrOccursIndexName :: String; -{-- - - Index of the attribute. e.g. silver.def.core.silver_def_core_pp__ON__silver_def_core_Expr - -} -synthesized attribute attrOccursIndex :: String; - -aspect default production -top::DclInfo ::= -{ - -- See TODO in the env DclInfo - top.attrOccursIndexName = error("Internal compiler error: must be defined for all occurs declarations"); - top.attrOccursIndex = error("Internal compiler error: must be defined for all occurs declarations"); -} - - -aspect production occursDcl -top::DclInfo ::= sg::String sl::Location fnnt::String fnat::String ntty::Type atty::Type -{ - top.attrOccursIndexName = makeIdName(fnat ++ "__ON__" ++ fnnt); - top.attrOccursIndex = makeName(sg) ++ ".Init." ++ top.attrOccursIndexName; -} -aspect production annoInstanceDcl -top::DclInfo ::= sg::String sl::Location fnnt::String fnat::String ntty::Type atty::Type -{ - top.attrOccursIndexName = error("Not actually an attribute"); - top.attrOccursIndex = error("Not actually an attribute"); -} - - -aspect production localDcl -top::DclInfo ::= sg::String sl::Location fn::String ty::Type -{ - -- TODO: BUG: See https://github.com/melt-umn/silver/issues/52 - -- This is the kind of nasty hack that we might fix with a FullName type, instead of hacking on 'fn' - -- Also, this choice of name is actually buggy! Can cause java compiler errors with name collisions. - local attribute li :: Integer; - li = lastIndexOf(":local:", fn); - top.attrOccursIndexName = makeIdName(substring(li+7, length(fn), fn) ++ "__ON__" ++ substring(0,li,fn)); - top.attrOccursIndex = makeName(sg) ++ ".Init." ++ top.attrOccursIndexName; -} - diff --git a/grammars/silver/translation/java/core/Expr.sv b/grammars/silver/translation/java/core/Expr.sv deleted file mode 100644 index 9059a5776..000000000 --- a/grammars/silver/translation/java/core/Expr.sv +++ /dev/null @@ -1,601 +0,0 @@ -grammar silver:translation:java:core; - -import silver:util; - -import silver:analysis:typechecking:core only finalSubst; - -function finalType -Type ::= e::Decorated Expr -{ - return performSubstitution(e.typerep, e.finalSubst); -} - -{-- - - A translation string that will be a thunk instead of the raw value. - - BUT, is permitted to be a raw value IF it's totally safe to do so. - -} -synthesized attribute lazyTranslation :: String; - -attribute lazyTranslation, translation occurs on Expr; -attribute lazyTranslation occurs on Exprs; - --- `translation` should yield an expression of the appropriate Java type. --- e.g. `NodeFactory` for a (String ::= ...) --- At the moment, this requires a lot of casts. Oh well. - --- `lazyTranslation` can yield any type, since it's only ever immediately used --- to put values in a `new Object[]{...}` - -aspect production errorExpr -top::Expr ::= msg::[Message] -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production errorReference -top::Expr ::= msg::[Message] q::Decorated QName -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production childReference -top::Expr ::= q::Decorated QName -{ - local childIDref :: String = - top.frame.className ++ ".i_" ++ q.lookupValue.fullName; - - top.translation = - if q.lookupValue.typerep.isDecorable - then if finalType(top).isDecorable - then s"((${finalType(top).transType})context.childDecorated(${childIDref}).undecorate())" - else s"((${finalType(top).transType})context.childDecorated(${childIDref}))" - else s"((${finalType(top).transType})context.childAsIs(${childIDref}))"; - -- the reason we do .childDecorated().undecorate() is that it's not safe to mix as-is/decorated accesses to the same child. - -- this is a potential source of minor inefficiency for functions that do not decorate. - - top.lazyTranslation = - if !top.frame.lazyApplication then top.translation else - if q.lookupValue.typerep.isDecorable - then if finalType(top).isDecorable - then s"common.Thunk.transformUndecorate(context.childDecoratedLazy(${childIDref}))" - else s"context.childDecoratedLazy(${childIDref})" - else s"context.childAsIsLazy(${childIDref})"; -} - -aspect production localReference -top::Expr ::= q::Decorated QName -{ - top.translation = - if q.lookupValue.typerep.isDecorable - then if finalType(top).isDecorable - then s"((${finalType(top).transType})context.localDecorated(${q.lookupValue.dcl.attrOccursIndex}).undecorate())" - else s"((${finalType(top).transType})context.localDecorated(${q.lookupValue.dcl.attrOccursIndex}))" - else s"((${finalType(top).transType})context.localAsIs(${q.lookupValue.dcl.attrOccursIndex}))"; - -- reminder: look at comments for childReference - - top.lazyTranslation = - if !top.frame.lazyApplication then top.translation else - if q.lookupValue.typerep.isDecorable - then if finalType(top).isDecorable - then s"common.Thunk.transformUndecorate(context.localDecoratedLazy(${q.lookupValue.dcl.attrOccursIndex}))" - else s"context.localDecoratedLazy(${q.lookupValue.dcl.attrOccursIndex})" - else s"context.localAsIsLazy(${q.lookupValue.dcl.attrOccursIndex})"; -} - -aspect production lhsReference -top::Expr ::= q::Decorated QName -{ - top.translation = - if finalType(top).isDecorable - then s"((${finalType(top).transType})context.undecorate())" - else "context"; - - top.lazyTranslation = top.translation; -} - -aspect production forwardReference -top::Expr ::= q::Decorated QName -{ - top.translation = - if finalType(top).isDecorable - then s"((${finalType(top).transType})context.forward().undecorate())" - else "context.forward()"; - - -- this might evaluate the forward equation, so suspend it as a thunk - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production productionReference -top::Expr ::= q::Decorated QName -{ - top.translation = makeClassName(q.lookupValue.fullName) ++ ".factory"; - top.lazyTranslation = top.translation; -} - -aspect production functionReference -top::Expr ::= q::Decorated QName -{ - -- functions, unlike productions, can return a type variable. - -- as such, we have to cast it to the real inferred final type. - top.translation = s"((${finalType(top).transType})${top.lazyTranslation})"; - top.lazyTranslation = makeClassName(q.lookupValue.fullName) ++ ".factory"; -} - -aspect production globalValueReference -top::Expr ::= q::Decorated QName -{ - local directThunk :: String = - s"${makeName(q.lookupValue.dcl.sourceGrammar)}.Init.${fullNameToShort(q.lookupValue.fullName)}"; - - top.translation = s"((${finalType(top).transType})${directThunk}.eval())"; - top.lazyTranslation = - if top.frame.lazyApplication - then directThunk - else top.translation; -} - -aspect production errorApplication -top::Expr ::= e::Decorated Expr es::AppExprs annos::AnnoAppExprs -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production functionInvocation -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ - top.translation = - case e of - | functionReference(q) -> -- static method invocation - s"((${finalType(top).transType})${makeClassName(q.lookupValue.fullName)}.invoke(${argsTranslation(es)}))" - | productionReference(q) -> -- static constructor invocation - s"((${finalType(top).transType})new ${makeClassName(q.lookupValue.fullName)}(${implode(", ", map((.lazyTranslation), es.exprs ++ reorderedAnnoAppExprs(annos)))}))" - | _ -> -- dynamic method invocation - s"((${finalType(top).transType})${e.translation}.invoke(new Object[]{${argsTranslation(es)}}, ${namedargsTranslation(annos)}))" - end ; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -function argsTranslation -String ::= e::Decorated AppExprs -{ - -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. - return implode(", ", map((.lazyTranslation), e.exprs)); -} -function namedargsTranslation -String ::= e::Decorated AnnoAppExprs -{ - -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. - return if null(e.exprs) then "null" - else s"new Object[]{${implode(", ", map((.lazyTranslation), reorderedAnnoAppExprs(e)))}}"; -} -function namedargsTranslationNOReorder -String ::= e::Decorated AnnoAppExprs -{ - -- TODO: This is the ONLY use of .exprs We could eliminate that, if we fix this. - return if null(e.exprs) then "null" - else s"new Object[]{${implode(", ", map((.lazyTranslation), e.exprs))}}"; -} - -function int2str String ::= i::Integer { return toString(i); } - -aspect production partialApplication -top::Expr ::= e::Decorated Expr es::Decorated AppExprs annos::Decorated AnnoAppExprs -{ - local step1 :: String = e.translation; - -- Note: we check for nullity of the index lists instead of use - -- isPartial here... Because we may supply ALL values (thus, NOT isPartial!) - -- of one of the param lists, but that means we still need to apply it!! - local step2 :: String = - if !null(es.appExprIndicies) then - step1 ++ ".invokePartial(" ++ - s"new int[]{${implode(", ", map(int2str, es.appExprIndicies))}}, " ++ - s"new Object[]{${argsTranslation(es)}})" - else step1; - local step3 :: String = - if !null(annos.annoIndexConverted) || !null(annos.annoIndexSupplied) then - step2 ++ ".invokeNamedPartial(" ++ - (if null(annos.annoIndexConverted) then "null" - else s"new int[]{${implode(", ", map(int2str, annos.annoIndexConverted))}}") ++ ", " ++ - (if null(annos.annoIndexSupplied) then "null" - else s"new int[]{${implode(", ", map(int2str, annos.annoIndexSupplied))}}") ++ ", " ++ - namedargsTranslationNOReorder(annos) ++ ")" - else step2; - - -- The theory is the `e.translation` we started with has the right type, so we don't need a cast here. In theory. - top.translation = step3; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production attributeSection -top::Expr ::= '(' '.' q::QName ')' -{ - local outTy :: String = finalType(top).outputType.transType; - - top.translation = - if inputType.isDecorated then - s"new common.AttributeSection<${outTy}>(${occursCheck.dcl.attrOccursIndex})" - else - -- Please note: context is not actually required here, we do so to make runtime error messages - -- more comprehensible. This is a similar situation to the code for 'decorate E with {}'. - -- Rather pin more memory than necessary than make errors bad. For now. - -- TODO: This is a good candidate for removing if we make the well-definedness error check required, though! - -- That error would be more comprehensible! (the trouble with this is that we're reporting as context the - -- function/production we appear within here. The function *may* be applied elsewhere. However, the most common - -- case is something like map((.attr), list) so, that's probably best to report here instead of within map.) - s"new common.AttributeSection.Undecorated<${outTy}>(${occursCheck.dcl.attrOccursIndex}, context)"; - - top.lazyTranslation = top.translation; -} - -aspect production errorAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production errorDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production forwardAccess -top::Expr ::= e::Expr '.' 'forward' -{ - top.translation = s"((${finalType(top).transType})${e.translation}.forwardOrThis())"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production synDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.translation = s"((${finalType(top).transType})${e.translation}.synthesized(${q.dcl.attrOccursIndex}))"; - - top.lazyTranslation = - case e, top.frame.lazyApplication of - | childReference(cqn), true -> - if cqn.lookupValue.typerep.isDecorable - then - s"context.childDecoratedSynthesizedLazy(${top.frame.className}.i_${cqn.lookupValue.fullName}, ${q.dcl.attrOccursIndex})" - else - s"context.childAsIsSynthesizedLazy(${top.frame.className}.i_${cqn.lookupValue.fullName}, ${q.dcl.attrOccursIndex})" - | lhsReference(_), true -> - s"context.contextSynthesizedLazy(${q.dcl.attrOccursIndex})" - | _, _ -> wrapThunk(top.translation, top.frame.lazyApplication) - end; -} - -aspect production inhDecoratedAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - top.translation = s"((${finalType(top).transType})${e.translation}.inherited(${q.dcl.attrOccursIndex}))"; - - top.lazyTranslation = - case e, top.frame.lazyApplication of - | lhsReference(_), true -> s"context.contextInheritedLazy(${q.dcl.attrOccursIndex})" - | _, _ -> wrapThunk(top.translation, top.frame.lazyApplication) - end; -} - -aspect production terminalAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - local accessor :: String = - if q.name == "lexeme" || q.name == "location" - then q.name - else if q.name == "line" - then "getLine()" - else if q.name == "column" - then "getColumn()" - else if q.name == "filename" - then "getFilename()" - else error("Not possible -- an error should have been raised about " ++ q.unparse); - - top.translation = s"((${finalType(top).transType})${e.translation}.${accessor})"; - - top.lazyTranslation = top.translation; -} - -aspect production annoAccessHandler -top::Expr ::= e::Decorated Expr q::Decorated QNameAttrOccur -{ - -- Note that the transType is specific to the nonterminal we're accessing from. - top.translation = s"((${finalType(top).transType})${e.translation}.getAnno_${makeIdName(q.attrDcl.fullName)}())"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - - -aspect production decorateExprWith -top::Expr ::= 'decorate' e::Expr 'with' '{' inh::ExprInhs '}' -{ - top.translation = e.translation ++ - case inh of - | exprInhsEmpty() -> ".decorate(context, (common.Lazy[])null)" - -- Note: we don't NEED to pass context here, but it's good for error messages! - -- When the user forgets to provide inherited attributes - -- (especially important because we're implicitly inserted when accessing attributes - -- from undecorated nodes, and this is a common error for new silverers.) - | _ -> ".decorate(context, common.Util.populateInh(" ++ - s"${makeNTClassName(finalType(e).typeName)}.num_inh_attrs, " ++ - s"new int[]{${implode(", ", inh.nameTrans)}}, " ++ - s"new common.Lazy[]{${implode(", ", inh.valueTrans)}}))" - end; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -synthesized attribute nameTrans :: [String]; -synthesized attribute valueTrans :: [String]; - -attribute nameTrans occurs on ExprInhs, ExprInh, ExprLHSExpr; -attribute valueTrans occurs on ExprInhs, ExprInh; - -aspect production exprInh -top::ExprInh ::= lhs::ExprLHSExpr '=' e::Expr ';' -{ - top.nameTrans = lhs.nameTrans; - top.valueTrans = [wrapLazy(e)]; -- TODO: this is another appearance of the nested lazy problem... -} - -aspect production exprInhsEmpty -top::ExprInhs ::= -{ - top.nameTrans = []; - top.valueTrans = []; -} - -aspect production exprInhsOne -top::ExprInhs ::= lhs::ExprInh -{ - top.nameTrans = lhs.nameTrans; - top.valueTrans = lhs.valueTrans; -} - -aspect production exprInhsCons -top::ExprInhs ::= lhs::ExprInh inh::ExprInhs -{ - top.nameTrans = lhs.nameTrans ++ inh.nameTrans; - top.valueTrans = lhs.valueTrans ++ inh.valueTrans; -} - - -aspect production exprLhsExpr -top::ExprLHSExpr ::= q::QNameAttrOccur -{ - top.nameTrans = [q.dcl.attrOccursIndex]; -} - - -aspect production trueConst -top::Expr ::='true' -{ - top.translation = "true"; - top.lazyTranslation = top.translation; -} - -aspect production falseConst -top::Expr ::= 'false' -{ - top.translation = "false"; - top.lazyTranslation = top.translation; -} - -aspect production and -top::Expr ::= e1::Expr '&&' e2::Expr -{ - top.translation = s"(${e1.translation} && ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production or -top::Expr ::= e1::Expr '||' e2::Expr -{ - top.translation = s"(${e1.translation} || ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production not -top::Expr ::= '!' e::Expr -{ - top.translation = s"(!${e.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - --- Some notes on numbers: --- Use `Integer.valueOf` (et al) instead of `new Integer`. It's more efficient. --- Let Java's autoboxing do the heavy lifting for us, why not? It's smarter. --- Primitive casts ensure `Integer == Integer` will be value-eq, not reference-eq -function comparisonTranslation -String ::= e1::Decorated Expr op::String e2::Decorated Expr -{ - return case finalType(e1) of - | intType() -> s"(${e1.translation} ${op} (int)${e2.translation})" - | floatType() -> s"(${e1.translation} ${op} (float)${e2.translation})" - | boolType() -> s"(${e1.translation} ${op} (boolean)${e2.translation})" - | stringType() -> s"(${e1.translation}.toString().compareTo(${e2.translation}.toString()) ${op} 0)" - | terminalIdType() -> s"(${e1.translation} ${op} (int)${e2.translation})" - | t -> error(s"INTERNAL ERROR: no ${op} trans for type ${prettyType(t)}") - end; -} - -aspect production gt -top::Expr ::= e1::Expr '>' e2::Expr -{ - top.translation = comparisonTranslation(e1, ">", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production lt -top::Expr ::= e1::Expr '<' e2::Expr -{ - top.translation = comparisonTranslation(e1, "<", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production gteq -top::Expr ::= e1::Expr '>=' e2::Expr -{ - top.translation = comparisonTranslation(e1, ">=", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production lteq -top::Expr ::= e1::Expr '<=' e2::Expr -{ - top.translation = comparisonTranslation(e1, "<=", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production eqeq -top::Expr ::= e1::Expr '==' e2::Expr -{ - top.translation = comparisonTranslation(e1, "==", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production neq -top::Expr ::= e1::Expr '!=' e2::Expr -{ - top.translation = comparisonTranslation(e1, "!=", e2); - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production ifThenElse -top::Expr ::= 'if' e1::Expr 'then' e2::Expr 'else' e3::Expr -{ - top.translation = s"(${e1.translation} ? ${e2.translation} : ${e3.translation})"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production intConst -top::Expr ::= i::Int_t -{ - top.translation = s"((int)${i.lexeme})"; - top.lazyTranslation = top.translation; -} - -aspect production floatConst -top::Expr ::= f::Float_t -{ - top.translation = s"((float)${f.lexeme})"; - top.lazyTranslation = top.translation; -} - -aspect production plus -top::Expr ::= e1::Expr '+' e2::Expr -{ - top.translation = s"(${e1.translation} + ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production minus -top::Expr ::= e1::Expr '-' e2::Expr -{ - top.translation = s"(${e1.translation} - ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production multiply -top::Expr ::= e1::Expr '*' e2::Expr -{ - top.translation = s"(${e1.translation} * ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production divide -top::Expr ::= e1::Expr '/' e2::Expr -{ - top.translation = s"(${e1.translation} / ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production modulus -top::Expr ::= e1::Expr '%' e2::Expr -{ - top.translation = s"(${e1.translation} % ${e2.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} -aspect production neg -top::Expr ::= '-' e::Expr -{ - top.translation = s"(-${e.translation})"; - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production stringConst -top::Expr ::= s::String_t -{ - top.translation = s"(new common.StringCatter(${s.lexeme}))"; - top.lazyTranslation = top.translation; -} - -aspect production errorPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); - top.lazyTranslation = top.translation; -} - -aspect production stringPlusPlus -top::Expr ::= e1::Decorated Expr e2::Decorated Expr -{ - top.translation = s"new common.StringCatter(${e1.translation}, ${e2.translation})"; - - top.lazyTranslation = wrapThunk(top.translation, top.frame.lazyApplication); -} - -aspect production exprsEmpty -top::Exprs ::= -{ - top.lazyTranslation = ""; -} - -aspect production exprsSingle -top::Exprs ::= e::Expr -{ - top.lazyTranslation = e.lazyTranslation; -} - -aspect production exprsCons -top::Exprs ::= e1::Expr ',' e2::Exprs -{ - top.lazyTranslation = e1.lazyTranslation ++ ", " ++ e2.lazyTranslation; -} - -aspect production exprRef -top::Expr ::= e::Decorated Expr -{ - top.translation = e.translation; - top.lazyTranslation = e.lazyTranslation; -} - - -function wrapThunk -String ::= exp::String beLazy::Boolean -{ - return if beLazy then wrapThunkText(exp, "Object") else exp; -} -function wrapThunkText -String ::= exp::String ty::String -{ - return s"new common.Thunk<${ty}>(new common.Thunk.Evaluable() { public final ${ty} eval() { return ${exp}; } })"; - --TODO: java lambdas are bugged - --return s"new common.Thunk<${ty}>(() -> ${exp})"; -} -function wrapLazy -String ::= e::Decorated Expr -{ - -- It *may* be wise to leave `Lazy`s as anon classes, rather than lambdas. - -- This splits all the Thunk methods across each `Lazy` instead of concentrating - -- them all on the top-level class, like `Init` - -- We're *unlikely* to be close to hitting the 64K method limit, but - -- we have hit the 64K bytecode limit in the past, which is why `Init` farms - -- initialization code out across each production. So who knows. - return s"new common.Lazy() { public final Object eval(final common.DecoratedNode context) { return ${e.translation}; } }"; -} - diff --git a/grammars/silver/translation/java/core/FunctionDcl.sv b/grammars/silver/translation/java/core/FunctionDcl.sv deleted file mode 100644 index b0cb5868e..000000000 --- a/grammars/silver/translation/java/core/FunctionDcl.sv +++ /dev/null @@ -1,194 +0,0 @@ -grammar silver:translation:java:core; - -import silver:modification:ffi only ioForeignType; -- for main type check only -import silver:util; - -aspect production functionDcl -top::AGDcl ::= 'function' id::Name ns::FunctionSignature body::ProductionBody -{ - top.setupInh := body.setupInh; - top.initProd := s"\t\t//FUNCTION ${id.name} ${ns.unparse}\n" ++ body.translation; - - local localVar :: String = "count_local__ON__" ++ makeIdName(fName); - - top.initWeaving := s"\tpublic static int ${localVar} = 0;\n"; - top.valueWeaving := body.valueWeaving; - - local argsAccess :: String = - implode(", ", map((.childRefElem), namedSig.inputElements)); - - local funBody :: String = -s""" final common.DecoratedNode context = new P${id.name}(${argsAccess}).decorate(); - //${head(body.uniqueSignificantExpression).unparse} - return (${namedSig.outputElement.typerep.transType})(${head(body.uniqueSignificantExpression).translation}); -"""; - - top.genFiles := - [pair(s"P${id.name}.java", generateFunctionClassString(top.grammarName, id.name, namedSig, funBody))] ++ - if id.name == "main" then [pair("Main.java", generateMainClassString(top.grammarName))] - else []; - - -- main function signature check TODO: this should probably be elsewhere! - top.errors <- - if id.name == "main" && - unify(namedSig.typerep, - functionType(nonterminalType("core:IOVal", [intType()]), [ - decoratedType(nonterminalType("core:List", [stringType()])), - ioForeignType], [])).failure - then [err(top.location, "main function must have type signature (IOVal ::= [String] IO). Instead it has type " ++ prettyType(namedSig.typerep))] - else []; -} - -function generateFunctionClassString -String ::= whatGrammar::String whatName::String whatSig::NamedSignature whatResult::String -{ - local className :: String = "P" ++ whatName; - - local localVar :: String = - s"count_local__ON__${makeIdName(whatGrammar)}_${whatName}"; - - return s""" -package ${makeName(whatGrammar)}; - -public final class ${className} extends common.FunctionNode { - -${makeIndexDcls(0, whatSig.inputElements)} - - public static final Class childTypes[] = { ${implode(",", map(makeChildTypes, whatSig.inputElements))} }; - - public static final int num_local_attrs = Init.${localVar}; - public static final String[] occurs_local = new String[num_local_attrs]; - - public static final common.Lazy[][] childInheritedAttributes = new common.Lazy[${toString(length(whatSig.inputElements))}][]; - - public static final common.Lazy[] localAttributes = new common.Lazy[num_local_attrs]; - public static final common.Lazy[][] localInheritedAttributes = new common.Lazy[num_local_attrs][]; - - static { -${implode("", map((.childStaticElem), whatSig.inputElements))} - } - - public ${className}(${whatSig.javaSignature}) { -${implode("", map(makeChildAssign, whatSig.inputElements))} - } - -${implode("", map((.childDeclElem), whatSig.inputElements))} - - @Override - public Object getChild(final int index) { - switch(index) { -${implode("", map(makeChildAccessCase, whatSig.inputElements))} - default: return null; - } - } - - @Override - public Object getChildLazy(final int index) { - switch(index) { -${implode("", map(makeChildAccessCaseLazy, whatSig.inputElements))} - default: return null; - } - } - - @Override - public final int getNumberOfChildren() { - return ${toString(length(whatSig.inputElements))}; - } - - @Override - public common.Lazy[] getLocalInheritedAttributes(final int key) { - return localInheritedAttributes[key]; - } - - @Override - public common.Lazy[] getChildInheritedAttributes(final int key) { - return childInheritedAttributes[key]; - } - - @Override - public common.Lazy getLocal(final int key) { - return localAttributes[key]; - } - - @Override - public final int getNumberOfLocalAttrs() { - return num_local_attrs; - } - - @Override - public final String getNameOfLocalAttr(final int index) { - return occurs_local[index]; - } - - @Override - public String getName() { - return "${whatSig.fullName}"; - } - - public static ${whatSig.outputElement.typerep.transType} invoke(${whatSig.javaSignature}) { - try { -${whatResult} - } catch(Throwable t) { - throw new common.exceptions.TraceException("Error while evaluating function ${whatSig.fullName}", t); - } - } - - // Use of ? to permit casting to more specific types - public static final common.NodeFactory factory = new Factory(); - - public static final class Factory extends common.NodeFactory<${whatSig.outputElement.typerep.transType}> { - @Override - public final ${whatSig.outputElement.typerep.transType} invoke(final Object[] children, final Object[] namedNotApplicable) { - return ${className}.invoke(${implode(", ", unpackChildren(0, whatSig.inputElements))}); - } - - @Override - public final common.FunctionTypeRep getType() { -${makeTyVarDecls(3, whatSig.typerep.freeVariables)} - return ${whatSig.typerep.transFreshTypeRep}; - } - - @Override - public final String toString() { - return "${whatGrammar}:${whatName}"; - } - }; -}"""; -} - -function generateMainClassString -String ::= whatGrammar::String -{ - local attribute package :: String; - package = makeName(whatGrammar); - - return s""" -package ${package}; - -public class Main { - public static void main(String[] args) { - ${package}.Init.initAllStatics(); - ${package}.Init.init(); - ${package}.Init.postInit(); - try { - common.Node rv = (common.Node) ${package}.Pmain.invoke(cvargs(args), common.IOToken.singleton); - common.DecoratedNode drv = rv.decorate(common.TopNode.singleton, (common.Lazy[])null); - drv.synthesized(core.Init.core_io__ON__core_IOVal); // demand the io token - System.exit( (Integer)drv.synthesized(core.Init.core_iovalue__ON__core_IOVal) ); - } catch(Throwable t) { - Throwable rt = common.exceptions.SilverException.getRootCause(t); - if(rt instanceof common.exceptions.SilverExit) - System.exit(((common.exceptions.SilverExit)rt).getExitCode()); - common.Util.printStackCauses(t); - } - } - public static common.ConsCell cvargs(String[] args) { - common.ConsCell result = common.ConsCell.nil; - for(int i = args.length - 1; i >= 0; i--) { - result = new common.ConsCell(new common.StringCatter(args[i]), result); - } - return result; - } -}"""; -} - diff --git a/grammars/silver/translation/java/core/GlobalDcl.sv b/grammars/silver/translation/java/core/GlobalDcl.sv deleted file mode 100644 index f5ea28ee0..000000000 --- a/grammars/silver/translation/java/core/GlobalDcl.sv +++ /dev/null @@ -1,8 +0,0 @@ -grammar silver:translation:java:core; - -aspect production globalValueDclConcrete -top::AGDcl ::= 'global' id::Name '::' t::TypeExpr '=' e::Expr ';' -{ - top.initValues := - s"\tpublic static final common.Thunk<${t.typerep.transType}> ${id.name} = ${wrapThunkText(e.translation, t.typerep.transType)};\n"; -} diff --git a/grammars/silver/translation/java/core/NamedSignature.sv b/grammars/silver/translation/java/core/NamedSignature.sv deleted file mode 100644 index 3953eea1e..000000000 --- a/grammars/silver/translation/java/core/NamedSignature.sv +++ /dev/null @@ -1,176 +0,0 @@ -grammar silver:translation:java:core; - -{-- - - The java translation of the *input parameters* signature. - -} -synthesized attribute javaSignature :: String occurs on NamedSignature; -synthesized attribute refInvokeTrans :: String occurs on NamedSignature; --- "final Object c_signame" -synthesized attribute childSigElem :: String occurs on NamedSignatureElement; -synthesized attribute annoSigElem :: String occurs on NamedSignatureElement; --- "c_signame" -synthesized attribute childRefElem :: String occurs on NamedSignatureElement; -synthesized attribute annoRefElem :: String occurs on NamedSignatureElement; --- "inhs[c_signame] = new lazy[]" -synthesized attribute childStaticElem :: String occurs on NamedSignatureElement; --- "private Object child_signame..." -synthesized attribute childDeclElem :: String occurs on NamedSignatureElement; -synthesized attribute annoDeclElem :: String occurs on NamedSignatureElement; --- "signame" -synthesized attribute annoNameElem :: String occurs on NamedSignatureElement; --- "if (name.equals("signame")) { return getAnno_signame(); }" -synthesized attribute annoLookupElem :: String occurs on NamedSignatureElement; - -aspect production namedSignature -top::NamedSignature ::= fn::String ie::[NamedSignatureElement] oe::NamedSignatureElement np::[NamedSignatureElement] -{ - top.javaSignature = implode(", ", map((.childSigElem), ie) ++ map((.annoSigElem), np)); - top.refInvokeTrans = implode(", ", map((.childRefElem), ie) ++ map((.annoRefElem), np)); -} - --- TODO: It'd be nice to maybe split these into the ordered parameters and the annotations -aspect production namedSignatureElement -top::NamedSignatureElement ::= n::String ty::Type -{ - top.childSigElem = "final Object c_" ++ n; - top.childRefElem = "c_" ++ n; - top.childDeclElem = -s""" private Object child_${n}; - public final ${ty.transType} getChild_${n}() { - return (${ty.transType}) (child_${n} = common.Util.demand(child_${n})); - } - -"""; - - top.childStaticElem = - if !ty.isDecorable then "" - else s"\tchildInheritedAttributes[i_${n}] = new common.Lazy[${makeNTClassName(ty.typeName)}.num_inh_attrs];\n"; - - -- annos are full names: - - local fn :: String = makeIdName(n); - - top.annoSigElem = "final Object a_" ++ fn; - top.annoRefElem = "a_" ++ fn; - top.annoDeclElem = -s""" private Object anno_${fn}; - @Override - public final ${ty.transType} getAnno_${fn}() { - return (${ty.transType}) (anno_${fn} = common.Util.demand(anno_${fn})); - } - -"""; - - top.annoNameElem = s"\"${n}\""; - top.annoLookupElem = -s"""if (name.equals("${n}")) { - return getAnno_${fn}(); - } else """; -} - -function makeIndexDcls -String ::= i::Integer s::[NamedSignatureElement] -{ - return if null(s) then "" - else s"\tpublic static final int i_${head(s).elementName} = ${toString(i)};\n" ++ makeIndexDcls(i+1, tail(s)); -} - --- TODO I'd really like to just get rid of this. -function makeChildTypes -String ::= ns::NamedSignatureElement -{ return ns.typerep.transClassType ++ ".class"; -} - -function unpackChildren -[String] ::= i::Integer ns::[NamedSignatureElement] -{ - return if null(ns) then [] - else (s"children[${toString(i)}]") :: unpackChildren(i + 1, tail(ns)); -} -function unpackAnnotations -[String] ::= i::Integer ns::[NamedSignatureElement] -{ - return if null(ns) then [] - else (s"annotations[${toString(i)}]") :: unpackAnnotations(i + 1, tail(ns)); -} - -function makeChildAccessCase -String ::= n::NamedSignatureElement -{ - return s"\t\t\tcase i_${n.elementName}: return getChild_${n.elementName}();\n"; -} -function makeChildAccessCaseLazy -String ::= n::NamedSignatureElement -{ - return s"\t\t\tcase i_${n.elementName}: return child_${n.elementName};\n"; -} - -function makeAnnoAssign -String ::= n::NamedSignatureElement -{ - local fn :: String = makeIdName(n.elementName); - return s"\t\tthis.anno_${fn} = a_${fn};\n"; -} -function makeChildAssign -String ::= n::NamedSignatureElement -{ - return s"\t\tthis.child_${n.elementName} = c_${n.elementName};\n"; -} - -function makeTyVarDecls -String ::= indent::Integer vars::[TyVar] -{ - return - implode( - "\n", - map( - \ tv::TyVar -> - s"${sconcat(repeat("\t", indent))}common.VarTypeRep freshTypeVar_${toString(tv.extractTyVarRep)} = new common.VarTypeRep();", - vars)); - -} -function makeAnnoIndexDcls -String ::= i::Integer s::[NamedSignatureElement] -{ - return if null(s) then "" - else s"\t\tfinal int i${head(s).annoRefElem} = ${toString(i)};\n" ++ makeAnnoIndexDcls(i+1, tail(s)); -} -function makeChildUnify -String ::= fn::String n::NamedSignatureElement -{ - return -s"""try { - if (!common.TypeRep.unify(${n.typerep.transFreshTypeRep}, common.Reflection.getType(getChild_${n.elementName}()))) { - throw new common.exceptions.SilverInternalError("Unification failed."); - } - } catch (common.exceptions.SilverException e) { - throw new common.exceptions.TraceException("While constructing type of child '${n.elementName}' of production '${fn}'", e); - } -"""; -} -function makeChildReify -String ::= fn::String numChildren::Integer n::NamedSignatureElement -{ - return -s"""Object ${n.childRefElem} = null; - try { - ${n.childRefElem} = common.Reflection.reify(${n.typerep.transFreshTypeRep}, childASTs[i_${n.elementName}]); - } catch (common.exceptions.SilverException e) { - throw new common.exceptions.ChildReifyTraceException("${fn}", "${n.elementName}", ${toString(numChildren)}, i_${n.elementName}, e); - } -"""; -} -function makeAnnoReify -String ::= fn::String n::NamedSignatureElement -{ - return -s"""Object ${n.annoRefElem} = null; - try { - ${n.annoRefElem} = common.Reflection.reify(${n.typerep.transFreshTypeRep}, annotationASTs[i${n.annoRefElem}]); - } catch (common.exceptions.SilverException e) { - throw new common.exceptions.AnnotationReifyTraceException("${fn}", "${n.elementName}", e); - } -"""; -} - - diff --git a/grammars/silver/translation/java/core/NonTerminalDcl.sv b/grammars/silver/translation/java/core/NonTerminalDcl.sv deleted file mode 100644 index 31bef74af..000000000 --- a/grammars/silver/translation/java/core/NonTerminalDcl.sv +++ /dev/null @@ -1,82 +0,0 @@ -grammar silver:translation:java:core; - -aspect production nonterminalDcl -top::AGDcl ::= cl::ClosedOrNot 'nonterminal' id::Name tl::BracketedOptTypeExprs nm::NonterminalModifiers ';' -{ - local className :: String = "N" ++ id.name; - - local inhVar :: String = "count_inh__ON__" ++ id.name; - local synVar :: String = "count_syn__ON__" ++ id.name; - - local myAnnos :: [NamedSignatureElement] = - annotationsForNonterminal(nonterminalType(fName, tl.types), top.env); - - top.initWeaving := s""" - public static int ${inhVar} = 0; - public static int ${synVar} = 0;"""; - - top.genFiles := [pair(className ++ ".java", s""" -package ${makeName(top.grammarName)}; - -import java.util.*; - -public abstract class ${className} extends common.Node${ - (if null(myAnnos) then "" else - " implements " ++ implode(", ", map(makeAnnoClassName, map((.elementName), myAnnos))) - )} { - - public static final int num_inh_attrs = Init.${inhVar}; - public static final int num_syn_attrs = Init.${synVar}; - - public static final String[] occurs_inh = new String[num_inh_attrs]; - public static final String[] occurs_syn = new String[num_syn_attrs]; - public static final LinkedList decorators = new LinkedList(); - - public static final common.Lazy[] defaultSynthesizedAttributes = new common.Lazy[num_syn_attrs]; - - protected ${className}(${implode(", ", map((.annoSigElem), myAnnos))}) { -${implode("", map(makeAnnoAssign, myAnnos))} - } - -${implode("", map((.annoDeclElem), myAnnos))} - - @Override - public final int getNumberOfInhAttrs() { - return num_inh_attrs; - } - - @Override - public final int getNumberOfSynAttrs() { - return num_syn_attrs; - } - - @Override - public final common.Lazy getDefaultSynthesized(final int index) { - return defaultSynthesizedAttributes[index]; - } - - @Override - public final String getNameOfInhAttr(final int index) { - return occurs_inh[index]; - } - - @Override - public final String getNameOfSynAttr(final int index) { - return occurs_syn[index]; - } - - @Override - public final String[] getAnnoNames() { - return new String[]{${implode(", ", map((.annoNameElem), myAnnos))}}; - } - - @Override - public final Object getAnno(final String name) { - ${sconcat(map((.annoLookupElem), myAnnos))}{ - throw new common.exceptions.SilverInternalError("Invalid annotation " + name); - } - } -} -""")]; - -} diff --git a/grammars/silver/translation/java/core/OccursDcl.sv b/grammars/silver/translation/java/core/OccursDcl.sv deleted file mode 100644 index 106c1d7f3..000000000 --- a/grammars/silver/translation/java/core/OccursDcl.sv +++ /dev/null @@ -1,31 +0,0 @@ -grammar silver:translation:java:core; - -aspect production defaultAttributionDcl -top::AGDcl ::= at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - local ntfn :: String = nt.lookupType.fullName; - local occursType :: String = if at.lookupAttribute.dcl.isSynthesized then "syn" else "inh"; - local ntgrammar :: String = substring(0, lastIndexOf(":", ntfn), ntfn); - local ntname :: String = substring(lastIndexOf(":", ntfn)+1 , length(ntfn), ntfn); - -- TODO: don't we have a 'grammarFromQName' function somewhere? or even an attribute on qname why not? - - top.setupInh := - if at.lookupAttribute.dcl.isAnnotation then - "" - else - s"\t\t${makeNTClassName(ntfn)}.occurs_${occursType}[${head(occursCheck).attrOccursIndex}] = \"${at.lookupAttribute.fullName}\";\n"; - - top.valueWeaving := - if at.lookupAttribute.dcl.isAnnotation then - "" - else - s"public static final int ${head(occursCheck).attrOccursIndexName} = " ++ - s"${makeName(ntgrammar)}.Init.count_${occursType}__ON__${ntname}++;\n"; -} - -aspect production errorAttributionDcl -top::AGDcl ::= msg::[Message] at::Decorated QName attl::BracketedOptTypeExprs nt::QName nttl::BracketedOptTypeExprs -{ - top.setupInh := error("Internal compiler error: translation not defined in the presence of errors"); - top.valueWeaving := error("Internal compiler error: translation not defined in the presence of errors"); -} diff --git a/grammars/silver/translation/java/core/ProductionBody.sv b/grammars/silver/translation/java/core/ProductionBody.sv deleted file mode 100644 index c71569aad..000000000 --- a/grammars/silver/translation/java/core/ProductionBody.sv +++ /dev/null @@ -1,185 +0,0 @@ -grammar silver:translation:java:core; - -import silver:util; - -synthesized attribute attrName :: String; - -attribute attrName occurs on ForwardLHSExpr; - -attribute setupInh, translation, valueWeaving occurs on ProductionBody, ProductionStmts, ProductionStmt; -attribute translation occurs on DefLHS, ForwardInhs, ForwardInh; - -propagate setupInh, valueWeaving on ProductionBody, ProductionStmts; - -aspect production productionBody -top::ProductionBody ::= '{' stmts::ProductionStmts '}' -{ - top.translation = stmts.translation; -} - -aspect production productionStmtsNil -top::ProductionStmts ::= -{ - top.translation = ""; -} - -aspect production productionStmtsSnoc -top::ProductionStmts ::= h::ProductionStmts t::ProductionStmt -{ - top.translation = h.translation ++ t.translation; -} - -------- - -aspect production productionStmtAppend -top::ProductionStmt ::= h::ProductionStmt t::ProductionStmt -{ - propagate setupInh, valueWeaving; - top.translation = h.translation ++ t.translation; -} - -aspect production errorProductionStmt -top::ProductionStmt ::= e::[Message] -{ - top.translation = ""; -} - --------------------------------------------------------------------------------- - -aspect default production -top::ProductionStmt ::= -{ - -- Always require translation - propagate setupInh, valueWeaving; -} - -aspect production forwardsTo -top::ProductionStmt ::= 'forwards' 'to' e::Expr ';' -{ - top.translation = ""; -} - -aspect production forwardingWith -top::ProductionStmt ::= 'forwarding' 'with' '{' inh::ForwardInhs '}' ';' -{ - top.translation = inh.translation; -} - -aspect production forwardInh -top::ForwardInh ::= lhs::ForwardLHSExpr '=' e::Expr ';' -{ - top.translation = - s"\t\t//${top.unparse}\n" ++ - s"\t\t${top.frame.className}.forwardInheritedAttributes[${lhs.attrName}] = ${wrapLazy(e)};\n"; -} - -aspect production forwardInhsOne -top::ForwardInhs ::= lhs::ForwardInh -{ - top.translation = lhs.translation; -} - -aspect production forwardInhsCons -top::ForwardInhs ::= lhs::ForwardInh rhs::ForwardInhs -{ - top.translation = lhs.translation ++ rhs.translation; -} - -aspect production forwardLhsExpr -top::ForwardLHSExpr ::= q::QNameAttrOccur -{ - top.attrName = q.dcl.attrOccursIndex; -} - -aspect production localAttributeDcl -top::ProductionStmt ::= 'local' 'attribute' a::Name '::' te::TypeExpr ';' -{ - local attribute ugh_dcl_hack :: DclInfo; - ugh_dcl_hack = head(getValueDclAll(fName, top.env)); -- TODO really, we should have a DclInfo for ourselves no problem. but out current approach of constructing it via localDef makes this annoyingly difficult. this suggests a probably environment refactoring... - - top.valueWeaving := s"public static final int ${ugh_dcl_hack.attrOccursIndexName} = ${top.frame.prodLocalCountName}++;\n"; - - top.setupInh := - if !te.typerep.isDecorable then "" else - s"\t\t//${top.unparse}\n" ++ - s"\t\t${top.frame.className}.localInheritedAttributes[${ugh_dcl_hack.attrOccursIndex}] = " ++ - s"new common.Lazy[${makeNTClassName(te.typerep.typeName)}.num_inh_attrs];\n"; - - top.setupInh <- s"\t\t${top.frame.className}.occurs_local[${ugh_dcl_hack.attrOccursIndex}] = \"${fName}\";\n"; - - top.translation = ""; -} - -aspect production childDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.translation = s"${top.frame.className}.childInheritedAttributes[${top.frame.className}.i_${q.lookupValue.fullName}]"; -} - -aspect production lhsDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.translation = s"${top.frame.className}.synthesizedAttributes"; -} - -aspect production localDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.translation = s"${top.frame.className}.localInheritedAttributes[${q.lookupValue.dcl.attrOccursIndex}]"; -} - -aspect production forwardDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.translation = s"${top.frame.className}.forwardInheritedAttributes"; -} - -aspect production errorDefLHS -top::DefLHS ::= q::Decorated QName -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); -} - -aspect production errorAttributeDef -top::ProductionStmt ::= msg::[Message] dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); -} - -aspect production synthesizedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.translation = - s"\t\t// ${dl.unparse}.${attr.unparse} = ${e.unparse}\n" ++ - s"\t\t${dl.translation}[${attr.dcl.attrOccursIndex}] = ${wrapLazy(e)};\n"; -} - -aspect production inheritedAttributeDef -top::ProductionStmt ::= dl::Decorated DefLHS attr::Decorated QNameAttrOccur e::Expr -{ - top.translation = - s"\t\t// ${dl.unparse}.${attr.unparse} = ${e.unparse}\n" ++ - s"\t\t${dl.translation}[${attr.dcl.attrOccursIndex}] = ${wrapLazy(e)};\n"; -} - - -aspect production errorValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.translation = error("Internal compiler error: translation not defined in the presence of errors"); -} - -aspect production localValueDef -top::ProductionStmt ::= val::Decorated QName e::Expr -{ - top.translation = - s"\t\t// ${val.unparse} = ${e.unparse}\n" ++ - s"\t\t${top.frame.className}.localAttributes[${val.lookupValue.dcl.attrOccursIndex}] = ${wrapLazy(e)};\n"; -} - -aspect production returnDef -top::ProductionStmt ::= 'return' e::Expr ';' -{ - top.translation = ""; -} - diff --git a/grammars/silver/translation/java/core/ProductionDcl.sv b/grammars/silver/translation/java/core/ProductionDcl.sv deleted file mode 100644 index 83399b4fa..000000000 --- a/grammars/silver/translation/java/core/ProductionDcl.sv +++ /dev/null @@ -1,196 +0,0 @@ -grammar silver:translation:java:core; - -import silver:util; - -aspect production productionDcl -top::AGDcl ::= 'abstract' 'production' id::Name ns::ProductionSignature body::ProductionBody -{ - local className :: String = "P" ++ id.name; - - top.setupInh := body.setupInh; - top.initProd := s"\t\t${makeName(top.grammarName)}.${className}.initProductionAttributeDefinitions();\n"; - top.postInit := s"\t\tcommon.Decorator.applyDecorators(${fnnt}.decorators, ${className}.class);\n"; - - top.initWeaving := s"\tpublic static int ${localVar} = 0;\n"; - top.valueWeaving := body.valueWeaving; - - local localVar :: String = "count_local__ON__" ++ makeIdName(fName); - local ntName :: String = namedSig.outputElement.typerep.typeName; - local fnnt :: String = makeNTClassName(ntName); - - top.genFiles := [pair(className ++ ".java", s""" -package ${makeName(top.grammarName)}; - -// ${ns.unparse} -public final class ${className} extends ${fnnt} { - -${makeIndexDcls(0, namedSig.inputElements)} - - public static final Class childTypes[] = {${implode(",", map(makeChildTypes, namedSig.inputElements))}}; - - public static final int num_local_attrs = Init.${localVar}; - public static final String[] occurs_local = new String[num_local_attrs]; - - public static final common.Lazy[] forwardInheritedAttributes = new common.Lazy[${fnnt}.num_inh_attrs]; - - public static final common.Lazy[] synthesizedAttributes = new common.Lazy[${fnnt}.num_syn_attrs]; - public static final common.Lazy[][] childInheritedAttributes = new common.Lazy[${toString(length(namedSig.inputElements))}][]; - - public static final common.Lazy[] localAttributes = new common.Lazy[num_local_attrs]; - public static final common.Lazy[][] localInheritedAttributes = new common.Lazy[num_local_attrs][]; - - static { -${implode("", map((.childStaticElem), namedSig.inputElements))} - } - - public ${className}(${namedSig.javaSignature}) { - super(${implode(", ", map((.annoRefElem), namedSig.namedInputElements))}); -${implode("", map(makeChildAssign, namedSig.inputElements))} - } - -${implode("", map((.childDeclElem), namedSig.inputElements))} - - @Override - public Object getChild(final int index) { - switch(index) { -${implode("", map(makeChildAccessCase, namedSig.inputElements))} - default: return null; - } - } - - @Override - public Object getChildLazy(final int index) { - switch(index) { -${implode("", map(makeChildAccessCaseLazy, namedSig.inputElements))} - default: return null; - } - } - - @Override - public final int getNumberOfChildren() { - return ${toString(length(namedSig.inputElements))}; - } - - @Override - public common.Lazy getSynthesized(final int index) { - return synthesizedAttributes[index]; - } - - @Override - public common.Lazy[] getLocalInheritedAttributes(final int key) { - return localInheritedAttributes[key]; - } - - @Override - public common.Lazy[] getChildInheritedAttributes(final int key) { - return childInheritedAttributes[key]; - } - - @Override - public boolean hasForward() { - return ${(if null(body.uniqueSignificantExpression) then "false" else "true")}; - } - - @Override - public common.Node evalForward(final common.DecoratedNode context) { - ${if null(body.uniqueSignificantExpression) - then s"throw new common.exceptions.SilverInternalError(\"Production ${fName} erroneously claimed to forward\")" - else "return " ++ head(body.uniqueSignificantExpression).translation}; - } - - @Override - public common.Lazy getForwardInheritedAttributes(final int index) { - return forwardInheritedAttributes[index]; - } - - @Override - public common.Lazy getLocal(final int key) { - return localAttributes[key]; - } - - @Override - public final int getNumberOfLocalAttrs() { - return num_local_attrs; - } - - @Override - public final String getNameOfLocalAttr(final int index) { - return occurs_local[index]; - } - - @Override - public String getName() { - return "${fName}"; - } - - @Override - public final common.BaseTypeRep getType() { -${makeTyVarDecls(2, namedSig.typerep.freeVariables)} - - ${implode("\n\t\t", map(makeChildUnify(fName, _), namedSig.inputElements))} - - return ${namedSig.outputElement.typerep.transFreshTypeRep}; - } - - static void initProductionAttributeDefinitions() { -${body.translation} - } - - public static ${className} reify( - final common.TypeRep resultType, - final core.reflect.NAST[] childASTs, - final String[] annotationNames, - final core.reflect.NAST[] annotationASTs) { - assert annotationNames.length == annotationASTs.length; -${makeAnnoIndexDcls(0, namedSig.namedInputElements)} -${makeTyVarDecls(2, namedSig.typerep.freeVariables)} - - common.TypeRep givenType = ${namedSig.outputElement.typerep.transFreshTypeRep}; - if (!common.TypeRep.unify(resultType, givenType)) { - throw new common.exceptions.SilverError("reify is constructing " + resultType.toString() + ", but found " + givenType.toString() + " production ${fName} AST."); - } - - if (childASTs.length != ${toString(length(namedSig.inputElements))}) { - throw new common.exceptions.SilverError("Production ${fName} expected ${toString(length(namedSig.inputElements))} child(ren), but got " + childASTs.length + "."); - } - - String[] expectedAnnotationNames = new String[] {${implode(", ", map((.annoNameElem), annotationsForNonterminal(namedSig.outputElement.typerep, top.env)))}}; - if (!java.util.Arrays.equals(annotationNames, expectedAnnotationNames)) { - throw new common.exceptions.SilverError("Production ${fName} expected " + common.Util.namesToString(expectedAnnotationNames, "no") + " annotation(s), but got " + common.Util.namesToString(annotationNames, "none") + "."); - } - - ${implode("\n\t\t", map(makeChildReify(fName, length(namedSig.inputElements), _), namedSig.inputElements))} - ${implode("\n\t\t", map(makeAnnoReify(fName, _), namedSig.namedInputElements))} - - return new ${className}(${namedSig.refInvokeTrans}); - } - - public static final common.NodeFactory<${fnnt}> factory = new Factory(); - - public static final class Factory extends common.NodeFactory<${fnnt}> { - @Override - public final ${fnnt} invoke(final Object[] children, final Object[] annotations) { - return new ${className}(${implode(", ", unpackChildren(0, namedSig.inputElements) ++ unpackAnnotations(0, namedSig.namedInputElements))}); - } - - @Override - public final common.FunctionTypeRep getType() { -${makeTyVarDecls(3, namedSig.typerep.freeVariables)} - return ${namedSig.typerep.transFreshTypeRep}; - } - - @Override - public final String toString() { - return "${top.grammarName}:${id.name}"; - } - }; - -} -""")]; - - -- main function signature check TODO: this should probably be elsewhere! - top.errors <- - if id.name == "main" - then [err(top.location, "main should be a function!")] - else []; -} diff --git a/grammars/silver/translation/java/core/Project.sv b/grammars/silver/translation/java/core/Project.sv deleted file mode 100644 index bc6c824b2..000000000 --- a/grammars/silver/translation/java/core/Project.sv +++ /dev/null @@ -1,64 +0,0 @@ -grammar silver:translation:java:core; - -imports silver:translation:java:type; - -imports silver:definition:core; -imports silver:definition:type:syntax; - -imports silver:definition:env; -imports silver:definition:type; - - -import silver:util; - -function makeName -String ::= str::String -{ - return substitute(":", ".", str); -} -function makeIdName -String ::= str::String -{ - return substitute(":", "_", str); -} - -function makeClassName -String ::= s::String -{ - return substituteLast(".", ".P", makeName(s)); -} - -function makeNTClassName -String ::= s::String -{ - return substituteLast(".", ".N", makeName(s)); -} - -function makeAnnoClassName -String ::= s::String -{ - return substituteLast(".", ".A", makeName(s)); -} - -function makeTerminalName -String ::= s::String -{ - return substituteLast(".", ".T", makeName(s)); -} - -function makeParserName -String ::= s::String -{ - return "Parser_" ++ makeIdName(s); -} - -function substituteLast -String ::= r::String s::String str::String -{ - local attribute i::Integer; - i = lastIndexOf(r, str); - - return if i == -1 then str - else substring(0,i,str) ++ s ++ substring(i+length(r), length(str), str); -} - diff --git a/grammars/silver/translation/java/core/Root.sv b/grammars/silver/translation/java/core/Root.sv deleted file mode 100644 index 6e3c23123..000000000 --- a/grammars/silver/translation/java/core/Root.sv +++ /dev/null @@ -1,50 +0,0 @@ -grammar silver:translation:java:core; - -{-- - - Java classes to generate. (filename, contents) - -} -monoid attribute genFiles :: [Pair] with [], ++; -{-- - - Early initializers: occurs.add, local's inh attr map creation, decorators.add, collection object creation - -} -monoid attribute setupInh :: String with "", ++; -{-- - - Initialize the attributes maps for each production. - - note to be confused with "production attribute" dcls. - -} -monoid attribute initProd :: String with "", ++; -{-- - - Global values. - -} -monoid attribute initValues :: String with "", ++; -{-- - - Late initializers. Decorator application (late because we want all attribute equations to be posted first!!) - -} -monoid attribute postInit :: String with "", ++; - -synthesized attribute translation :: String; -{-- - - Initial values for early weaving. e.g. counter for # attributes on NT - -} -monoid attribute initWeaving :: String with "", ++; -{-- - - Values computed by early weaving. e.g. index of attribute in NT arrays - -} -monoid attribute valueWeaving :: String with "", ++; - -attribute genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving occurs on Root, AGDcls, AGDcl, Grammar; - -propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving on Root, AGDcls, Grammar; - -aspect default production -top::AGDcl ::= -{ - -- Empty values as defaults - propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving; -} - -aspect production appendAGDcl -top::AGDcl ::= h::AGDcl t::AGDcl -{ - propagate genFiles,setupInh,initProd,initValues,postInit,initWeaving,valueWeaving; -} diff --git a/grammars/silver/translation/java/core/RootSpec.sv b/grammars/silver/translation/java/core/RootSpec.sv deleted file mode 100644 index 44c72e8df..000000000 --- a/grammars/silver/translation/java/core/RootSpec.sv +++ /dev/null @@ -1,74 +0,0 @@ -grammar silver:translation:java:core; - -import silver:driver:util; - -attribute genFiles occurs on RootSpec; - -aspect production interfaceRootSpec -top::RootSpec ::= _ _ _ -{ - top.genFiles := []; -} - -aspect production errorRootSpec -top::RootSpec ::= _ _ _ _ _ -{ - top.genFiles := []; -} - -aspect production grammarRootSpec -top::RootSpec ::= g::Grammar _ _ _ _ -{ - top.genFiles := g.genFiles ++ - [pair("Silver.svi", unparseRootSpec(top)), - pair("Init.java", s""" -package ${makeName(g.declaredName)}; - -public class Init{ - private static boolean preInit = false; - private static boolean init = false; - private static boolean postInit = false; - - public static void initAllStatics(){ - if(preInit) return; - preInit = true; -${makeOthers(top.allGrammarDependencies, "initAllStatics")} - } - - public static void init(){ - if(init) return; - init = true; - setupInheritedAttributes(); -${makeOthers(top.allGrammarDependencies, "init")} - initProductionAttributeDefinitions(); - } - - public static void postInit(){ - if(postInit) return; - postInit = true; -${makeOthers(top.allGrammarDependencies, "postInit")} -${g.postInit} - } - - private static void setupInheritedAttributes(){ -${g.setupInh} - } - - private static void initProductionAttributeDefinitions(){ -${g.initProd} - } - -${g.initWeaving} -${g.valueWeaving} - final static common.DecoratedNode context = common.TopNode.singleton; // For globals -${g.initValues} -} -""")]; -} - -function makeOthers -String ::= others::[String] nme::String -{ - return if null(others) then "" else s"\t\t${makeName(head(others))}.Init.${nme}();\n${makeOthers(tail(others),nme)}"; -} - diff --git a/grammars/silver/translation/java/core/TerminalDcl.sv b/grammars/silver/translation/java/core/TerminalDcl.sv deleted file mode 100644 index 2b2fcbdce..000000000 --- a/grammars/silver/translation/java/core/TerminalDcl.sv +++ /dev/null @@ -1,49 +0,0 @@ -grammar silver:translation:java:core; - -imports silver:definition:concrete_syntax; -imports silver:modification:copper; - -aspect production terminalDclDefault -top::AGDcl ::= t::TerminalKeywordModifier id::Name r::RegExpr tm::TerminalModifiers -{ - top.genFiles := terminalTranslation(id.name, top.grammarName, tm.lexerClasses); -} - -function terminalTranslation -[Pair] ::= name::String grammarName::String lexerClasses::[String] -{ - local className :: String = "T" ++ name; - local fName :: String = grammarName ++ ":" ++ name; - local lexerClassesStr :: String = implode(", ", map(\ s::String -> s"\"${s}\"", lexerClasses)); - - return [pair(className ++ ".java", s""" -package ${makeName(grammarName)}; - -import edu.umn.cs.melt.copper.runtime.engines.semantics.VirtualLocation; -import core.NLocation; - -public class ${className} extends common.Terminal { - public ${className}(final String lexeme, final VirtualLocation vl, final int index, final int endIndex) { - super(lexeme, vl, index, endIndex); - } - - public ${className}(final common.StringCatter lexeme, final NLocation location) { - super(lexeme, location); - } - - @Override - public String getName() { return "${fName}"; } - - private static String[] lexerclasses = null; - @Override - public String[] getLexerClasses() { - // Avoid doing more work at class-load time, in case we don't need this - if (lexerclasses == null) { - lexerclasses = new String[] {${lexerClassesStr}}; - } - return lexerclasses; - } -} - -""")]; -} diff --git a/grammars/silver/translation/java/driver/BuildProcess.sv b/grammars/silver/translation/java/driver/BuildProcess.sv deleted file mode 100644 index faf7ada53..000000000 --- a/grammars/silver/translation/java/driver/BuildProcess.sv +++ /dev/null @@ -1,271 +0,0 @@ -grammar silver:translation:java:driver; - -import silver:translation:java:core; - -import silver:driver; -import silver:definition:env; -import silver:definition:core; - -import silver:util; -import silver:util:cmdargs; - -synthesized attribute noJavaGeneration :: Boolean occurs on CmdArgs; -synthesized attribute buildSingleJar :: Boolean occurs on CmdArgs; -synthesized attribute relativeJar :: Boolean occurs on CmdArgs; -synthesized attribute includeRTJars :: [String] occurs on CmdArgs; -synthesized attribute buildXmlLocation :: [String] occurs on CmdArgs; - -aspect production endCmdArgs -top::CmdArgs ::= _ -{ - top.noJavaGeneration = false; - top.buildSingleJar = false; - top.relativeJar = false; - top.includeRTJars = []; - top.buildXmlLocation = []; -} -abstract production xjFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.noJavaGeneration = true; - forwards to rest; -} -abstract production onejarFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.buildSingleJar = true; - forwards to rest; -} -abstract production relativejarFlag -top::CmdArgs ::= rest::CmdArgs -{ - top.relativeJar = true; - forwards to rest; -} -abstract production includeRTJarFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.includeRTJars = s :: forward.includeRTJars; - forwards to rest; -} -abstract production buildXmlFlag -top::CmdArgs ::= s::String rest::CmdArgs -{ - top.buildXmlLocation = s :: forward.buildXmlLocation; - forwards to rest; -} - -aspect function parseArgs -Either ::= args::[String] -{ - flags <- [pair("--dont-translate", flag(xjFlag)), - pair("--onejar", flag(onejarFlag)), - pair("--one-jar", flag(onejarFlag)), - pair("--relative-jar", flag(relativejarFlag)), - pair("--include-jar", option(includeRTJarFlag)), - pair("--build-xml-location", option(buildXmlFlag)) - ]; - flagdescs <- ["\t--one-jar : include runtime libraries in the jar"]; -} -aspect production compilation -top::Compilation ::= g::Grammars _ buildGrammar::String benv::BuildEnv -{ - -- This is a little bit of a hack. It's only job is to allow the Eclipse support - -- for Silver to put this file elsewhere than the local directory. - -- e.g. --build-xml-location /path/to/workspace/project/build.xml - local buildXmlLocation :: String = - if null(top.config.buildXmlLocation) then "build.xml" - else head(top.config.buildXmlLocation); - - top.postOps <- - [genBuild(buildXmlLocation, buildXml)] ++ - (if top.config.noJavaGeneration then [] else [genJava(top.config, grammarsToTranslate, benv.silverGen)]); - - -- From here on, it's all build.xml stuff: - - -- Presently, copper, copper_mda, and impide all contribute new targets into build.xml: - production attribute extraTopLevelDecls :: [String] with ++; - extraTopLevelDecls := []; - - -- Presently, impide and copper_mda introduce a new top-level goal: - production attribute extraDistDeps :: [String] with ++; - extraDistDeps := if top.config.noJavaGeneration then [] else ["jars"]; - - -- Presently, unused? - production attribute extraJarsDeps :: [String] with ++; - extraJarsDeps := ["grammars"]; - - -- Presently, copper and copper_mda - production attribute extraGrammarsDeps :: [String] with ++; - extraGrammarsDeps := ["init"]; - - production attribute classpathCompiler :: [String] with ++; - classpathCompiler := []; - - production attribute classpathRuntime :: [String] with ++; - classpathRuntime := ["${sh}/jars/SilverRuntime.jar"]; - - -- The --XRTjar hack - classpathRuntime <- top.config.includeRTJars; - - production attribute extraManifestAttributes :: [String] with ++; - extraManifestAttributes := [ - "", - "", - ""]; -- TODO: we "should" make main depend on whether there is a main... - - extraManifestAttributes <- - if top.config.buildSingleJar then [] - else [""]; - - local attribute outputFile :: String; - outputFile = if !null(top.config.outName) then head(top.config.outName) - else (if g.jarName.isJust then g.jarName.fromJust else makeName(buildGrammar)) ++ ".jar"; - - local attribute buildXml :: String; - buildXml = -"\n" ++ -" Generated build script for the grammar " ++ buildGrammar ++ "\n\n" ++ - -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n\n" ++ - -" \n" ++ - sflatMap(pathLocation, classpathRuntime) ++ -" \n\n" ++ - -" \n" ++ -" \n" ++ -" \n" ++ - sflatMap(pathLocation, classpathCompiler) ++ - sflatMap(pathLocation, map(\s::String -> s ++ "bin/", benv.silverHostGen)) ++ -" \n\n" ++ - -implode("\n\n", extraTopLevelDecls) ++ "\n\n" ++ - -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n" ++ -" \n\n" ++ - -" \n" ++ -" \n\n" ++ - -" \n" ++ --- Uncondintionally compute this, but it's included conditionally as a manifest attribute -" \n" ++ -( - if top.config.relativeJar then --- Removes all paths from the classpath. This means we expect to find all these --- jars in the same directory as this jar. -" \n" - else --- Escape spaces as url-encoded spaces. maybe there's a better way? --- This solves the problem of spaces in paths, where Class-Path in manifests are split on spaces. -" \n" -) ++ -" \n" ++ -" \n" ++ - sflatMap(includeClassFiles, grammarsRelevant) ++ -" \n" ++ -" " ++ implode("\n ", extraManifestAttributes) ++ "\n" ++ -" \n" ++ - --- If we're building a single jar, then include the runtimes TODO: this method kinda sucks - (if top.config.buildSingleJar then implode("", map(zipfileset, classpathRuntime)) else "") ++ - -" \n" ++ -" \n\n" ++ - -" \n" ++ -" \n" ++ - sflatMap(includeJavaFiles, grammarsDependedUpon) ++ -" \n" ++ -" \n" ++ -"\n"; -} - -abstract production genJava -top::DriverAction ::= a::Decorated CmdArgs specs::[Decorated RootSpec] silverGen::String -{ - local pr :: IO = print("Generating Translation.\n", top.ioIn); - - top.io = writeAll(pr, a, specs, silverGen); - top.code = 0; - top.order = 4; -} - -abstract production genBuild -top::DriverAction ::= buildFileLocation::String buildXml::String -{ - top.io = writeFile(buildFileLocation, buildXml, top.ioIn); - top.code = 0; - top.order = 6; -} - -function writeAll -IO ::= i::IO a::Decorated CmdArgs l::[Decorated RootSpec] silverGen::String -{ - local now :: IO = writeSpec(i, head(l), silverGen); - local recurse :: IO = writeAll(now, a, tail(l), silverGen); - - return if null(l) then i else recurse; -} - -function writeSpec -IO ::= i::IO r::Decorated RootSpec silverGen::String -{ - local srcPath :: String = silverGen ++ "src/" ++ grammarToPath(r.declaredName); - local binPath :: String = silverGen ++ "bin/" ++ grammarToPath(r.declaredName); - - local mkiotest :: IOVal = - isDirectory(srcPath, i); - local mksrc :: IOVal = - if mkiotest.iovalue then mkiotest else mkdir(srcPath, mkiotest.io); - local clean :: IO = - deleteDirFiles(srcPath, deleteDirFiles(binPath, mksrc.io).io).io; - - local printio :: IO = - if mksrc.iovalue - then print("\t[" ++ r.declaredName ++ "]\n", clean) - else exit(-5, print("\nUnrecoverable Error: Unable to create directory: " ++ srcPath ++ "\nWarning: if some interface file writes were successful, but others not, Silver's temporaries are in an inconsistent state. Use the --clean flag next run.\n\n", mksrc.io)); - - return writeFiles(srcPath, r.genFiles, printio); -} - -{-- - - Given a path (with terminating /) and list of (file names relative to that root, contents), - - write these out. - -} -function writeFiles -IO ::= path::String s::[Pair] i::IO -{ - return if null(s) then i else writeFile(path ++ head(s).fst, head(s).snd, writeFiles(path, tail(s), i)); -} - -function zipfileset -String ::= s::String -{ - return " \n"; -} -function pathLocation -String ::= s::String -{ - return " \n"; -} -function includeJavaFiles -String ::= gram::String -{ - return s" \n"; -} -function includeClassFiles -String ::= gram::Decorated RootSpec -{ - return s" \n"; -} - diff --git a/grammars/silver/translation/java/type/Type.sv b/grammars/silver/translation/java/type/Type.sv deleted file mode 100644 index 0626c6153..000000000 --- a/grammars/silver/translation/java/type/Type.sv +++ /dev/null @@ -1,150 +0,0 @@ -grammar silver:translation:java:type; - -import silver:definition:type; -import silver:translation:java:core only makeNTClassName, makeTerminalName; - --- The Java type corresponding to the Silver Type -synthesized attribute transType :: String; --- Java has crappy syntax for some things. --- If we want to statically refer to the class of this type, we cannot use --- the <> part of the type!! e.g. "Foo.class" is illegal, should be "Foo.class" -synthesized attribute transClassType :: String; --- The runtime representation of a type, used for reification -synthesized attribute transTypeRep :: String; --- The runtime representation of a type, where all skolems are replaced with flexible vars, used for reification -synthesized attribute transFreshTypeRep :: String; - -attribute transType, transClassType, transTypeRep, transFreshTypeRep occurs on Type; - -aspect production varType -top::Type ::= tv::TyVar -{ - top.transType = "Object"; - top.transClassType = "Object"; - top.transTypeRep = s"freshTypeVar_${toString(tv.extractTyVarRep)}"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production skolemType -top::Type ::= tv::TyVar -{ - top.transType = "Object"; - top.transClassType = "Object"; - top.transTypeRep = s"new common.BaseTypeRep(\"b${toString(tv.extractTyVarRep)}\")"; - top.transFreshTypeRep = s"freshTypeVar_${toString(tv.extractTyVarRep)}"; -} - -aspect production errorType -top::Type ::= -{ - local oops :: String = error("Attempting to translate in presence of errors"); - top.transType = oops; - top.transClassType = oops; - top.transTypeRep = oops; - top.transFreshTypeRep = oops; -} - -aspect production intType -top::Type ::= -{ - top.transType = "Integer"; - top.transClassType = "Integer"; - top.transTypeRep = "new common.BaseTypeRep(\"Integer\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production boolType -top::Type ::= -{ - top.transType = "Boolean"; - top.transClassType = "Boolean"; - top.transTypeRep = "new common.BaseTypeRep(\"Boolean\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production floatType -top::Type ::= -{ - top.transType = "Float"; - top.transClassType = "Float"; - top.transTypeRep = "new common.BaseTypeRep(\"Float\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production stringType -top::Type ::= -{ - top.transType = "common.StringCatter"; - top.transClassType = "common.StringCatter"; - top.transTypeRep = "new common.BaseTypeRep(\"String\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production terminalIdType -top::Type ::= -{ - top.transType = "Integer"; - top.transClassType = "Integer"; - top.transTypeRep = "new common.BaseTypeRep(\"TerminalId\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production nonterminalType -top::Type ::= fn::String params::[Type] -{ - -- untightened version would be "common.Node", but we prefer the generated - -- class, e.g. silver.definition.core.NExpr - top.transType = makeNTClassName(fn); - top.transClassType = top.transType; - top.transTypeRep = - s"new common.BaseTypeRep(\"${fn}\", new common.TypeRep[] {${implode(", ", map((.transTypeRep), params))}})"; - top.transFreshTypeRep = - s"new common.BaseTypeRep(\"${fn}\", new common.TypeRep[] {${implode(", ", map((.transFreshTypeRep), params))}})"; -} - -aspect production terminalType -top::Type ::= fn::String -{ - top.transType = makeTerminalName(fn); - top.transClassType = makeTerminalName(fn); - top.transTypeRep = s"new common.BaseTypeRep(\"${fn}\")"; - top.transFreshTypeRep = top.transTypeRep; -} - -aspect production decoratedType -top::Type ::= te::Type -{ - -- TODO: this should probably be a generic. e.g. "DecoratedNode" - top.transType = "common.DecoratedNode"; - top.transClassType = "common.DecoratedNode"; - top.transTypeRep = - case te of - nonterminalType(fn, params) -> - s"new common.BaseTypeRep(\"Decorated ${fn}\", new common.TypeRep[] {${implode(", ", map((.transTypeRep), params))}})" - | _ -> error("Found decoratedType that does not wrap nonterminalType!") - end; - top.transFreshTypeRep = - case te of - nonterminalType(fn, params) -> - s"new common.BaseTypeRep(\"Decorated ${fn}\", new common.TypeRep[] {${implode(", ", map((.transFreshTypeRep), params))}})" - | _ -> error("Found decoratedType that does not wrap nonterminalType!") - end; -} - -aspect production functionType -top::Type ::= out::Type params::[Type] namedParams::[NamedArgType] -{ - top.transType = "common.NodeFactory<" ++ out.transType ++ ">"; - top.transClassType = "common.NodeFactory"; - top.transTypeRep = - s"new common.FunctionTypeRep(${out.transTypeRep}, " ++ - s"new common.TypeRep[] {${implode(", ", map((.transTypeRep), params))}}, " ++ - s"new String[] {${implode(", ", map(\ nat::NamedArgType -> s"\"${nat.argName}\"", namedParams))}}, " ++ - s"new common.TypeRep[] {${implode(", ", map((.transTypeRep), map((.argType), namedParams)))}})"; - top.transFreshTypeRep = - s"new common.FunctionTypeRep(${out.transFreshTypeRep}, " ++ - s"new common.TypeRep[] {${implode(", ", map((.transFreshTypeRep), params))}}, " ++ - s"new String[] {${implode(", ", map(\ nat::NamedArgType -> s"\"${nat.argName}\"", namedParams))}}, " ++ - s"new common.TypeRep[] {${implode(", ", map((.transFreshTypeRep), map((.argType), namedParams)))}})"; -} - diff --git a/grammars/silver/util/DocConfig.sv b/grammars/silver/util/DocConfig.sv deleted file mode 100644 index fa49a472c..000000000 --- a/grammars/silver/util/DocConfig.sv +++ /dev/null @@ -1,6 +0,0 @@ -grammar silver:util; - -{@config - header:"---\nlayout: sv_wiki\ntitle: Utilities\nmenu_title: Util\nmenu_weight: 100\n---" -@} - diff --git a/grammars/silver/util/Utils.sv b/grammars/silver/util/Utils.sv deleted file mode 100644 index 5e9cc6e36..000000000 --- a/grammars/silver/util/Utils.sv +++ /dev/null @@ -1,78 +0,0 @@ -grammar silver:util; - -function contains -Boolean ::= s::String sl::[String] -{ - return (!null(sl)) && (s == head(sl) || contains(s, tail(sl))); -} - -function containsSet -Boolean ::= s::[String] sl::[[String]] -{ - return (!null(sl)) && (equals(s, head(sl)) || containsSet(s, tail(sl))); -} - -function containsDuplicates -Boolean ::= s::[String] -{ - return (!null(s)) && (contains(head(s),tail(s)) || containsDuplicates(tail(s))); -} - -function equals -Boolean ::= s1::[String] s2::[String] -{ - return length(s1) == length(s2) && containsAll(s1, s2); -} - --- all of s1 in s2? -function containsAll -Boolean ::= s1::[String] s2::[String] -{ - return null(s1) || (contains(head(s1), s2) && containsAll(tail(s1), s2)); -} - --- any of s1 in s2? -function containsAny -Boolean ::= s1::[String] s2::[String] -{ - return !null(s1) && (contains(head(s1), s2) || containsAny(tail(s1), s2)); -} - ---takes in a list of strings and returns a set of strings. -function makeSet -[String] ::= list::[String] -{ - local attribute recurse :: [String]; - recurse = makeSet(tail(list)); - - return if null(list) then [] - else if contains(head(list), recurse) - then recurse - else cons(head(list), recurse); -} - ---removes the strings of the first list that appear in the second list. returns the filtered list. -function rem -[String] ::= n::[String] seen::[String] --result = n - seem; -{ - return if null(n) then [] - else if contains(head(n), seen) - then rem(tail(n), seen) - else cons(head(n), rem(tail(n), seen)); -} - -function remove -[String] ::= n::String s::[String] -{ - return if null(s) - then [] - else if n == head(s) - then remove(n, tail(s)) - else [head(s)] ++ remove(n, tail(s)); -} - -function startsWithAny -Boolean ::= pre::[String] s::String{ - return !null(pre) && (startsWith(head(pre), s) || startsWithAny(tail(pre), s)); -} - diff --git a/grammars/silver/util/cmdargs/CmdArgs.sv b/grammars/silver/util/cmdargs/CmdArgs.sv index 6c431468c..d0510f9f4 100644 --- a/grammars/silver/util/cmdargs/CmdArgs.sv +++ b/grammars/silver/util/cmdargs/CmdArgs.sv @@ -1,32 +1,144 @@ grammar silver:util:cmdargs; + +@{- + - The type of lists of arguments given on the command line + - + - Define a new production (as a cons cell for the CmdArgs list) for each flag given on the command line: + - - For a flag that takes no arguments on the command line, define a production of type (CmdArgs ::= CmdArgs) + - - For a flag that takes a single argument, define a production of type (CmdArgs ::= String CmdArgs) + - - For a flag that takes more arguments, define a prodution of type (CmdArgs ::= [String] CmdArgs) + -} nonterminal CmdArgs with cmdRemaining, cmdError; +@{- + - The remaining arguments from the commandline after parsing the flags + -} synthesized attribute cmdRemaining :: [String]; +@{- + - The errors from parsing commandline arguments + -} synthesized attribute cmdError :: Maybe; +@{- + - A specification of a flag for @link[interpretCmdArgs]. + - + - This uses the annotations-as-record pattern; when a proper record extension + - is merged, this should use that instead. + - + - The `name` is the string that should be present in the arguments list in + - order for this flag to be recognized. In an example `-o` flag, it would be + - `"-o"`. + - + - The `paramString` is a string describing the parameters the flag takes, for + - use in the help text. In our running example, this might be + - `just("")`. For flags that don't take an argument, this should be + - `nothing()`. + - + - The `help` is a string describing the usage of the flag. Typically, this is + - an English-language string that does *not* start with a capital letter, nor + - end with a period. Often the imperative voice is used (e.g. "place the + - output into " rather than "the output file is "). + - + - The `flagParser` is the `Flag` value used to handle the flag. + - + - Putting these together, the `FlagSpec` for our example `-o` flag might look + - something like: + - + - ```silver + - flagSpec(name="-o", paramString=just(""), + - help="place the output into ", flagParser=option(outFlag)) + - ``` + -} +nonterminal FlagSpec with name, paramString, help, flagParser; + +annotation name::String; +annotation paramString::Maybe; +annotation help::String; +annotation flagParser::Flag; + +@{- The constructor of FlagSpec values. -} +production flagSpec +this::FlagSpec ::= +{} + +@{- + - Produce a parsed list of arguments using the given flags from the given input + - + - @param flags A list of FlagSpecs + - @param input A list of strings, generally the commandline arguments + - @return The parsed list of arguments + - @warning The flag names in the flags parameter MUST start with a hyphen + -} function interpretCmdArgs -CmdArgs ::= flags::[Pair] input::[String] +CmdArgs ::= flags::[FlagSpec] input::[String] { - local attribute l :: Maybe; - l = lookupBy(stringEq, head(input), flags); - - local attribute here :: Flag; - here = l.fromJust; - here.flagInput = input; - here.flagOriginal = interpretCmdArgs(flags, here.flagOutput); + local matchingFlagSpec::Maybe = lookup( + head(input), + map(\flag::FlagSpec -> (flag.name, flag), flags)); + + -- Partial! Only demand if matchingFlagSpec.isJust + local matchingFlag::Flag = matchingFlagSpec.fromJust.flagParser; + matchingFlag.flagInput = input; + matchingFlag.flagOriginal = interpretCmdArgs(flags, matchingFlag.flagOutput); return if null(input) then endCmdArgs([]) - else if !l.isJust + else if !matchingFlagSpec.isJust then if startsWith("-", head(input)) then errorCmdArgs("Unrecognized flag: " ++ head(input)) else endCmdArgs(input) - else here.flagModified; + else matchingFlag.flagModified; } -{-- - - For defining base, default values for any attributes on CmdArgs - -} +@{- Formats the --help text for the given FlagSpecs. -} +function flagSpecsToHelpText +String ::= flagSpecs::[FlagSpec] +{ + -- Output looks like (where _ = space, >>>> = tab): + -- + -- __--flag1________>>>>flag1 help + -- __--flag2 _>>>>flag2 help + -- \____________/\___/\________/ + -- \ \ \ + -- flag part pad part help part + + local unpaddedFlagParts::[(String, FlagSpec)] = + map(\flagSpec::FlagSpec -> + ( flagSpec.name ++ mapOrElse("", + \paramString::String -> + " " ++ paramString, + flagSpec.paramString) + , flagSpec + ), + sortByKey(\flagSpec::FlagSpec -> flagSpec.name, + flagSpecs)); + local longestUnpaddedFlagPart::Integer = + foldl(max, 0, + map(\flagPartItem::(String, FlagSpec) -> length(flagPartItem.1), + unpaddedFlagParts)); + local lines::[String] = + map(\flagPartItem::(String, FlagSpec) -> + -- Leading space + " " ++ + -- Basic flag part + flagPartItem.1 ++ + -- Flag part padding + replicate(longestUnpaddedFlagPart - length(flagPartItem.1), " ") ++ + -- Pad part + " \t" ++ + -- Help part + flagPartItem.2.help ++ + -- Trailing newline + "\n", + unpaddedFlagParts); + return implode("", lines); +} + +@{-- + - For defining base, default values for any attributes on CmdArgs + - + - @param remaining Commandline arguments remaining after parsing flags + -} abstract production endCmdArgs top::CmdArgs ::= remaining::[String] { @@ -34,10 +146,12 @@ top::CmdArgs ::= remaining::[String] top.cmdError = nothing(); } -{-- - - Only used when an error is encountered attempting to parse an option. - - One should always check for .cmdError.isJust BEFORE accessing any other attributes. - -} +@{-- + - Only used when an error is encountered attempting to parse an option. + - One should always check for .cmdError.isJust BEFORE accessing any other attributes. + - + - @param errmsg Error message + -} abstract production errorCmdArgs top::CmdArgs ::= errmsg::String { @@ -46,10 +160,10 @@ top::CmdArgs ::= errmsg::String forwards to endCmdArgs([]); -- Well, this is an abuse, but this whole thing is an abuse, really. } -{-- - - Flags are a representation of what to do with command line flags/options. - - It should not be necessary to define any new flags. - -} +@{-- + - Flags are a representation of what to do with command line flags/options. + - It should not be necessary to define any new flags. + -} nonterminal Flag with flagInput, flagOutput, flagOriginal, flagModified; inherited attribute flagInput::[String]; @@ -57,9 +171,11 @@ synthesized attribute flagOutput::[String]; inherited attribute flagOriginal::CmdArgs; synthesized attribute flagModified::CmdArgs; -{-- +@{-- - In the terminology I've just made up, a 'flag' is a cmd line option - with no parameters. + - + - @param ast Production for handling this commandline option being given -} abstract production flag top::Flag ::= ast::(CmdArgs ::= CmdArgs) @@ -68,9 +184,11 @@ top::Flag ::= ast::(CmdArgs ::= CmdArgs) top.flagModified = ast(top.flagOriginal); } -{-- +@{-- - In the terminology I've just made up, an 'option' is a cmd line option - with one, single parameter. + - + - @param ast Production for handling this commandline option being given -} abstract production option top::Flag ::= ast::(CmdArgs ::= String CmdArgs) @@ -81,16 +199,26 @@ top::Flag ::= ast::(CmdArgs ::= String CmdArgs) else ast(head(tail(top.flagInput)), top.flagOriginal); } -{-- +@{-- - In the terminology I've just made up, 'nOptions' is a cmd line option - with n parameters. + - + - @param n The number of arguments expected by the flag + - @param ast Production for handling this commandline option being given -} abstract production nOptions top::Flag ::= n::Integer ast::(CmdArgs ::= [String] CmdArgs) { - top.flagOutput = if length(top.flagInput) <= n then [] else drop(n, top.flagInput); - top.flagModified = if length(top.flagInput) <= n - then errorCmdArgs(head(top.flagInput) ++ " only has " ++ toString(length(top.flagInput)-1) ++ " parameters, when it should have " ++ toString(n)) - else ast(take(n, top.flagInput), top.flagOriginal); + top.flagOutput = + if length(top.flagInput) <= n + 1 + then [] + else drop(n + 1, top.flagInput); + top.flagModified = + if length(top.flagInput) < n + 1 + then errorCmdArgs(head(top.flagInput) ++ " only has " ++ + toString(length(top.flagInput)) ++ + " parameters, when it should have " ++ + toString(n)) + else ast(take(n, drop(1, top.flagInput)), top.flagOriginal); } diff --git a/grammars/silver/util/command/Command.sv b/grammars/silver/util/command/Command.sv deleted file mode 100644 index 1a62a5064..000000000 --- a/grammars/silver/util/command/Command.sv +++ /dev/null @@ -1,148 +0,0 @@ -grammar silver:util:command; -import silver:util; - -terminal Flag_t /[\-]([\-]?)[a-zA-Z0-9\-]+/ dominates {WhiteSpace_t}; -terminal Chunk_t /[^\-\ ][^\ ]*/ dominates {WhiteSpace_t}; -ignore terminal WhiteSpace_t /[\ \t]+/; - -nonterminal Command with flags, grammarName, okay, usage, flag_usage; -nonterminal PieceList with flags, lookups, needChunk, nextChunk, lastChunk, grammarName, hasChunk; - -synthesized attribute okay :: Boolean; -synthesized attribute usage :: String; - --- EVW: I'm adding this so that we can re use the command line grammar for other --- languages and build custom error messages (e.g. that don't include "GrammarName") -synthesized attribute flag_usage :: String; - -autocopy attribute lookups :: [Flag]; -inherited attribute needChunk :: Boolean; -synthesized attribute nextChunk :: String; -synthesized attribute lastChunk :: Boolean; - -synthesized attribute grammarName :: String; - -synthesized attribute flags :: [Flag]; - ---abstract production main ---top::Main ::= args::String{ --- local attribute p :: Command; --- p = parse(args); - --- top.ioOut = if !p.okay then print(p.usage, top.ioIn) else print(parse(args).grammarName ++ "\n" ++ printf(parse(args).flags) ++ "\n\n", top.ioIn); ---} - -concrete production cRootAll -top::Command ::= c1::PieceList -{ - production attribute uses :: [String] with ++; - uses := []; - - production attribute flagLookups :: [Flag] with ++; - flagLookups := []; - - - top.flags = c1.flags; - top.grammarName = c1.grammarName; - top.usage = "\nsilver [Options] GrammarName\n" ++ implode("", uses) ++ "\n\n"; - top.flag_usage = implode("", uses) ++ "\n\n"; - - top.okay = isOkay(flagLookups, c1.flags); - - c1.lookups = flagLookups; - - c1.needChunk = false ; -} - -function isOkay -Boolean ::= exp::[Flag] fnd::[Flag]{ - - local attribute fl :: [Flag]; - fl = findFlag(head(fnd).flag, exp); - - return if null(fnd) then true else !null(fl) && (head(fnd).hasChunk || !head(fl).hasChunk) && isOkay(exp, tail(fnd)); -} - -concrete production cPieceNone -top::PieceList ::= -{ - top.grammarName = "Not:Defined"; - top.lastChunk = true; - top.hasChunk = false; - top.flags = []; -} - -concrete production cFlag -top::PieceList ::= c1::Flag_t c2::PieceList -{ - top.grammarName = c2.grammarName; - - top.hasChunk = false; - top.lastChunk = false; - top.nextChunk = ""; - - local attribute fl :: [Flag]; - fl = findFlag(c1.lexeme, top.lookups); - - local attribute nChunk :: Boolean; - nChunk = !null(fl) && head(fl).hasChunk; - - c2.needChunk = nChunk; - - top.flags = [flagDef(c1.lexeme, if nChunk then c2.nextChunk else "", nChunk && c2.hasChunk)] ++ c2.flags; -} - -concrete production cChunk -top::PieceList ::= c1::Chunk_t c2::PieceList -{ - top.hasChunk = true; - top.lastChunk = false; - top.nextChunk = c1.lexeme; - - c2.needChunk = false; - - top.flags = c2.flags; - top.grammarName = if !top.needChunk && c2.lastChunk then c1.lexeme else c2.grammarName; -} - -function printf -String ::= l::[Flag]{ - return if null(l) then "\n" else head(l).flag ++ " " ++ head(l).chunk ++ "\n" ++ printf(tail(l)); -} - -nonterminal Flag with flag, chunk, hasChunk; -synthesized attribute flag :: String; -synthesized attribute chunk :: String; -synthesized attribute hasChunk :: Boolean; - -abstract production flagDef -top::Flag ::= f::String c::String b::Boolean{ - top.flag = f; - top.chunk = c; - top.hasChunk = b; -} - -abstract production flagLookup -top::Flag ::= f::String b::Boolean { - top.flag = f; - top.chunk = ""; - top.hasChunk = b; -} - - -function findFlag -[Flag] ::= f::String l::[Flag]{ - return if null(l) then [] else (if head(l).flag == f then [head(l)] else []) ++ findFlag(f, tail(l)); -} - -function foldf -String ::= s::String c1::[Flag] -{ - return if null(c1) then "" else head(c1).chunk ++ (if null(tail(c1)) then "" else s) ++ foldf(s, tail(c1)); -} - -function getFlagChunk -String ::= f::Flag -{ - return f.chunk; -} diff --git a/grammars/silver/util/deque/Deque.sv b/grammars/silver/util/deque/Deque.sv index 2e705df5e..3ad84bcff 100644 --- a/grammars/silver/util/deque/Deque.sv +++ b/grammars/silver/util/deque/Deque.sv @@ -1,5 +1,7 @@ grammar silver:util:deque; +import silver:core with reverse as listReverse; + -- This is all based on Okasaki 1998. nonterminal Deque; @@ -8,77 +10,81 @@ abstract production deque top::Deque ::= ln::Integer l::[a] rn::Integer r::[a] {} -function dqEmpty +function empty Deque ::= { return deque(0, [], 0, []); } -function dqCons +function cons Deque ::= e::a q::Deque { - return case q of deque(ln,l,rn,r) -> dqCheck(ln+1,e::l,rn,r) end; + return case q of deque(ln,l,rn,r) -> check(ln+1,e::l,rn,r) end; } -function dqHead +function head a ::= q::Deque { return case q of | deque(_,[],_,x::_) -> x -- single element | deque(_,x::_,_,_) -> x + | _ -> error("Requested head of empty deque") end; } -function dqTail +function tail Deque ::= q::Deque { return case q of - | deque(_,[],_,_::_) -> dqEmpty() -- single element - | deque(ln,lh::lt,rn,r) -> dqCheck(ln-1, lt, rn, r) + | deque(_,[],_,_::_) -> empty() -- single element + | deque(ln,lh::lt,rn,r) -> check(ln-1, lt, rn, r) + | _ -> error("Requested tail of empty deque") end; } -function dqSnoc +function snoc Deque ::= q::Deque e::a { - return case q of deque(ln,l,rn,r) -> dqCheck(ln,l,rn+1,e::r) end; + return case q of deque(ln,l,rn,r) -> check(ln,l,rn+1,e::r) end; } -function dqLast +function last a ::= q::Deque { return case q of | deque(_,x::_,_,[]) -> x -- single element | deque(_,_,_,x::_) -> x + | _ -> error("Requested last of empty deque") end; } -function dqInit +function init Deque ::= q::Deque { return case q of - | deque(_,_::_,_,[]) -> dqEmpty() -- single element - | deque(ln,l,rn,rh::rt) -> dqCheck(ln, l, rn-1, rt) + | deque(_,_::_,_,[]) -> empty() -- single element + | deque(ln,l,rn,rh::rt) -> check(ln, l, rn-1, rt) + | _ -> error("Requested init of empty deque") end; } -function dqIsEmpty +function isEmpty Boolean ::= q::Deque { return case q of deque(ln,_,rn,_) -> ln == 0 && rn == 0 end; } -function dqReverse +function reverse Deque ::= q::Deque { return case q of deque(ln,l,rn,r) -> deque(rn,r,ln,l) end; } -function dqCheck +function check Deque ::= lenf::Integer f::[a] lenr::Integer r::[a] { local mid::Integer = (lenf + lenr) / 2; local rest::Integer = lenf + lenr - mid; return if lenf > 2 * lenr + 1 then let fprime ::[a]= take(mid,f), - rprime ::[a] = r ++ reverse(drop(mid,f)) + rprime ::[a] = r ++ listReverse(drop(mid,f)) in deque(mid, fprime, rest, rprime) end else if lenr > 2*lenf + 1 then let rprime ::[a]= take(mid, r), - fprime ::[a]= f ++ reverse(drop(mid, r)) + fprime ::[a]= f ++ listReverse(drop(mid, r)) in deque(rest, fprime, mid, rprime) end else deque(lenf, f, lenr, r); } diff --git a/grammars/silver/util/fixedmap/FixedMap.sv b/grammars/silver/util/fixedmap/FixedMap.sv deleted file mode 100644 index b948459ea..000000000 --- a/grammars/silver/util/fixedmap/FixedMap.sv +++ /dev/null @@ -1,114 +0,0 @@ -grammar silver:util:fixedmap; - --- One should always import this via 'import silver:util:fixedmap as ...' --- The names are too general otherwise. - -{-- - - Looks up an element from the map, empty list if not contained. - -} -function lookup -[b] ::= key::String mp::Map -{ - mp.treeKey = key; - return mp.treeLookup; -} - -{-- - - Creates a map from a list of pairs - -} -function create -Map ::= lst::[Pair] -{ - return internalBuild(groupBy(fstStringEq, sortBy(fstStringLte, lst))); -} - -{-- - - Converts a multimap back to a list of pairs, in sorted order by key. - -} -function toList -[Pair] ::= mp::Map -{ - return mp.treeToList; -} - - -nonterminal Map with treeKey, treeLookup, treeToList; - -inherited attribute treeKey :: a; -synthesized attribute treeLookup :: [b]; -synthesized attribute treeToList :: [Pair]; - -{-- - - Returns a new, empty, string multimap. - -} -abstract production empty -top::Map ::= -{ - top.treeLookup = []; - top.treeToList = []; -} - -abstract production node -top::Map ::= label::String values::[b] l::Map r::Map -{ - l.treeKey = top.treeKey; - r.treeKey = top.treeKey; - top.treeLookup = if top.treeKey <= label - then if top.treeKey == label - then values - else l.treeLookup - else r.treeLookup; - - top.treeToList = l.treeToList ++ treeMapKeyValues(label, values) ++ r.treeToList; -} - --- internal stuffs - -function treeMapKeyValues -[Pair] ::= k::a v::[b] -{ - return if null(v) then [] else pair(k, head(v)) :: treeMapKeyValues(k, tail(v)); -} - -function fstStringLte -Boolean ::= l::Pair r::Pair -{ - return l.fst <= r.fst; -} -function fstStringEq -Boolean ::= l::Pair r::Pair -{ - return l.fst == r.fst; -} - -function internalBuild -Map ::= collected::[[Pair]] -{ - return internalBuildHelp(collected, length(collected)); -} - -function internalBuildHelp -Map ::= collected::[[Pair]] upTo::Integer -{ - return if upTo == 0 then empty() - else if upTo == 1 - then node(head(head(collected)).fst, map(getSnd, head(collected)), empty(), empty()) - else node(head(head(right_list)).fst, map(getSnd, head(right_list)), ltree, rtree); - - local attribute ltree :: Map; - ltree = internalBuildHelp(collected, middle); - - local attribute rtree :: Map; - rtree = internalBuildHelp(tail(right_list), upTo - middle - 1); -- note the tail - - -- this in fact includes the current as well as the right side. - local attribute right_list :: [[Pair]]; - right_list = drop(middle,collected); - - local attribute middle :: Integer; - middle = toInteger(toFloat(upTo) / 2.0); -} - -function getSnd -b ::= l::Pair -{ return l.snd; } diff --git a/grammars/silver/util/graph/Graph.sv b/grammars/silver/util/graph/Graph.sv new file mode 100644 index 000000000..22e565cdf --- /dev/null +++ b/grammars/silver/util/graph/Graph.sv @@ -0,0 +1,97 @@ +grammar silver:util:graph; + +import silver:util:treeset as set; + +@{-- + - A primitive graph representation. Edges has no special value + - they either exist or do not. + -} +type Graph foreign = "java.util.TreeMap>"; + +@{-- + - Returns an empty graph using Ord for comparison. + -} +function empty +Ord a => Graph ::= +{ + return emptyWith(compare); +} + +@{-- + - Returns an empty graph using the specified vertex comparator. + -} +function emptyWith +Graph ::= comparator::(Integer ::= a a) +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.empty(%comparator%)"; +} + +@{-- + - Adds a list of edges to the map + -} +function add +Graph ::= lst::[Pair] graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.add(%lst%, %graph%)"; +} + +@{-- + - Returns a set of edges FROM a particular vertex + -} +function edgesFrom +set:Set ::= vertex::a graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.edgesFrom(%vertex%, %graph%)"; +} + +@{-- + - Determines whether an edge already exists in the graph. + -} +function contains +Boolean ::= edge::Pair graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.contains(%edge%, %graph%)"; +} + +@{-- + - Returns the list of edges that make up the graph. + -} +function toList +[Pair] ::= graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.toList(%graph%)"; +} + +@{-- + - Returns the transitiveClosure of a graph + -} +function transitiveClosure +Graph ::= graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.transitiveClosure(%graph%)"; +} + +@{-- + - Assumes graph is already a transitive closure, + - adds the new edges, and repair the transitive closure. + -} +function repairClosure +Graph ::= lst::[Pair] graph::Graph +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawGraph.repairClosure(%lst%, %graph%)"; +} + diff --git a/grammars/silver/util/random/Arbitrary.sv b/grammars/silver/util/random/Arbitrary.sv new file mode 100644 index 000000000..3cdd27ce3 --- /dev/null +++ b/grammars/silver/util/random/Arbitrary.sv @@ -0,0 +1,53 @@ +grammar silver:util:random; + +-- TODO: Consider adding a Shrink type class for use in conjuction with Arbitrary in randomized testing. +-- See https://hackage.haskell.org/package/QuickCheck-2.14.2/docs/Test-QuickCheck.html#v:shrink + +@{-- + - Represents primitive (terminal) types for which arbitrary random values can be generated. + -} +class Arbitrary a { + genArb :: (RandomGen ::= Integer); +} + +instance Arbitrary Integer { + genArb = \ Integer -> randomRange(-100, 100); -- TODO: Is this a reasonable default? Revisit. +} + +instance Arbitrary Float { + genArb = \ Integer -> randomRange(-10.0, 10.0); -- TODO: Is this a reasonable default? Revisit. +} + +instance Arbitrary Boolean { + genArb = \ Integer -> random; +} + +instance Arbitrary String { + -- TODO: Is this a reasonable default? Revisit. + genArb = \ depth::Integer -> do { + let chars :: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789`~!@#$%^&*()_-+=\\|[]{}/?'\";:,.<>\n\n\n\t\r "; + randChars :: [Integer] <- randomShuffle(concat(repeat(stringToChars(chars), depth / 3))); + len :: Integer <- randomRange(0, max(depth, 1) * 5); + return charsToString(take(len, randChars)); + }; +} + +instance Arbitrary Location { + genArb = \ depth::Integer -> pure(txtLoc("arbitrary at depth " ++ toString(depth))); +} + +instance Arbitrary a, Arbitrary b => Arbitrary Pair { + genArb = \ depth::Integer -> lift2(pair, genArb(depth), genArb(depth)); +} + +instance Arbitrary a, Arbitrary b => Arbitrary Either { + genArb = \ depth::Integer -> + bind(random, \ b::Boolean -> if b then map(left, genArb(depth)) else map(right, genArb(depth))); +} + +instance Arbitrary a => Arbitrary [a] { + genArb = \ depth::Integer -> + if depth > 0 + then lift2(cons, genArb(depth), genArb(depth - 1)) + else pure([]); +} diff --git a/grammars/silver/util/random/Random.sv b/grammars/silver/util/random/Random.sv new file mode 100644 index 000000000..f61387d1f --- /dev/null +++ b/grammars/silver/util/random/Random.sv @@ -0,0 +1,68 @@ +grammar silver:util:random; + +@{- +Types for which random values can be generated. + +This class imposes no restrictions on the range or distribution of random values, +as there may be instances for types with no notion of ordering/equality. +-} +class Random a { + random :: RandomGen; + randomT :: ((a, RandomToken) ::= RandomToken) = runTokenRandomGen(_, random); +} + +-- Uniform random integer on [INT_MIN, INT_MAX] +instance Random Integer { + random = randomInteger(); + randomT = randomTInteger; +} + +-- Uniform random float on [0.0, 1.0) +instance Random Float { + random = randomFloat(); + randomT = randomTFloat; +} + +-- 50/50 true or false +instance Random Boolean { + random = randomBoolean(); + randomT = randomTBoolean; +} + +@{- +Types for which random values can be generated, uniformly distributed over some closed range [min, max]. + +Note that this is not a subclass of Ord since we may have instances for partial orders. + +If a has an instance for Ord, then instances should satisfy: + runRandomGen(randomRange(min, max)) >= min + runRandomGen(randomRange(min, max)) <= max +-} +class Random a => RandomRange a { + randomRange :: (RandomGen ::= a a); + randomRangeT :: ((a, RandomToken) ::= a a RandomToken) = + \ min::a max::a token::RandomToken -> runTokenRandomGen(token, randomRange(min, max)); +} + +instance RandomRange Integer { + -- Uniform integer in range is actually kinda tricky to do correctly, so this is a primitive + randomRange = randomRangeInteger; + randomRangeT = randomRangeTInteger; +} + +-- Does not allow for generating NaN or infinities, at the moment +instance RandomRange Float { + randomRange = \ min::Float max::Float -> + if min > max then error(s"Empty Float range [${toString(min)}, ${toString(max)}]") + else map(\ f::Float -> f * (max - min) + min, random); +} + +instance RandomRange Boolean { + randomRange = \ min::Boolean max::Boolean -> + case min, max of + | false, false -> pure(false) + | false, true -> random + | true, false -> error("Empty Boolean range") + | true, true -> pure(true) + end; +} diff --git a/grammars/silver/util/random/RandomMonad.sv b/grammars/silver/util/random/RandomMonad.sv new file mode 100644 index 000000000..ef2177309 --- /dev/null +++ b/grammars/silver/util/random/RandomMonad.sv @@ -0,0 +1,59 @@ +grammar silver:util:random; + +@{- + Run a RandomGen computation, using an arbitrary seed. + + @param r The computation to run + @param ioIn The IO token + @return An IOVal containing the result of the computation +-} +function runRandomGenT +IOVal ::= r::RandomGen ioIn::IOToken +{ + return error("foreign function"); +} foreign { + "java": return "%ioIn%.runRandomGen(originCtx, %r%)"; +} + +@{- + Run a RandomGen computation, using an arbitrary seed (IO monadic version.) + + @param r The computation to run +-} +production runRandomGen +top::IO ::= r::RandomGen +{ + local res::IOVal = runRandomGenT(r, top.stateIn); + top.stateOut = res.io; + top.stateVal = res.iovalue; +} + +@{- + Run a RandomGen computation, using an arbitrary seed. + + @param seed The initial seed value for the random number generator + @param r The computation to run + @return The result of the computation +-} +function runSeedRandomGen +a ::= seed::Integer r::RandomGen +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.runRandomGen(originCtx, %seed%, %r%)"; +} + +@{- + Run a RandomGen computation, using a random token. + + @param token The random number generator to use + @param r The computation to run + @return The result of the computation +-} +function runTokenRandomGen +(a, RandomToken) ::= token::RandomToken r::RandomGen +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.evalRandomTokenOp(%token%, (rng) -> common.RandomGen.runRandomGen(originCtx, rng, %r%))"; +} diff --git a/grammars/silver/util/random/RandomToken.sv b/grammars/silver/util/random/RandomToken.sv new file mode 100644 index 000000000..670f6f650 --- /dev/null +++ b/grammars/silver/util/random/RandomToken.sv @@ -0,0 +1,43 @@ +grammar silver:util:random; + +@{-- +Monadic action to generate a RandomToken in the RandomGen Monad. +-} +global randomToken::RandomGen = randomToken_(); + +@{-- +Utility attribute pair to thread a random token through an abstract syntax tree. +-} +threaded attribute randomIn, randomOut :: RandomToken; + +function randomTInteger +(Integer, RandomToken) ::= token::RandomToken +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.evalRandomTokenOp(%token%, java.util.Random::nextInt)"; +} + +function randomTFloat +(Float, RandomToken) ::= token::RandomToken +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.evalRandomTokenOp(%token%, java.util.Random::nextFloat)"; +} + +function randomTBoolean +(Boolean, RandomToken) ::= token::RandomToken +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.evalRandomTokenOp(%token%, java.util.Random::nextBoolean)"; +} + +function randomRangeTInteger +(Integer, RandomToken) ::= min::Integer max::Integer token::RandomToken +{ + return error("foreign function"); +} foreign { + "java": return "common.RandomGen.evalRandomTokenOp(%token%, (rng) -> common.RandomGen.randomRangeInteger(%min%, %max%, rng))"; +} diff --git a/grammars/silver/util/random/Util.sv b/grammars/silver/util/random/Util.sv new file mode 100644 index 000000000..ad26e5246 --- /dev/null +++ b/grammars/silver/util/random/Util.sv @@ -0,0 +1,88 @@ +grammar silver:util:random; + +@{-- +Randomly shuffle the elements of a list. + +@param elems The list to shuffle. +@return A RandomGen monadic action to shuffle the list. +-} +function randomShuffle +RandomGen<[a]> ::= elems::[a] +{ + return + if null(elems) then pure([]) + else do { + i :: Integer <- randomRange(0, length(elems) - 1); + let hd :: [a] = take(i, elems); + let tl :: [a] = drop(i, elems); + rest :: [a] <- randomShuffle(hd ++ tail(tl)); + return head(tl) :: rest; + }; +} + +@{-- +Select a random element from a non-empty list. +An error is raised when the list is empty. + +@param elems The list from which to select an element. +@return A RandomGen monadic action to select an element from the list. +-} +function randomElem +RandomGen ::= elems::[a] +{ + return if null(elems) then error("randomElem of empty list!") else + do { + i :: Integer <- randomRange(0, length(elems) - 1); + return head(drop(i, elems)); + }; +} + + +@{-- +Utility for creating a random value using the token-based random library. +Example: + production foo::RandomVal = randomVal(); + thread randomIn, randomOut on top, foo, top; + local fooVal::Integer = foo.randomValue; +-} +nonterminal RandomVal with randomIn, randomOut, randomValue; + +synthesized attribute randomValue::a; + +@{-- +Create a random value using the default Random instance for the type. +-} +production randomVal +Random a => top::RandomVal ::= +{ + production result::(a, RandomToken) = randomT(top.randomIn); + top.randomValue = result.1; + top.randomOut = result.2; +} + +@{-- +Create a random value using the default RandomRange instance for the type. + +@param min The minimum bound for the random value. +@param max The maximum bound for the random value. +-} +production randomRangeVal +RandomRange a => top::RandomVal ::= min::a max::a +{ + production result::(a, RandomToken) = randomRangeT(min, max, top.randomIn); + top.randomValue = result.1; + top.randomOut = result.2; +} + +@{-- +Create a random value using a monadic RandomGen action. + +@param g The random generator to use to generate a value. +-} +production randomGenVal +top::RandomVal ::= g::RandomGen +{ + production result::(a, RandomToken) = runTokenRandomGen(top.randomIn, g); + top.randomValue = result.1; + top.randomOut = result.2; +} diff --git a/grammars/silver/util/raw/graph/Graph.sv b/grammars/silver/util/raw/graph/Graph.sv deleted file mode 100644 index fcf511445..000000000 --- a/grammars/silver/util/raw/graph/Graph.sv +++ /dev/null @@ -1,89 +0,0 @@ -grammar silver:util:raw:graph; - -import silver:util:raw:treeset as set; - -{-- - - A primitive graph representation. Edges has no special value - - they either exist or do not. - -} -type Graph foreign; - - -{-- - - Returns an empty graph using the specified vertex comparator. - -} -function empty -Graph ::= comparator::(Integer ::= a a) -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.empty(%comparator%)"; -} - -{-- - - Adds a list of edges to the map - -} -function add -Graph ::= lst::[Pair] graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.add(%lst%, (java.util.TreeMap>)%graph%)"; -} - -{-- - - Returns a set of edges FROM a particular vertex - -} -function edgesFrom -set:Set ::= vertex::a graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.edgesFrom(%vertex%, (java.util.TreeMap>)%graph%)"; -} - -{-- - - Determines whether an edge already exists in the graph. - -} -function contains -Boolean ::= edge::Pair graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.contains(%edge%, (java.util.TreeMap>)%graph%)"; -} - -{-- - - Returns the list of edges that make up the graph. - -} -function toList -[Pair] ::= graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.toList((java.util.TreeMap>)%graph%)"; -} - -{-- - - Returns the transitiveClosure of a graph - -} -function transitiveClosure -Graph ::= graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.transitiveClosure((java.util.TreeMap>)%graph%)"; -} - -{-- - - Assumes graph is already a transitive closure, - - adds the new edges, and repair the transitive closure. - -} -function repairClosure -Graph ::= lst::[Pair] graph::Graph -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawGraph.repairClosure(%lst%, (java.util.TreeMap>)%graph%)"; -} - diff --git a/grammars/silver/util/raw/treemap/TreeMap.sv b/grammars/silver/util/raw/treemap/TreeMap.sv deleted file mode 100644 index f59144084..000000000 --- a/grammars/silver/util/raw/treemap/TreeMap.sv +++ /dev/null @@ -1,64 +0,0 @@ -grammar silver:util:raw:treemap; - --- One should always import this via 'import silver:util:raw:treemap as ...' --- The names are too general otherwise. - -type Map foreign; - -{-- - - Returns a new, empty, multimap using the specified comparator. - -} -function empty -Map ::= comparator::(Integer ::= a a) -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeMap.empty(%comparator%)"; -} - --- an 'insert' function is deliberating omitted due to its inefficiency, but there's add: - -{-- - - Adds a list of elements to a map. - -} -function add -Map ::= lst::[Pair] mp::Map -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeMap.addList(%lst%, (java.util.TreeMap)%mp%)"; -} - -{-- - - Looks up an element from the map, empty list if not contained. - -} -function lookup -[b] ::= key::a mp::Map -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeMap.lookup(%key%, (java.util.TreeMap)%mp%)"; -} - -{-- - - Converts a multimap back to a list of pairs, in sorted order by key. - -} -function toList -[Pair] ::= mp::Map -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeMap.toList((java.util.TreeMap)%mp%)"; -} - -{-- - - Updates the value returned by a key - -} -function update -Map ::= key::a value::[b] mp::Map -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeMap.update(%key%, %value%, (java.util.TreeMap)%mp%)"; -} - diff --git a/grammars/silver/util/raw/treeset/TreeSet.sv b/grammars/silver/util/raw/treeset/TreeSet.sv deleted file mode 100644 index ba15a3a6b..000000000 --- a/grammars/silver/util/raw/treeset/TreeSet.sv +++ /dev/null @@ -1,161 +0,0 @@ -grammar silver:util:raw:treeset; - --- One should always import this via 'import silver:util:raw:treeset as ...' --- The names are too general otherwise. - -type Set foreign; - -{-- - - Returns a new, empty, set using the specified comparator. - -} -function empty -Set ::= comparator::(Integer ::= a a) -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.empty(%comparator%)"; -} - --- an 'insert' function is deliberating omitted due to its inefficiency, but there's add: - -{-- - - Adds a list of elements to a set. - -} -function add -Set ::= lst::[a] set::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.addList(%lst%, (java.util.TreeSet)%set%)"; -} - -{-- - - Converts a set back to a list, in sorted order. - -} -function toList -[a] ::= set::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.toList((java.util.TreeSet)%set%)"; -} - -{-- - - Computes the union of the two sets. - -} -function union -Set ::= l::Set r::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.union((java.util.TreeSet)%l%,(java.util.TreeSet)%r%)"; -} - -{-- - - Computes the intersection of the two sets. - -} -function intersect -Set ::= l::Set r::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.intersect((java.util.TreeSet)%l%,(java.util.TreeSet)%r%)"; -} - -{-- - - Computes the difference of the two sets. (l - r) - -} -function difference -Set ::= l::Set r::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.difference((java.util.TreeSet)%l%,(java.util.TreeSet)%r%)"; -} - -{-- - - Determines if the element e is in the set. - -} -function contains -Boolean ::= e::a set::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.contains(%e%,(java.util.TreeSet)%set%)"; -} - -{-- - - Determines if all of the elements in e are in the set. - -} -function containsAll -Boolean ::= e::[a] set::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.containsAll(%e%,(java.util.TreeSet)%set%)"; -} - -{-- - - Determines if l is a subset of r. - -} -function subset -Boolean ::= l::Set r::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.subset((java.util.TreeSet)%l%,(java.util.TreeSet)%r%)"; -} - -{-- - - Determines if the sets are equal. - -} -function equals -Boolean ::= l::Set r::Set -{ - return subset(l,r) && subset(r,l); -} - -{-- - - Determines if a set is empty. - -} -function isEmpty -Boolean ::= s::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.isEmpty((java.util.TreeSet)%s%)"; -} - -{-- - - Determines the size of a set. - -} -function size -Integer ::= s::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.size((java.util.TreeSet)%s%)"; -} - -{-- - - Removes elements from a set, whenever 'f' return false. - -} -function filter -Set ::= f::(Boolean ::= a) s::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.filter(%f%, (java.util.TreeSet)%s%)"; -} - -{-- - - Remove all elements from the set (returns set - lst) - -} -function removeAll -Set ::= lst::[a] set::Set -{ - return error("NYI"); -} foreign { - "java" : return "common.rawlib.RawTreeSet.removeAll(%lst%, (java.util.TreeSet)%set%)"; -} - diff --git a/grammars/silver/util/subprocess/Subprocess.sv b/grammars/silver/util/subprocess/Subprocess.sv new file mode 100644 index 000000000..60183b519 --- /dev/null +++ b/grammars/silver/util/subprocess/Subprocess.sv @@ -0,0 +1,161 @@ +grammar silver:util:subprocess; + + +type ProcessHandle foreign = "common.rawlib.RawProcessHandle"; + + +@{-- + - Start a subprocess to run in the background with which to communicate. + - To run `cmd a1 a2`, call `spawnProcess("cmd", ["a1", "a2"])` + - + - @param cmd The command to run + - @param args The arguments to pass to the command (separate from the command) + - @param i The IO token + - @return The handle to communicate with the process +-} +function spawnProcess +IOVal ::= cmd::String args::[String] i::IOToken +{ + return error("Not Yet Implemented: spawnProcess"); +} foreign { + "java" : return "common.rawlib.RawProcessHandle.spawnProcess(%cmd%, %args%, %i%)"; +} + + +@{-- + - Send a string message to a subprocess + - + - @param p The process to which to send the message + - @param msg The message to send to the subprocess + - @param i The IO token + - @return The IO token +-} +function sendToProcess +IOToken ::= p::ProcessHandle msg::String i::IOToken +{ + return error("Not Yet Implemented: sendToProcess"); +} foreign { + "java" : return "%p%.sendToProcess(%msg%, %i%)"; +} + + +@{-- + - Read a line of output from a subprocess + - + - @param p The process from which to read + - @param i The IO token + - @return The line which was read +-} +function readLineFromProcess +IOVal ::= p::ProcessHandle i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readLineFromProcess(%i%)"; +} + + +@{-- + - Read everything available in the output from a subprocess. + - Returns an empty string if nothing is available. + - + - @param p The process from which to read + - @param i The IO token + - @return The line which was read +-} +function readAllFromProcess +IOVal ::= p::ProcessHandle i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readAllFromProcess(%i%)"; +} + + +@{-- + - Read everything in the stdout from a subprocess until reaching the string "ending". + - The ending string is included in the output. If the process never outputs the + - ending string to stdout, this function never returns. + - + - @param p The process from which to read + - @param ending The string to end on + - @param i The IO token + - @return The line which was read +-} +function readUntilFromProcess +IOVal ::= p::ProcessHandle ending::String i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readUntilFromProcess(%i%, %ending%)"; +} + + +@{-- + - Read a line of output from stderr of a subprocess + - + - @param p The process from which to read + - @param i The IO token + - @return The line which was read +-} +function readErrLineFromProcess +IOVal ::= p::ProcessHandle i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readErrLineFromProcess(%i%)"; +} + + +@{-- + - Read everything available in the stderr from a subprocess. + - Returns an empty string if nothing is available. + - + - @param p The process from which to read + - @param i The IO token + - @return The line which was read +-} +function readErrAllFromProcess +IOVal ::= p::ProcessHandle i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readErrAllFromProcess(%i%)"; +} + + +@{-- + - Read everything in the stderr from a subprocess until reaching the string "ending". + - The ending string is included in the output. If the process never outputs the + - ending string to stderr, this function never returns. + - + - @param p The process from which to read + - @param ending The string to end on + - @param i The IO token + - @return The line which was read +-} +function readErrUntilFromProcess +IOVal ::= p::ProcessHandle ending::String i::IOToken +{ + return error("Not Yet Implemented: readLineFromProcess"); +} foreign { + "java" : return "%p%.readErrUntilFromProcess(%i%, %ending%)"; +} + + +@{-- + - Wait for a running subprocess to end. There should be a reason to + - expect it to end; this does not kill it. + - + - @param p The process for which to wait + - @param i The IO token + - @return The IO token +-} +function waitForProcess +IOToken ::= p::ProcessHandle i::IOToken +{ + return error("Not Yet Implemented: waitForProcess"); +} foreign { + "java" : return "%p%.waitForProcess(%i%)"; +} + diff --git a/grammars/silver/util/treemap/TreeMap.sv b/grammars/silver/util/treemap/TreeMap.sv index fb9a6e44a..34d14fcf2 100644 --- a/grammars/silver/util/treemap/TreeMap.sv +++ b/grammars/silver/util/treemap/TreeMap.sv @@ -1,169 +1,104 @@ grammar silver:util:treemap; --- The API: +@@{- + - One should always import this via 'import silver:util:treemap as ...' + - The names are too general otherwise. + -} -{-- - - Creates a new (empty) tree. - - - - @param TLEop The "less that or equal to" operator on the key values for this tree - - @param EQop The "equal to" operator on the key values for this tree - - @return A new empty tree. +type Map foreign = "java.util.TreeMap"; + +@{-- + - Returns a new, empty, multimap using Ord for comparison. -} -function treeNew -TreeMap ::= CMP :: (Integer ::= a a) +function empty +Ord a => Map ::= { - return leaf(CMP); + return emptyWith(compare); } -{-- - - Inserts a key-value into the tree. If the key exists already, it will be - - associated with multiple values, not replaced. - - - - @param x The key to insert - - @param v The value to insert - - @param t The existing tree - - @return The tree with (x,v) inserted. +@{-- + - Returns a new, empty, multimap using the specified comparator. -} -function treeInsert -TreeMap ::= x::a v::b t::TreeMap +function emptyWith +Map ::= comparator::(Integer ::= a a) { - t.treeKey = x; - t.treeValue = v; - return t.treeInsert.makeBlack; + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.empty(%comparator%)"; } -{-- - - Retrieves all values associated with a key in the tree. - - - - @param x The key to loopup - - @param t The tree - - @return All values inserted for that key in the tree. +@@{- @warning An 'insert' function is deliberating omitted due to its inefficiency, but there's add: -} + +@{-- + - Adds a list of elements to a map. -} -function treeLookup -[b] ::= x::a t::TreeMap +function add +Map ::= lst::[Pair] mp::Map { - t.treeKey = x; - return t.treeLookup; + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.addList(%lst%, %mp%)"; } -{-- - - Inserts multiple key-value pairs into the tree. - - - - @param l A list of key-value pairs. - - @param t The existing tree - - @return The tree with all new key-value pairs inserted. +@{-- + - Returns a list of keys that are present in the map, in sorted order. -} -function treeConvert -TreeMap ::= l::[Pair] t::TreeMap +function keys +[a] ::= mp::Map { - return if null(l) then t - else treeConvert(tail(l), treeInsert(head(l).fst, head(l).snd, t)); + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.keys(%mp%)"; } -{-- - - Reverts a tree back into an association list. Any key associated with - - multiple values will appear multiple times in the list, for each value. - - - - @param t The existing tree - - @return A list of key-value pairs. +@{-- + - Looks up an element from the map, empty list if not contained. -} -function treeDeconvert -[Pair] ::= t::TreeMap +function lookup +[b] ::= key::a mp::Map { - return t.treeToList; + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.lookup(%key%, %mp%)"; } --- The implementation: - -nonterminal TreeMap with treeKey, treeLookup, treeValue, treeInsert, makeBlack, treeToList; - -inherited attribute treeKey :: a; -synthesized attribute treeLookup :: [b]; -- key -inherited attribute treeValue :: b; -synthesized attribute treeInsert :: TreeMap; -- key, value - -synthesized attribute makeBlack :: TreeMap; - -synthesized attribute treeToList :: [Pair]; - -abstract production leaf -top::TreeMap ::= CMP :: (Integer ::= a a) +@{-- + - Converts a list of pairs to a multimap. + -} +function fromList +Ord a => Map ::= l::[Pair] { - top.treeLookup = []; - top.treeInsert = node(false, top, top, top.treeKey, [top.treeValue], CMP); - top.makeBlack = top; - top.treeToList = []; + return add(l, empty()); } -abstract production node -top::TreeMap ::= black::Boolean lefttree::TreeMap righttree::TreeMap - label::a values::[b] - CMP :: (Integer ::= a a) +@{-- + - Converts a multimap back to a list of pairs, in sorted order by key. + -} +function toList +[Pair] ::= mp::Map { - top.treeLookup = let cmpr :: Integer = CMP(top.treeKey, label) - in if cmpr <= 0 - then if cmpr == 0 - then values - else lefttree.treeLookup - else righttree.treeLookup - end; - - top.treeInsert = let cmpr :: Integer = CMP(top.treeKey, label) - in if cmpr <= 0 - then if cmpr == 0 - then node(black, lefttree, righttree, label, top.treeValue :: values, CMP) - else - if black - then balanceL( lefttree.treeInsert, righttree, label, values, CMP) - else node(false, lefttree.treeInsert, righttree, label, values, CMP) - else if black - then balanceR( lefttree, righttree.treeInsert, label, values, CMP) - else node(false, lefttree, righttree.treeInsert, label, values, CMP) - end; - - top.makeBlack = if black then top else node(true, lefttree, righttree, label, values, CMP); - top.treeToList = lefttree.treeToList ++ treeMapKeyValues(label, values) ++ righttree.treeToList; - - lefttree.treeKey = top.treeKey; - lefttree.treeValue = top.treeValue; - righttree.treeKey = top.treeKey; - righttree.treeValue = top.treeValue; + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.toList(%mp%)"; } -function treeMapKeyValues -[Pair] ::= k::a v::[b] +@{-- + - Updates the value returned by a key + -} +function update +Map ::= key::a value::[b] mp::Map { - return if null(v) then [] else pair(k, head(v)) :: treeMapKeyValues(k, tail(v)); + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeMap.update(%key%, %value%, %mp%)"; } --- Invariant: every black node does not have double reds below it (on any of the 4 paths) --- Whenever we're reconstructing after an insert, all black nodes will examine the modified subtree --- to see if there are any double-reds, and act accordingly. --- These functions are only called by black nodes. -function balanceL -TreeMap ::= lefttree::TreeMap righttree::TreeMap - label::a values::[b] - CMP :: (Integer ::= a a) -{ -return case lefttree of - node(false, node(false, a, b, x1, x2,_), c, y1, y2,_) -> - node(false, node(true, a, b, x1, x2, CMP), node(true, c, righttree, label, values, CMP), y1, y2, CMP) -| node(false, a, node(false, b, c, y1, y2,_), x1, x2,_) -> - node(false, node(true, a, b, x1, x2, CMP), node(true, c, righttree, label, values, CMP), y1, y2, CMP) -| a -> node(true, a, righttree, label, values, CMP) -end; -} -function balanceR -TreeMap ::= lefttree::TreeMap righttree::TreeMap - label::a values::[b] - CMP :: (Integer ::= a a) -{ -return case righttree of - node(false, node(false, b, c, y1, y2,_), d, z1, z2,_) -> - node(false, node(true, lefttree, b, label, values, CMP), node(true, c, d, z1, z2, CMP), y1, y2, CMP) -| node(false, b, node(false, c, d, z1, z2,_), y1, y2,_) -> - node(false, node(true, lefttree, b, label, values, CMP), node(true, c, d, z1, z2, CMP), y1, y2, CMP) -| b -> node(true, lefttree, b, label, values, CMP) -end; +instance Eq a, Eq b => Eq Map { + -- TODO: This could be more efficient - eagerly converting both maps to lists before comparison. + eq = \ m1::Map m2::Map -> toList(m1) == toList(m2); } +instance Ord a, Ord b => Ord Map { + -- TODO: This could be more efficient - eagerly converting both maps to lists before comparison. + compare = \ m1::Map m2::Map -> compare(toList(m1), toList(m2)); +} diff --git a/grammars/silver/util/treeset/TreeSet.sv b/grammars/silver/util/treeset/TreeSet.sv new file mode 100644 index 000000000..5700a354d --- /dev/null +++ b/grammars/silver/util/treeset/TreeSet.sv @@ -0,0 +1,183 @@ +grammar silver:util:treeset; + +@@{- + - One should always import this via 'import silver:util:treemap as ...' + - The names are too general otherwise. + -} + +type Set foreign = "java.util.TreeSet"; + +@{-- + - Returns a new, empty, set using Ord for comparison. + -} +function empty +Ord a => Set ::= +{ + return emptyWith(compare); +} + +@{-- + - Returns a new, empty, set using the specified comparator. + -} +function emptyWith +Set ::= comparator::(Integer ::= a a) +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.empty(%comparator%)"; +} + +@@{- @warning An 'insert' function is deliberating omitted due to its inefficiency, but there's add: -} + +@{-- + - Adds a list of elements to a set. + -} +function add +Set ::= lst::[a] set::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.addList(%lst%, %set%)"; +} + +@{-- + - Converts a list to a set. + -} +function fromList +Ord a => Set ::= lst::[a] +{ + return add(lst, empty()); +} + +@{-- + - Converts a set back to a list, in sorted order. + -} +function toList +[a] ::= set::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.toList(%set%)"; +} + +@{-- + - Computes the union of the two sets. + -} +function union +Set ::= l::Set r::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.union(%l%,%r%)"; +} + +@{-- + - Computes the intersection of the two sets. + -} +function intersect +Set ::= l::Set r::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.intersect(%l%,%r%)"; +} + +@{-- + - Computes the difference of the two sets. (l - r) + -} +function difference +Set ::= l::Set r::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.difference(%l%,%r%)"; +} + +@{-- + - Determines if the element e is in the set. + -} +function contains +Boolean ::= e::a set::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.contains(%e%,%set%)"; +} + +@{-- + - Determines if all of the elements in e are in the set. + -} +function containsAll +Boolean ::= e::[a] set::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.containsAll(%e%,%set%)"; +} + +@{-- + - Determines if l is a subset of r. + -} +function subset +Boolean ::= l::Set r::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.subset(%l%,%r%)"; +} + +@{-- + - Determines if a set is empty. + -} +function isEmpty +Boolean ::= s::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.isEmpty(%s%)"; +} + +@{-- + - Determines the size of a set. + -} +function size +Integer ::= s::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.size(%s%)"; +} + +@{-- + - Removes elements from a set, whenever 'f' return false. + -} +function filter +Set ::= f::(Boolean ::= a) s::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.filter(%f%, %s%)"; +} + +@{-- + - Remove all elements from the set (returns set - lst) + -} +function removeAll +Set ::= lst::[a] set::Set +{ + return error("NYI"); +} foreign { + "java" : return "common.rawlib.RawTreeSet.removeAll(%lst%, %set%)"; +} + +instance Eq Set { + eq = \ l::Set r::Set -> subset(l,r) && subset(r,l); +} + +instance Semigroup Set { + append = union; +} + +instance Ord a => Monoid Set { + mempty = empty(); +} diff --git a/grammars/silver/xml/XMLTools.sv b/grammars/silver/xml/XMLTools.sv new file mode 100644 index 000000000..7e7f50294 --- /dev/null +++ b/grammars/silver/xml/XMLTools.sv @@ -0,0 +1,18 @@ +grammar silver:xml; + +exports silver:xml:ast; -- don't force users to import anything but silver:xml. + +import silver:xml:ast; + +@{- + - @warning WARNING: this is buggy! we're parsing a file without demanding an IO token!! + - WILL CHANGE IN THE FUTURE. + -} +function parseXMLFileN +ParseResult ::= filename::String +{ + return error("parseXMLFileN not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.parseXMLFileN(%filename%)"; +} + diff --git a/grammars/lib/xml/ast/XMLSpec.sv b/grammars/silver/xml/ast/XMLSpec.sv similarity index 99% rename from grammars/lib/xml/ast/XMLSpec.sv rename to grammars/silver/xml/ast/XMLSpec.sv index d4be77f42..8c72b47e1 100644 --- a/grammars/lib/xml/ast/XMLSpec.sv +++ b/grammars/silver/xml/ast/XMLSpec.sv @@ -1,4 +1,4 @@ -grammar lib:xml:ast; +grammar silver:xml:ast; -- Authors: August Schwerdfeger for Adventium Enterprises LLC, 2011. -- Ted Kaminski diff --git a/grammars/silver/xml/foreigntypes/Types.sv b/grammars/silver/xml/foreigntypes/Types.sv new file mode 100644 index 000000000..7acfb62f7 --- /dev/null +++ b/grammars/silver/xml/foreigntypes/Types.sv @@ -0,0 +1,147 @@ +grammar silver:xml:foreigntypes; + +import silver:xml:ast; + +type XML_Document foreign; +type XML_NodeList foreign; +type XML_Node foreign; + + +{-- + - WARNING: this is buggy! we're parsing a file without demanding an IO token!! + - WILL CHANGE IN THE FUTURE. + -} +function parseXMLFileF +ParseResult ::= filename::String +{ + return error("parseXMLFileF not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.parseXMLFileF(%filename%)"; +} + +-- NATIVE AST response. This is just a helper for +function nodeListXPathQueryN +XMLNodeList ::= query::String doc::XML_Document +{ + return xmlNodeListF2N(nodeListXPathQueryF(query, doc)); +} + +-- FOREIGN DOM response +function nodeListXPathQueryF +XML_NodeList ::= query::String doc::XML_Document +{ + return error("nodeListXPathQueryN not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), null)"; +} + +function stringXPathQuery +String ::= query::String doc::XML_Document +{ + return error("stringXPathQuery not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), null)"; +} + +-- REQUERYING a previous query result... +function nodeListXPathReQueryF +XML_NodeList ::= query::String doc::XML_Node +{ + return error("nodeListXPathReQueryF not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), null)"; +} +function stringXPathReQuery +String ::= query::String doc::XML_Node +{ + return error("stringXPathReQuery not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), null)"; +} + +----- namespace variants ------------------------------------------------------ + +-- FOREIGN DOM response +function nodeListXPathQueryFns +XML_NodeList ::= query::String ns::[Pair] doc::XML_Document +{ + return error("nodeListXPathQueryN not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), %ns%)"; +} + +function stringXPathQueryns +String ::= query::String ns::[Pair] doc::XML_Document +{ + return error("stringXPathQuery not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), %ns%)"; +} + +-- REQUERYING a previous query result... +function nodeListXPathReQueryFns +XML_NodeList ::= query::String ns::[Pair] doc::XML_Node +{ + return error("nodeListXPathReQueryF not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryNodeSet(%doc%, %query%.toString(), %ns%)"; +} +function stringXPathReQueryns +String ::= query::String ns::[Pair] doc::XML_Node +{ + return error("stringXPathReQuery not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.xpathQueryString(%doc%, %query%.toString(), %ns%)"; +} + +------------------------------------------------------------------------------- + + +function xmlNodeListF2N +XMLNodeList ::= nl::XML_NodeList +{ + return error("xmlNodeListF2N not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.nodeListF2N((org.w3c.dom.NodeList)%nl%)"; +} + +function nodeListF2NPartial +[XML_Node] ::= nl::XML_NodeList +{ + return error("nodeListF2NPartial not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.nodeListF2NPartial((org.w3c.dom.NodeList)%nl%)"; +} + +-- TODO: xmlNodeListN2F? Can accomplish for now by wrapping in a document, perhaps. +-- TODO: Node conversions? + +function xmlDocumentF2N +XMLDocument ::= doc::XML_Document +{ + return error("xmlDocumentF2N not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.documentF2N((org.w3c.dom.Document)%doc%)"; +} + +function xmlDocumentN2F +XML_Document ::= doc::XMLDocument +{ + return error("xmlDocumentN2F not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.documentN2F((silver.xml.ast.NXMLDocument)%doc%)"; +} + +function xmlDocumentF2String +String ::= doc::XML_Document +{ + return error("xmlDocumentF2String not yet implemented"); +} foreign { + "java" : return "common.rawlib.RawXML.documentF2String((org.w3c.dom.Document)%doc%)"; +} + +function xmlDocumentN2String +String ::= doc::XMLDocument +{ + return xmlDocumentF2String(xmlDocumentN2F(doc)); +} diff --git a/language-server/.project b/language-server/.project new file mode 100644 index 000000000..21731c915 --- /dev/null +++ b/language-server/.project @@ -0,0 +1,28 @@ + + + silver-langserver-parent + + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.m2e.core.maven2Nature + + + + 1665108614003 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/language-server/.settings/org.eclipse.ltk.core.refactoring.prefs b/language-server/.settings/org.eclipse.ltk.core.refactoring.prefs new file mode 100644 index 000000000..b196c64a3 --- /dev/null +++ b/language-server/.settings/org.eclipse.ltk.core.refactoring.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false diff --git a/language-server/.settings/org.eclipse.m2e.core.prefs b/language-server/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/language-server/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/language-server/build.sh b/language-server/build.sh new file mode 100755 index 000000000..e5b7fd198 --- /dev/null +++ b/language-server/build.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +set -e + +# For now, this expects you to have Copper installed locally; +# this can be done by running `mvn install` from wherever you have the copper repo checkedd out. + +mvn install:install-file -Dfile=../jars/SilverRuntime.jar -DgroupId=edu.umn.cs.melt -DartifactId=silver-runtime -Dversion=0.4.4-SNAPSHOT -Dpackaging=jar -DgeneratePom=true +mvn install:install-file -Dfile=../jars/silver.compiler.composed.Default.jar -DgroupId=edu.umn.cs.melt -DartifactId=silver -Dversion=0.4.4-SNAPSHOT -Dpackaging=jar -DgeneratePom=true + +(cd ../runtime/lsp4j && mvn install) + +mvn package + diff --git a/language-server/langserver/.gitignore b/language-server/langserver/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/language-server/langserver/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/language-server/langserver/pom.xml b/language-server/langserver/pom.xml new file mode 100644 index 000000000..d9ac484cc --- /dev/null +++ b/language-server/langserver/pom.xml @@ -0,0 +1,50 @@ + + + edu.umn.cs.melt + silver-langserver-parent + 0.1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + silver-langserver + jar + + + + edu.umn.cs.melt + lsp4j-util + 0.1.0-SNAPSHOT + + + edu.umn.cs.melt + silver + 0.4.4-SNAPSHOT + + + edu.umn.cs.melt + silver-runtime + 0.4.4-SNAPSHOT + + + edu.umn.cs.melt + copper-runtime + 1.0.1-SNAPSHOT + + + org.commonmark + commonmark + 0.17.2 + + + + + + + ../../grammars/ + grammars/ + + + + diff --git a/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageServer.java b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageServer.java new file mode 100644 index 000000000..03bb03d93 --- /dev/null +++ b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageServer.java @@ -0,0 +1,116 @@ +package edu.umn.cs.melt.silver.langserver; + +import java.io.IOException; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.lsp4j.ExecuteCommandOptions; +import org.eclipse.lsp4j.FileOperationFilter; +import org.eclipse.lsp4j.FileOperationOptions; +import org.eclipse.lsp4j.FileOperationPattern; +import org.eclipse.lsp4j.FileOperationsServerCapabilities; +import org.eclipse.lsp4j.InitializeParams; +import org.eclipse.lsp4j.InitializeResult; +import org.eclipse.lsp4j.SemanticTokensLegend; +import org.eclipse.lsp4j.SemanticTokensServerFull; +import org.eclipse.lsp4j.SemanticTokensWithRegistrationOptions; +import org.eclipse.lsp4j.ServerCapabilities; +import org.eclipse.lsp4j.TextDocumentSyncKind; +import org.eclipse.lsp4j.WorkspaceServerCapabilities; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.LanguageClientAware; +import org.eclipse.lsp4j.services.LanguageServer; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +import edu.umn.cs.melt.lsp4jutil.Util; + +public class SilverLanguageServer implements LanguageServer, LanguageClientAware { + private SilverLanguageService service; + private int errorCode = 1; + + public SilverLanguageServer() { + this.service = new SilverLanguageService(); + } + + @Override + public CompletableFuture initialize(InitializeParams initializeParams) { + common.Util.init(); + silver.compiler.composed.Default.Init.initAllStatics(); + silver.compiler.composed.Default.Init.init(); + silver.compiler.composed.Default.Init.postInit(); + + System.err.println("Initializing Silver language server"); + + Path silverGrammars; + try { + silverGrammars = Files.createTempDirectory("silver_grammars"); + Util.copyFromJar(getClass(), "grammars/", silverGrammars); + } catch (IOException | URISyntaxException e) { + throw new RuntimeException(e); + } + service.setSilverGrammarsPath(silverGrammars); + + service.setWorkspaceFolders(initializeParams.getWorkspaceFolders()); + + // Set the capabilities of the LS to inform the client. + ServerCapabilities capabilities = new ServerCapabilities(); + capabilities.setTextDocumentSync(TextDocumentSyncKind.Full); + capabilities.setSemanticTokensProvider( + new SemanticTokensWithRegistrationOptions( + new SemanticTokensLegend( + SilverLanguageService.tokenTypes, SilverLanguageService.tokenModifiers), + new SemanticTokensServerFull(false), false)); + capabilities.setExecuteCommandProvider(new ExecuteCommandOptions(List.of( + "silver.clean" + ))); + capabilities.setDeclarationProvider(true); + + FileOperationOptions fileOperationOptions = new FileOperationOptions( + List.of(new FileOperationFilter(new FileOperationPattern("**/*.{sv,ag,sv.md,ag.md}"))) + ); + FileOperationsServerCapabilities fileOperations = new FileOperationsServerCapabilities(); + fileOperations.setDidCreate(fileOperationOptions); + fileOperations.setDidDelete(fileOperationOptions); + fileOperations.setDidRename(fileOperationOptions); + WorkspaceServerCapabilities workspaceServer = new WorkspaceServerCapabilities(); + workspaceServer.setFileOperations(fileOperations); + capabilities.setWorkspace(workspaceServer); + + final InitializeResult initializeResult = new InitializeResult(capabilities); + return CompletableFuture.supplyAsync(()->initializeResult); + } + + @Override + public CompletableFuture shutdown() { + // If shutdown request comes from client, set the error code to 0. + errorCode = 0; + return null; + } + + @Override + public void exit() { + // Kill the LS on exit request from client. + System.exit(errorCode); + } + + @Override + public TextDocumentService getTextDocumentService() { + // Return the endpoint for language features. + return this.service; + } + + @Override + public WorkspaceService getWorkspaceService() { + // Return the endpoint for workspace functionality. + return this.service; + } + + @Override + public void connect(LanguageClient languageClient) { + service.setClient(languageClient); + } +} diff --git a/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageService.java b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageService.java new file mode 100644 index 000000000..b3e51d37b --- /dev/null +++ b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverLanguageService.java @@ -0,0 +1,473 @@ +package edu.umn.cs.melt.silver.langserver; + +import java.io.File; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.BasicFileAttributes; +import java.nio.file.attribute.FileTime; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.ApplyWorkspaceEditParams; +import org.eclipse.lsp4j.ConfigurationItem; +import org.eclipse.lsp4j.ConfigurationParams; +import org.eclipse.lsp4j.CreateFilesParams; +import org.eclipse.lsp4j.DeclarationParams; +import org.eclipse.lsp4j.DeleteFilesParams; +import org.eclipse.lsp4j.DidChangeConfigurationParams; +import org.eclipse.lsp4j.DidChangeTextDocumentParams; +import org.eclipse.lsp4j.DidChangeWatchedFilesParams; +import org.eclipse.lsp4j.DidCloseTextDocumentParams; +import org.eclipse.lsp4j.DidOpenTextDocumentParams; +import org.eclipse.lsp4j.DidSaveTextDocumentParams; +import org.eclipse.lsp4j.ExecuteCommandParams; +import org.eclipse.lsp4j.FileCreate; +import org.eclipse.lsp4j.Location; +import org.eclipse.lsp4j.LocationLink; +import org.eclipse.lsp4j.MessageParams; +import org.eclipse.lsp4j.MessageType; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.PublishDiagnosticsParams; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.RenameFilesParams; +import org.eclipse.lsp4j.SemanticTokens; +import org.eclipse.lsp4j.SemanticTokensParams; +import org.eclipse.lsp4j.TextDocumentContentChangeEvent; +import org.eclipse.lsp4j.TextEdit; +import org.eclipse.lsp4j.WorkspaceEdit; +import org.eclipse.lsp4j.WorkspaceFolder; +import org.eclipse.lsp4j.jsonrpc.CompletableFutures; +import org.eclipse.lsp4j.jsonrpc.messages.Either; +import org.eclipse.lsp4j.services.LanguageClient; +import org.eclipse.lsp4j.services.TextDocumentService; +import org.eclipse.lsp4j.services.WorkspaceService; + +import com.google.gson.JsonPrimitive; + +import common.ConsCell; +import common.DecoratedNode; +import common.OriginContext; +import common.SilverCopperParser; +import common.StringCatter; +import common.javainterop.ConsCellCollection; +import edu.umn.cs.melt.lsp4jutil.CopperParserNodeFactory; +import edu.umn.cs.melt.lsp4jutil.CopperSemanticTokenEncoder; +import edu.umn.cs.melt.lsp4jutil.Util; +import silver.compiler.composed.Default.Parser_silver_compiler_composed_Default_svParse; +import silver.compiler.definition.core.NRoot; +import silver.compiler.driver.PbuildRun; +import silver.compiler.driver.PparseArgsOrError; +import silver.compiler.driver.util.NBuildEnv; +import silver.compiler.driver.util.PbuildEnv; +import silver.compiler.driver.util.PwriteInterface; +import silver.compiler.langserver.PfindDeclLocation; +import silver.core.NLocation; +import silver.core.NPair; +import silver.core.PunsafeEvalIO; + +/** + * Implementation of LSP text document and workspace services for Silver. + * + * @author krame505 + */ +public class SilverLanguageService implements TextDocumentService, WorkspaceService { + private LanguageClient client; + private List folders; + private Map fileContents = new HashMap<>(); + private Map fileVersions = new HashMap<>(); + private Map savedVersions = new HashMap<>(); + private boolean buildInProgress = false, buildTriggered = false; + private boolean cleanBuild = false; + private boolean enableMWDA = false; + private String compilerJar = ""; + private String parserName = ""; + private FileTime compilerJarTime; + private String silverGen; + private String silverStdlibGrammars = null; + private DecoratedNode comp = null; + private Set grammarDirs = new HashSet<>(); + private Set buildGrammars = new HashSet<>(); + + public SilverLanguageService() { + try { + silverGen = Files.createTempDirectory("silver_generated").toString() + "/"; + } catch (IOException e) { + e.printStackTrace(); + } + setParserFactory(Parser_silver_compiler_composed_Default_svParse::new); + } + + public void setClient(LanguageClient client) { + this.client = client; + } + + public void setSilverGrammarsPath(Path path) { + this.silverStdlibGrammars = path.toString() + "/"; + } + + public void setWorkspaceFolders(List folders) { + this.folders = folders; + refreshWorkspace(); + } + + @Override + public void didOpen(DidOpenTextDocumentParams params) { + // System.err.println("Opened " + params); + String uri = params.getTextDocument().getUri(); + fileContents.put(uri, params.getTextDocument().getText()); + fileVersions.put(uri, params.getTextDocument().getVersion()); + savedVersions.put(uri, params.getTextDocument().getVersion()); + triggerBuild(false); + } + + @Override + public void didChange(DidChangeTextDocumentParams params) { + // System.err.println("Changed " + params); + String uri = params.getTextDocument().getUri(); + for (TextDocumentContentChangeEvent change : params.getContentChanges()) { + fileContents.put(uri, change.getText()); + fileVersions.put(uri, params.getTextDocument().getVersion()); + } + } + + @Override + public void didClose(DidCloseTextDocumentParams params) { + // System.err.println("Closed " + params); + String uri = params.getTextDocument().getUri(); + fileContents.remove(uri); + fileVersions.remove(uri); + savedVersions.remove(uri); + } + + @Override + public void didSave(DidSaveTextDocumentParams params) { + // System.err.println("Saved " + params); + String uri = params.getTextDocument().getUri(); + if (!fileVersions.containsKey(uri)) { + throw new IllegalStateException("File saved before it was changed"); + } + savedVersions.put(uri, fileVersions.get(uri)); + triggerBuild(false); + } + + @Override + public CompletableFuture executeCommand(ExecuteCommandParams params) { + switch (params.getCommand()) { + case "silver.clean": + triggerBuild(true); + return null; + } + throw new UnsupportedOperationException("Unsupported command " + params.getCommand()); + } + + @Override + public void didChangeConfiguration(DidChangeConfigurationParams params) { + + } + + @Override + public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) { + + } + + @Override + public void didCreateFiles(CreateFilesParams params) { + Map> edits = new HashMap<>(); + for (FileCreate f : params.getFiles()) { + if (!f.getUri().endsWith(".md")) { + SilverUtil.uriToGrammar(f.getUri()).ifPresent(grammar -> { + String grammarDecl = "grammar " + grammar + ";\n\n"; + TextEdit edit = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), grammarDecl); + edits.put(f.getUri(), List.of(edit)); + }); + } + } + client.applyEdit(new ApplyWorkspaceEditParams(new WorkspaceEdit(edits))); + refreshWorkspace(); + } + + @Override + public void didDeleteFiles(DeleteFilesParams params) { + refreshWorkspace(); + } + + @Override + public void didRenameFiles(RenameFilesParams params) { + refreshWorkspace(); + } + + @Override + public CompletableFuture, List>> declaration(DeclarationParams params) { + return CompletableFutures.computeAsync((cancelChecker) -> { + if (comp == null) { + return Either.forLeft(List.of()); + } + + String fileName = ""; + try { + fileName = new URI(params.getTextDocument().getUri()).getPath(); + } catch (URISyntaxException e) { + e.printStackTrace(); + } + return Either.forLeft(new ConsCellCollection( + PfindDeclLocation.invoke(OriginContext.FFI_CONTEXT, + new StringCatter(fileName), params.getPosition().getLine() + 1, params.getPosition().getCharacter(), comp)) + .stream() + .map((loc) -> new Location("file://" + Util.locationToFile(loc), Util.locationToRange(loc))) + .collect(Collectors.toList())); + }); + } + + public static final List tokenTypes = Arrays.asList(new String[] { + "namespace", "type", "interface", "class", "typeParameter", "parameter", "variable", "function", + "keyword", "modifier", "comment", "string", "number", "regexp", "operator", "macro" + }); + public static final List tokenModifiers = Arrays.asList(new String[] { + "declaration", "definition", "documentation", "defaultLibrary", "modification" + }); + + private CopperSemanticTokenEncoder semanticTokenEncoder; + private CopperParserNodeFactory parserFn; + private void setParserFactory(Supplier> parserFactory) { + semanticTokenEncoder = new CopperSemanticTokenEncoder(parserFactory, tokenTypes, tokenModifiers); + parserFn = new CopperParserNodeFactory(parserFactory); + } + + private CompletableFuture reloadParser() { + ConfigurationItem compilerJarConfigItem = new ConfigurationItem(); + compilerJarConfigItem.setSection("silver.compilerJar"); + ConfigurationItem parserNameConfigItem = new ConfigurationItem(); + parserNameConfigItem.setSection("silver.parserName"); + ConfigurationParams configParams = new ConfigurationParams( + List.of(compilerJarConfigItem, parserNameConfigItem)); + return client.configuration(configParams).thenAccept((configs) -> { + String oldParserJar = compilerJar; + String oldParserName = parserName; + compilerJar = ((JsonPrimitive)configs.get(0)).getAsString(); + parserName = ((JsonPrimitive)configs.get(1)).getAsString(); + if (compilerJar.isEmpty() || parserName.isEmpty()) { + if (!compilerJar.equals(oldParserJar) || !parserName.equals(oldParserName)) { + setParserFactory(Parser_silver_compiler_composed_Default_svParse::new); + } + } else { + try { + Path compilerJarPath = Paths.get(compilerJar); + FileTime newParserJarTime = Files.readAttributes(compilerJarPath, BasicFileAttributes.class).lastModifiedTime(); + if (!compilerJar.equals(oldParserJar) || !parserName.equals(oldParserName) || !newParserJarTime.equals(compilerJarTime)) { + compilerJarTime = newParserJarTime; + System.err.println("Loading parser " + compilerJar + " " + parserName); + setParserFactory(Util.loadCopperParserFactory(compilerJarPath, parserName, NRoot.class)); + } + } catch (SecurityException | ReflectiveOperationException | IOException e) { + client.showMessage(new MessageParams(MessageType.Error, "Error loading parser from jar: " + e.toString())); + } + } + }); + } + + @Override + public CompletableFuture semanticTokensFull(SemanticTokensParams params) { + String uri = params.getTextDocument().getUri(); + return reloadParser().thenApply((arg) -> { + List tokens; + int requestVersion; + // Recompute tokens if the file is changed while tokens are being computed + do { + requestVersion = fileVersions.get(uri); + if (fileContents.containsKey(uri)) { + tokens = semanticTokenEncoder.parseTokens(fileContents.get(uri)); + } else { + tokens = new ArrayList(); + } + } while (requestVersion != fileVersions.get(uri)); + return new SemanticTokens(tokens); + }); + } + + private void refreshWorkspace() { + grammarDirs.clear(); + buildGrammars.clear(); + + for (WorkspaceFolder folder : folders) { + URI uri; + try { + uri = new URI(folder.getUri()); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URI", e); + } + findGrammars(new File(uri)); + } + + triggerBuild(false); + } + + private void findGrammars(File root) { + // Workaround to avoid copied resource grammars in the maven build + // directory in this LSP plugin under the Silver repo. + // We might want to add a more intelegent way of specifying grammars to + // exclude in case duplicates are found. + if (root.getPath().contains("/target/classes/")) return; + + for (File file : root.listFiles()) { + if (file.isDirectory()) { + findGrammars(file); + } else if (SilverUtil.isValidSilverFileName(file.getName())) { + Optional fileGrammar = SilverUtil.uriToGrammar("file://" + file.getAbsolutePath()); + fileGrammar.ifPresent(buildGrammars::add); + fileGrammar.ifPresent(grammar -> grammarDirs.add( + root.getAbsolutePath().replace("/", ":").replace(".", ":") + .split(grammar, 2)[0].replace(":", "/"))); + } + } + } + + public synchronized void triggerBuild(boolean cleanBuild) { + this.cleanBuild = cleanBuild; + + buildTriggered = true; + if (!buildInProgress) { + buildInProgress = true; + new Thread(() -> { + while(true) { + Map buildVersions; + synchronized (this) { + buildTriggered = false; + buildVersions = new HashMap<>(savedVersions); + } + doBuild(buildVersions); + synchronized (this) { + if (!buildTriggered) { + buildInProgress = false; + break; + } + } + }; + }).start(); + } + } + + private void doBuild(Map buildVersions) { + System.err.println("Building"); + + String silverHome = ""; // Not needed since we aren't doing translation + List args = new ArrayList<>(); + List grammarPath = new ArrayList<>(grammarDirs); + List silverHostGen = List.of(silverGen); + + // Check the config for whether the MWDA is enabled + ConfigurationItem enableMWDAConfigItem = new ConfigurationItem(); + enableMWDAConfigItem.setSection("silver.enableMWDA"); + ConfigurationParams configParams = new ConfigurationParams(List.of(enableMWDAConfigItem)); + boolean newEnableMWDA = enableMWDA; + try { + newEnableMWDA = ((JsonPrimitive)client.configuration(configParams).get().get(0)).getAsBoolean(); + } catch (InterruptedException | ExecutionException e) { + // Ignore, getting the settings sometimes fails when a build is triggered during initialization + } + + if (newEnableMWDA && !enableMWDA) { + // Do a clean build when the MWDA is initially enabled + cleanBuild = true; + } + enableMWDA = newEnableMWDA; + + if (enableMWDA) { + System.err.println("MWDA enabled"); + args.add("--warn-all"); + } + + // Clean build if requested + if (cleanBuild) { + System.err.println("Clean build"); + args.add("--clean"); + } + + if (silverStdlibGrammars == null) { + throw new IllegalStateException("Silver host grammars path not set"); + } else { + // Add the silver resource grammars to the end of the grammar path, + // so if silver is in the workspace we will find it there first. + grammarPath.add(silverStdlibGrammars); + } + + // Set up the build environment + NBuildEnv benv = new PbuildEnv( + new StringCatter(silverHome), + new StringCatter(silverGen), + ConsCellCollection.fromStringList(grammarPath), + ConsCellCollection.fromStringList(silverHostGen) + ); + DecoratedNode a = PparseArgsOrError.invoke(OriginContext.FFI_CONTEXT, ConsCellCollection.fromStringList(args)); + + // Build! + DecoratedNode comp = (DecoratedNode)PunsafeEvalIO.invoke(OriginContext.FFI_CONTEXT, + PbuildRun.invoke(OriginContext.FFI_CONTEXT, + parserFn, a, benv, + ConsCellCollection.fromIterator(buildGrammars.stream().map(StringCatter::new).iterator()))); + + // Note that we must demand allGrammars from comp before demanding + // recompiledGrammars to ensure that IO happens properly, + // due to the circularity in the driver involving unsafeInterleaveIO. + Collection allGrammars = new ConsCellCollection<>( + comp.synthesized(silver.compiler.driver.util.Init.silver_compiler_driver_util_allGrammars__ON__silver_compiler_driver_util_Compilation)); + Collection recompiledGrammars = new ConsCellCollection<>( + comp.synthesized(silver.compiler.driver.util.Init.silver_compiler_driver_util_recompiledGrammars__ON__silver_compiler_driver_util_Compilation)); + + // Check that we built all triggered grammars. + Set builtGrammars = allGrammars.stream() + .map(r -> r.synthesized(silver.compiler.driver.util.Init.silver_compiler_definition_env_declaredName__ON__silver_compiler_driver_util_RootSpec).toString()) + .collect(Collectors.toSet()); + for (String grammar : buildGrammars) { + if (!builtGrammars.contains(grammar)) { + System.err.println("Failed to find triggered grammar " + grammar); + } + } + + // Report diagnostics + System.err.println("Reporting diagnostics"); + for (DecoratedNode r : allGrammars) { + String grammarSource = + r.synthesized(silver.compiler.driver.util.Init.silver_compiler_definition_env_grammarSource__ON__silver_compiler_driver_util_RootSpec).toString(); + + Collection allFileErrors = new ConsCellCollection<>( + r.synthesized(silver.compiler.driver.util.Init.silver_compiler_definition_core_allFileErrors__ON__silver_compiler_driver_util_RootSpec)); + for (NPair fileErrors : allFileErrors) { + DecoratedNode decFileErrors = fileErrors.decorate(); + String fileName = decFileErrors.synthesized(silver.core.Init.silver_core_fst__ON__silver_core_Pair).toString(); + ConsCell messages = decFileErrors.synthesized(silver.core.Init.silver_core_snd__ON__silver_core_Pair); + String uri = "file://" + grammarSource + fileName; + + // System.err.println("Reporting diagnostics for " + grammarSource + fileName); + client.publishDiagnostics(new PublishDiagnosticsParams(uri, Util.messagesToDiagnostics(messages, uri), buildVersions.get(uri))); + } + } + + // Write updated interface files + System.err.println("Writing interface files"); + for (DecoratedNode r : recompiledGrammars) { + PunsafeEvalIO.invoke(OriginContext.FFI_CONTEXT, + PwriteInterface.invoke(OriginContext.FFI_CONTEXT, new StringCatter(silverGen), r)); + } + + // Only update the compilation result once we are done reporting errors, to avoid race conditions from other attributes being demanded + this.comp = comp; + + // Reset option flags + this.cleanBuild = false; + } +} diff --git a/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverUtil.java b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverUtil.java new file mode 100644 index 000000000..984d85f4e --- /dev/null +++ b/language-server/langserver/src/main/java/edu/umn/cs/melt/silver/langserver/SilverUtil.java @@ -0,0 +1,74 @@ +package edu.umn.cs.melt.silver.langserver; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Optional; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Stream; + +import common.OriginContext; +import common.StringCatter; +import silver.compiler.driver.PisValidSilverFile; + +/** + * Silver-specific utilities used in SilverLanguageService. + * + * @author krame505 + */ +public class SilverUtil { + private static Pattern grammarDecl = Pattern.compile("grammar *([a-zA-Z0-9_:]+) *;"); + /** + * Attempt to infer the grammar from a source file URI. + * @param uriString The URI of a source file. + * @return The file's grammar. + */ + public static Optional uriToGrammar(String uriString) { + URI uri; + try { + uri = new URI(uriString); + } catch (URISyntaxException e) { + throw new IllegalArgumentException("Invalid URI", e); + } + + // Search for a grammar declaration in a file in the directory + Matcher m = grammarDecl.matcher(""); + try { + Optional fromGrammarDecl = Files.list(Path.of(uri).getParent()) + .filter(p -> !Files.isDirectory(p) && isValidSilverFileName(p.toString())) + .flatMap((Path p) -> { + try { + return Files.lines(p).findFirst().flatMap(line -> + m.reset(line).find()? Optional.of(m.toMatchResult().group(1)) : + Optional.empty()).stream(); + } catch (IOException e) { + e.printStackTrace(); + return Stream.empty(); + } + }).findFirst(); + if (fromGrammarDecl.isPresent()) { + return fromGrammarDecl; + } + } catch (IOException e) { + e.printStackTrace(); + } + + // Fall back: look for "grammars/" in the path + String path = uri.getPath(); + if (path.contains("grammars/")) { + return Optional.of( + path.substring(0, path.lastIndexOf("/")) + .split("grammars/", 2)[1].replace("/", ":").replace(".", ":")); + } + + return Optional.empty(); + + } + + public static boolean isValidSilverFileName(String file) { + return PisValidSilverFile.invoke(OriginContext.FFI_CONTEXT, new StringCatter(file)); + } +} diff --git a/language-server/launcher/.classpath b/language-server/launcher/.classpath new file mode 100644 index 000000000..9ba41a249 --- /dev/null +++ b/language-server/launcher/.classpath @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/language-server/launcher/.gitignore b/language-server/launcher/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/language-server/launcher/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/language-server/launcher/.project b/language-server/launcher/.project new file mode 100644 index 000000000..b85acda7f --- /dev/null +++ b/language-server/launcher/.project @@ -0,0 +1,34 @@ + + + launcher + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.m2e.core.maven2Builder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.m2e.core.maven2Nature + + + + 1665108613767 + + 30 + + org.eclipse.core.resources.regexFilterMatcher + node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ + + + + diff --git a/language-server/launcher/.settings/org.eclipse.jdt.core.prefs b/language-server/launcher/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 000000000..1679c57c7 --- /dev/null +++ b/language-server/launcher/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,576 @@ +eclipse.preferences.version=1 +enableParallelJavaIndexSearch=true +org.eclipse.jdt.core.builder.annotationPath.allLocations=disabled +org.eclipse.jdt.core.builder.cleanOutputFolder=clean +org.eclipse.jdt.core.builder.duplicateResourceTask=warning +org.eclipse.jdt.core.builder.invalidClasspath=abort +org.eclipse.jdt.core.builder.recreateModifiedClassFileInOutputFolder=ignore +org.eclipse.jdt.core.builder.resourceCopyExclusionFilter= +org.eclipse.jdt.core.circularClasspath=warning +org.eclipse.jdt.core.classpath.exclusionPatterns=enabled +org.eclipse.jdt.core.classpath.mainOnlyProjectHasTestOnlyDependency=error +org.eclipse.jdt.core.classpath.multipleOutputLocations=enabled +org.eclipse.jdt.core.classpath.outputOverlappingAnotherSource=error +org.eclipse.jdt.core.codeComplete.argumentPrefixes= +org.eclipse.jdt.core.codeComplete.argumentSuffixes= +org.eclipse.jdt.core.codeComplete.camelCaseMatch=enabled +org.eclipse.jdt.core.codeComplete.deprecationCheck=disabled +org.eclipse.jdt.core.codeComplete.discouragedReferenceCheck=disabled +org.eclipse.jdt.core.codeComplete.fieldPrefixes= +org.eclipse.jdt.core.codeComplete.fieldSuffixes= +org.eclipse.jdt.core.codeComplete.forbiddenReferenceCheck=enabled +org.eclipse.jdt.core.codeComplete.forceImplicitQualification=disabled +org.eclipse.jdt.core.codeComplete.localPrefixes= +org.eclipse.jdt.core.codeComplete.localSuffixes= +org.eclipse.jdt.core.codeComplete.staticFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFieldSuffixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes= +org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes= +org.eclipse.jdt.core.codeComplete.subwordMatch=disabled +org.eclipse.jdt.core.codeComplete.suggestStaticImports=enabled +org.eclipse.jdt.core.codeComplete.visibilityCheck=enabled +org.eclipse.jdt.core.compiler.annotation.inheritNullAnnotations=disabled +org.eclipse.jdt.core.compiler.annotation.missingNonNullByDefaultAnnotation=ignore +org.eclipse.jdt.core.compiler.annotation.nonnull=javax.annotation.Nonnull +org.eclipse.jdt.core.compiler.annotation.nonnull.secondary= +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault=org.eclipse.jdt.annotation.NonNullByDefault +org.eclipse.jdt.core.compiler.annotation.nonnullbydefault.secondary= +org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable +org.eclipse.jdt.core.compiler.annotation.nullable.secondary= +org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.lambda.genericSignature=do not generate +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.shareCommonFinallyBlocks=disabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=11 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +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 +org.eclipse.jdt.core.compiler.doc.comment.support=enabled +org.eclipse.jdt.core.compiler.emulateJavacBug8031744=enabled +org.eclipse.jdt.core.compiler.generateClassFiles=enabled +org.eclipse.jdt.core.compiler.maxProblemPerUnit=100 +org.eclipse.jdt.core.compiler.problem.APILeak=warning +org.eclipse.jdt.core.compiler.problem.annotatedTypeArgumentToUnannotated=info +org.eclipse.jdt.core.compiler.problem.annotationSuperInterface=warning +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.autoboxing=ignore +org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning +org.eclipse.jdt.core.compiler.problem.deadCode=warning +org.eclipse.jdt.core.compiler.problem.deadCodeInTrivialIfStatement=disabled +org.eclipse.jdt.core.compiler.problem.deprecation=warning +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 +org.eclipse.jdt.core.compiler.problem.fatalOptionalError=disabled +org.eclipse.jdt.core.compiler.problem.fieldHiding=ignore +org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning +org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=warning +org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning +org.eclipse.jdt.core.compiler.problem.hiddenCatchBlock=warning +org.eclipse.jdt.core.compiler.problem.includeNullInfoFromAsserts=disabled +org.eclipse.jdt.core.compiler.problem.incompatibleNonInheritedInterfaceMethod=warning +org.eclipse.jdt.core.compiler.problem.incompleteEnumSwitch=warning +org.eclipse.jdt.core.compiler.problem.indirectStaticAccess=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadoc=ignore +org.eclipse.jdt.core.compiler.problem.invalidJavadocTags=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsDeprecatedRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsNotVisibleRef=disabled +org.eclipse.jdt.core.compiler.problem.invalidJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.localVariableHiding=ignore +org.eclipse.jdt.core.compiler.problem.methodWithConstructorName=warning +org.eclipse.jdt.core.compiler.problem.missingDefaultCase=ignore +org.eclipse.jdt.core.compiler.problem.missingDeprecatedAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingEnumCaseDespiteDefault=disabled +org.eclipse.jdt.core.compiler.problem.missingHashCodeMethod=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocComments=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocCommentsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingJavadocTagDescription=return_tag +org.eclipse.jdt.core.compiler.problem.missingJavadocTags=ignore +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsMethodTypeParameters=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsOverriding=disabled +org.eclipse.jdt.core.compiler.problem.missingJavadocTagsVisibility=public +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore +org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotationForInterfaceMethodImplementation=enabled +org.eclipse.jdt.core.compiler.problem.missingSerialVersion=ignore +org.eclipse.jdt.core.compiler.problem.missingSynchronizedOnInheritedMethod=ignore +org.eclipse.jdt.core.compiler.problem.noEffectAssignment=warning +org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=warning +org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore +org.eclipse.jdt.core.compiler.problem.nonnullParameterAnnotationDropped=warning +org.eclipse.jdt.core.compiler.problem.nonnullTypeVariableFromLegacyInvocation=warning +org.eclipse.jdt.core.compiler.problem.nullAnnotationInferenceConflict=warning +org.eclipse.jdt.core.compiler.problem.nullReference=warning +org.eclipse.jdt.core.compiler.problem.nullSpecViolation=warning +org.eclipse.jdt.core.compiler.problem.nullUncheckedConversion=warning +org.eclipse.jdt.core.compiler.problem.overridingMethodWithoutSuperInvocation=ignore +org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning +org.eclipse.jdt.core.compiler.problem.parameterAssignment=ignore +org.eclipse.jdt.core.compiler.problem.pessimisticNullAnalysisForFreeTypeVariables=warning +org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=ignore +org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning +org.eclipse.jdt.core.compiler.problem.potentiallyUnclosedCloseable=ignore +org.eclipse.jdt.core.compiler.problem.rawTypeReference=warning +org.eclipse.jdt.core.compiler.problem.redundantNullAnnotation=warning +org.eclipse.jdt.core.compiler.problem.redundantNullCheck=ignore +org.eclipse.jdt.core.compiler.problem.redundantSpecificationOfTypeArguments=ignore +org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=warning +org.eclipse.jdt.core.compiler.problem.reportMethodCanBePotentiallyStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportMethodCanBeStatic=ignore +org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore +org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled +org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=warning +org.eclipse.jdt.core.compiler.problem.suppressOptionalErrors=disabled +org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled +org.eclipse.jdt.core.compiler.problem.suppressWarningsNotFullyAnalysed=info +org.eclipse.jdt.core.compiler.problem.syntacticNullAnalysisForFields=disabled +org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore +org.eclipse.jdt.core.compiler.problem.tasks=warning +org.eclipse.jdt.core.compiler.problem.terminalDeprecation=warning +org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning +org.eclipse.jdt.core.compiler.problem.unavoidableGenericTypeProblems=enabled +org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning +org.eclipse.jdt.core.compiler.problem.unclosedCloseable=warning +org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=ignore +org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=ignore +org.eclipse.jdt.core.compiler.problem.uninternedIdentityComparison=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentType=warning +org.eclipse.jdt.core.compiler.problem.unlikelyCollectionMethodArgumentTypeStrict=disabled +org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType=info +org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore +org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=ignore +org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore +org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName=warning +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=ignore +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled +org.eclipse.jdt.core.compiler.problem.unusedExceptionParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedImport=warning +org.eclipse.jdt.core.compiler.problem.unusedLabel=warning +org.eclipse.jdt.core.compiler.problem.unusedLocal=warning +org.eclipse.jdt.core.compiler.problem.unusedObjectAllocation=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameter=ignore +org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled +org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled +org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning +org.eclipse.jdt.core.compiler.problem.unusedTypeArgumentsForMethodInvocation=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.processAnnotations=disabled +org.eclipse.jdt.core.compiler.release=disabled +org.eclipse.jdt.core.compiler.source=11 +org.eclipse.jdt.core.compiler.storeAnnotations=disabled +org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled +org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL +org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX +org.eclipse.jdt.core.computeJavaBuildOrder=ignore +org.eclipse.jdt.core.encoding=utf8 +org.eclipse.jdt.core.formatter.align_assignment_statements_on_columns=false +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_selector_in_method_invocation_on_expression_first_line=true +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.align_variable_declarations_on_columns=false +org.eclipse.jdt.core.formatter.align_with_spaces=false +org.eclipse.jdt.core.formatter.alignment_for_additive_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_enum_constant=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_field=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_local_variable=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_method=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_package=49 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_parameter=0 +org.eclipse.jdt.core.formatter.alignment_for_annotations_on_type=49 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assertion_message=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_bitwise_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_loops=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression_chain=0 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_arrow=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_switch_case_with_colon=16 +org.eclipse.jdt.core.formatter.alignment_for_logical_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_module_statements=16 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_multiplicative_operator=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_record_components=16 +org.eclipse.jdt.core.formatter.alignment_for_relational_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_shift_operator=0 +org.eclipse.jdt.core.formatter.alignment_for_string_concatenation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_record_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_switch_case_with_arrow=20 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_annotations=0 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_last_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_abstract_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_statement_group_in_switch=0 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_constructor=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_record_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.align_tags_descriptions_grouped=true +org.eclipse.jdt.core.formatter.comment.align_tags_names_descriptions=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=false +org.eclipse.jdt.core.formatter.comment.indent_root_tags=false +org.eclipse.jdt.core.formatter.comment.indent_tag_description=false +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_between_different_tags=do not insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=do not insert +org.eclipse.jdt.core.formatter.comment.line_length=80 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=false +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_record_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=true +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_permitted_types=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_record_components=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_switch_case_expressions=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_not_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_additive_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_case=insert +org.eclipse.jdt.core.formatter.insert_space_before_arrow_in_switch_default=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_bitwise_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_permitted_types=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_record_components=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_switch_case_expressions=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_logical_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_multiplicative_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_constructor=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_record_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_record_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_relational_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_shift_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_string_concatenation=insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=false +org.eclipse.jdt.core.formatter.join_wrapped_lines=false +org.eclipse.jdt.core.formatter.keep_annotation_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_anonymous_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_code_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_enum_constant_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_enum_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_if_then_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_lambda_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_loop_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_method_body_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_record_constructor_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_record_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_simple_do_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_for_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_simple_getter_setter_on_one_line=false +org.eclipse.jdt.core.formatter.keep_simple_while_body_on_same_line=false +org.eclipse.jdt.core.formatter.keep_switch_body_block_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_switch_case_with_arrow_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_type_declaration_on_one_line=one_line_never +org.eclipse.jdt.core.formatter.lineSplit=120 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_after_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_code_block=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_end_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_blank_lines_before_code_block=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_record_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.text_block_indentation=0 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_additive_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assertion_message_operator=true +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_bitwise_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_logical_operator=true +org.eclipse.jdt.core.formatter.wrap_before_multiplicative_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_before_relational_operator=true +org.eclipse.jdt.core.formatter.wrap_before_shift_operator=true +org.eclipse.jdt.core.formatter.wrap_before_string_concatenation=true +org.eclipse.jdt.core.formatter.wrap_before_switch_case_arrow_operator=false +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.incompatibleJDKLevel=ignore +org.eclipse.jdt.core.incompleteClasspath=error +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter +org.eclipse.jdt.core.timeoutForParameterNameFromAttachedJavadoc=50 diff --git a/language-server/launcher/.settings/org.eclipse.m2e.core.prefs b/language-server/launcher/.settings/org.eclipse.m2e.core.prefs new file mode 100644 index 000000000..f897a7f1c --- /dev/null +++ b/language-server/launcher/.settings/org.eclipse.m2e.core.prefs @@ -0,0 +1,4 @@ +activeProfiles= +eclipse.preferences.version=1 +resolveWorkspaceProjects=true +version=1 diff --git a/language-server/launcher/pom.xml b/language-server/launcher/pom.xml new file mode 100644 index 000000000..b89098089 --- /dev/null +++ b/language-server/launcher/pom.xml @@ -0,0 +1,57 @@ + + + silver-langserver-parent + edu.umn.cs.melt + 0.1.0-SNAPSHOT + ../pom.xml + + 4.0.0 + + launcher + jar + + + + silver-langserver + edu.umn.cs.melt + 0.1.0-SNAPSHOT + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.2.0 + + + + *:* + + META-INF/*.SF + META-INF/*.DSA + META-INF/*.RSA + + + + + + StdioLauncher + + + ${project.artifactId} + + + + package + + shade + + + + + + + diff --git a/language-server/launcher/src/main/java/StdioLauncher.java b/language-server/launcher/src/main/java/StdioLauncher.java new file mode 100644 index 000000000..32d504a31 --- /dev/null +++ b/language-server/launcher/src/main/java/StdioLauncher.java @@ -0,0 +1,55 @@ +import edu.umn.cs.melt.silver.langserver.SilverLanguageServer; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.launch.LSPLauncher; +import org.eclipse.lsp4j.services.LanguageClient; + +import java.io.InputStream; +import java.io.OutputStream; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +/** + * Launcher for silver language server. + */ +public class StdioLauncher { + public static void main(String[] args) throws ExecutionException, InterruptedException { + // As we are using system std io channels + // we need to reset and turn off the logging globally + // So our client->server communication doesn't get interrupted. + LogManager.getLogManager().reset(); + Logger globalLogger = Logger.getLogger(Logger.GLOBAL_LOGGER_NAME); + globalLogger.setLevel(Level.OFF); + + // start the language server + startServer(System.in, System.out); + } + + /** + * Start the language server. + * @param in System Standard input stream + * @param out System standard output stream + * @throws ExecutionException Unable to start the server + * @throws InterruptedException Unable to start the server + */ + private static void startServer(InputStream in, OutputStream out) throws ExecutionException, InterruptedException { + // Initialize the SilverLanguageServer + SilverLanguageServer silverLanguageServer = new SilverLanguageServer(); + // Create JSON RPC launcher for SilverLanguageServer instance. + Launcher launcher = LSPLauncher.createServerLauncher(silverLanguageServer, in, out); + + // Get the client that request to launch the LS. + LanguageClient client = launcher.getRemoteProxy(); + + // Set the client to language server + silverLanguageServer.connect(client); + + // Start the listener for JsonRPC + Future startListening = launcher.startListening(); + + // Get the computed result from LS. + startListening.get(); + } +} diff --git a/language-server/pom.xml b/language-server/pom.xml new file mode 100644 index 000000000..647621fe1 --- /dev/null +++ b/language-server/pom.xml @@ -0,0 +1,37 @@ + + + edu.umn.cs.melt + 4.0.0 + silver-langserver-parent + pom + 0.1.0-SNAPSHOT + + + org.eclipse.lsp4j + org.eclipse.lsp4j + ${lsp4j.version} + + + + langserver + launcher + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 11 + 11 + + + + + + 0.14.0 + + diff --git a/make-dist b/make-dist index 2cae04f6f..c1d95f79a 100755 --- a/make-dist +++ b/make-dist @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu @@ -33,16 +33,18 @@ tar -zcvf $SV.tar.gz \ $SV/support/gedit \ $SV/support/nailgun \ $SV/tutorials/ \ - $SV/grammars/core/ \ - $SV/grammars/lib/extcore/ \ - $SV/grammars/lib/xml/ \ + $SV/grammars/silver/core/ \ + $SV/grammars/silver/xml/ \ + $SV/grammars/silver/regex/ \ + $SV/grammars/silver/rewrite/ \ $SV/grammars/silver/testing/ \ $SV/grammars/silver/util/cmdargs/ \ $SV/grammars/silver/util/deque/ \ $SV/grammars/silver/util/treemap/ \ - $SV/grammars/silver/util/raw/treemap/ \ - $SV/grammars/silver/util/raw/treeset/ \ - $SV/grammars/silver/util/raw/graph/ \ + $SV/grammars/silver/util/treeset/ \ + $SV/grammars/silver/util/graph/ \ + $SV/grammars/silver/util/subprocess/ \ + $SV/grammars/silver/util/random/ \ $SV/grammars/silver/langutil/ \ $SV/grammars/silver/reflect/ \ $SV/COPYING.LESSER \ diff --git a/make-docs b/make-docs new file mode 100755 index 000000000..c8b2de955 --- /dev/null +++ b/make-docs @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -eu + +if [ $0 != "./make-docs" ]; then + echo "Run as ./make-docs" + exit 1 +fi + +: ${DOCGRAMMAR="silver:compiler:extension:doc:extra"} + +rm -rf ./generated/doc + +./support/bin/silver --dont-translate --doc --clean --warn-error $DOCGRAMMAR + diff --git a/make-ide b/make-ide index 9856557c6..4bc99691e 100755 --- a/make-ide +++ b/make-ide @@ -1,10 +1,10 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu # Make sure any compiler changes make it before we build ./self-compile -cp build/silver.composed.Default.jar jars/ +cp build/silver.compiler.composed.Default.jar jars/ # Rebuild the IDE-specific runtime jars cd runtime/imp @@ -12,15 +12,15 @@ cd runtime/imp cd ../.. # Create plug-in sources and .class files. -# this creates a silver.composed.idetest.jar that is not used, but +# this creates a silver.compiler.composed.idetest.jar that is not used, but # this does create stuff in the generated directory that is used in # the next step -SV_BUILD_TARGET="silver:composed:idetest" ./self-compile --clean +SV_BUILD_TARGET="silver:compiler:composed:idetest" ./self-compile --clean # Create the Eclipse repository. -# The repository is in the generated/ide/silver.composed.idetest +# The repository is in the generated/ide/silver.compiler.composed.idetest # directory. To make it public run the make-ide-dist script. -cd generated/ide/silver.composed.idetest +cd generated/ide/silver.compiler.composed.idetest mvn package TARGET=$(pwd)/updatesite/target/repository @@ -28,7 +28,7 @@ TARGET=$(pwd)/updatesite/target/repository echo "..." echo "..." echo "..." -echo "Be sure to increment IDE version number in silver:composed:idetest:Main.sv" +echo "Be sure to increment IDE version number in silver:compiler:composed:idetest:Main.sv" echo "..." echo "..." echo "Reminder: add $TARGET as path to find update site in eclipse, if using directly" diff --git a/make-ide-dist b/make-ide-dist index 4d82961be..a037c9a11 100755 --- a/make-ide-dist +++ b/make-ide-dist @@ -1,9 +1,9 @@ -#!/bin/bash +#!/usr/bin/env bash set -e IDEDIR="/web/research/melt.cs.umn.edu/downloads/silver-eclipse-beta" -ZIPFILE="generated/ide/silver.composed.idetest/updatesite/target/SILVER_UPDATESITE.zip" +ZIPFILE="generated/ide/silver.compiler.composed.idetest/updatesite/target/SILVER_UPDATESITE.zip" if [ ! -d $IDEDIR ]; then echo "Must be running on a umn machine in the melt group." @@ -16,7 +16,7 @@ if [ ! -a $ZIPFILE ]; then fi echo "..." -echo "Be sure to increment IDE version number in silver:composed:idetest:Main.sv" +echo "Be sure to increment IDE version number in silver:compiler:composed:idetest:Main.sv" echo "..." rm -Rf ${IDEDIR}/* diff --git a/publish-jars b/publish-jars index ca65c6f0a..538bfad00 100755 --- a/publish-jars +++ b/publish-jars @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu @@ -17,7 +17,7 @@ if [ ! -d jars ]; then fi # I like to keep a backup series of our jars... -cp $JARS_STORE/silver.composed.Default.jar $JARS_BACKUP/silver.composed.Default.jar.${DT} +cp $JARS_STORE/silver.compiler.composed.Default.jar $JARS_BACKUP/silver.compiler.composed.Default.jar.${DT} # Do the copy with rsync, so we only copy if the file changed... # -c Use checksums and avoid copying if it's the same. diff --git a/rebuild-runtime b/rebuild-runtime new file mode 100755 index 000000000..f2e9a81c6 --- /dev/null +++ b/rebuild-runtime @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu + +echo === BUILD JAVA RUNTIME === +cd runtime/java +ant +echo === INSTALL NEW RUNTIME === +cd ../.. +cp runtime/java/*.jar jars diff --git a/resources/ide_skeleton/feature/build.properties b/resources/ide_skeleton/feature/build.properties deleted file mode 100644 index 64f93a9f0..000000000 --- a/resources/ide_skeleton/feature/build.properties +++ /dev/null @@ -1 +0,0 @@ -bin.includes = feature.xml diff --git a/resources/ide_skeleton/feature/feature.xml b/resources/ide_skeleton/feature/feature.xml deleted file mode 100644 index 4d07def9a..000000000 --- a/resources/ide_skeleton/feature/feature.xml +++ /dev/null @@ -1,48 +0,0 @@ - - - - - @FEATURE_DESCRIPTION_TEXT@ - - - - @FEATURE_COPYRIGHT_TEXT@ - - - - @FEATURE_LICENSE_TEXT@ - - - - - - - - - - - - diff --git a/resources/ide_skeleton/feature/pom.xml b/resources/ide_skeleton/feature/pom.xml deleted file mode 100644 index eaed96846..000000000 --- a/resources/ide_skeleton/feature/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - 4.0.0 - @GROUP_ID@ - @GROUP_ID@.feature - @IDE_VERSION@.@IDE_BUILD_TIMESTAMP@ - eclipse-feature - @LANG_NAME@ Eclipse feature - - - @GROUP_ID@ - parent - @IDE_VERSION@ - ../ - - - diff --git a/resources/ide_skeleton/plugin/META-INF/MANIFEST.MF b/resources/ide_skeleton/plugin/META-INF/MANIFEST.MF deleted file mode 100644 index b6cde98e6..000000000 --- a/resources/ide_skeleton/plugin/META-INF/MANIFEST.MF +++ /dev/null @@ -1,24 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: @LANG_NAME@ -Bundle-SymbolicName: @GROUP_ID@;singleton:=true -Bundle-Version: @IDE_VERSION@.@IDE_BUILD_TIMESTAMP@ -Bundle-RequiredExecutionEnvironment: JavaSE-1.6 -Bundle-ClassPath: ., - @LANG_COMPOSED@.jar, - CopperRuntime.jar, - SilverRuntime.jar, - IDEPluginRuntime.jar -Require-Bundle: org.eclipse.core.runtime, - org.eclipse.core.resources, - org.eclipse.imp.runtime, - org.eclipse.ui, - org.eclipse.jface.text, - org.eclipse.jdt.core, - org.eclipse.ui.editors, - org.eclipse.ui.workbench.texteditor, - org.eclipse.ui.console, - org.eclipse.ui.ide, - org.eclipse.ant.core -Bundle-Activator: @PKG_NAME@.Plugin -Eclipse-LazyStart: true diff --git a/resources/ide_skeleton/plugin/build.properties b/resources/ide_skeleton/plugin/build.properties deleted file mode 100644 index 129fa0026..000000000 --- a/resources/ide_skeleton/plugin/build.properties +++ /dev/null @@ -1,11 +0,0 @@ -source.. = src/ -output.. = bin/ -bin.includes = META-INF/,\ - icons/,\ - resource/,\ - .,\ - @LANG_COMPOSED@.jar,\ - CopperRuntime.jar,\ - SilverRuntime.jar,\ - IDEPluginRuntime.jar,\ - plugin.xml diff --git a/resources/ide_skeleton/plugin/icons/fldr_obj.gif b/resources/ide_skeleton/plugin/icons/fldr_obj.gif deleted file mode 100644 index 51e703b1b..000000000 Binary files a/resources/ide_skeleton/plugin/icons/fldr_obj.gif and /dev/null differ diff --git a/resources/ide_skeleton/plugin/icons/link_obj.gif b/resources/ide_skeleton/plugin/icons/link_obj.gif deleted file mode 100644 index 5fd9c9494..000000000 Binary files a/resources/ide_skeleton/plugin/icons/link_obj.gif and /dev/null differ diff --git a/resources/ide_skeleton/plugin/pom.xml b/resources/ide_skeleton/plugin/pom.xml deleted file mode 100644 index 68894fa0e..000000000 --- a/resources/ide_skeleton/plugin/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - 4.0.0 - @GROUP_ID@ - @GROUP_ID@ - @IDE_VERSION@.@IDE_BUILD_TIMESTAMP@ - eclipse-plugin - @LANG_NAME@ Eclipse plugin - - - @GROUP_ID@ - parent - @IDE_VERSION@ - ../ - - - diff --git a/resources/ide_skeleton/pom.xml b/resources/ide_skeleton/pom.xml deleted file mode 100644 index 2e14e29c0..000000000 --- a/resources/ide_skeleton/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ - - 4.0.0 - @GROUP_ID@ - parent - @IDE_VERSION@ - pom - IDE for @LANG_NAME@ - - - ./plugin - ./feature - ./updatesite - - - - 0.22.0 - - - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho-version} - true - - - - - - - helios - p2 - http://download.eclipse.org/releases/helios/ - - - imp - p2 - http://melt.cs.umn.edu/downloads/imp-mirror/ - - - diff --git a/resources/ide_skeleton/updatesite/category.xml b/resources/ide_skeleton/updatesite/category.xml deleted file mode 100644 index a90aa6bd6..000000000 --- a/resources/ide_skeleton/updatesite/category.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/resources/ide_skeleton/updatesite/pom.xml b/resources/ide_skeleton/updatesite/pom.xml deleted file mode 100644 index d7335a2bc..000000000 --- a/resources/ide_skeleton/updatesite/pom.xml +++ /dev/null @@ -1,16 +0,0 @@ - - 4.0.0 - @GROUP_ID@ - @GROUP_ID@.updatesite - @IDE_VERSION@.@IDE_BUILD_TIMESTAMP@ - eclipse-repository - @LANG_NAME@ Eclipse update site - - - @GROUP_ID@ - parent - @IDE_VERSION@ - ../ - - - diff --git a/runtime/.classpath b/runtime/.classpath deleted file mode 100644 index a9869922d..000000000 --- a/runtime/.classpath +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - - - - - diff --git a/runtime/.project b/runtime/.project deleted file mode 100644 index af2b30c03..000000000 --- a/runtime/.project +++ /dev/null @@ -1,18 +0,0 @@ - - - Silver runtime - - - Copper run-time - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - diff --git a/runtime/imp/build.sh b/runtime/imp/build.sh deleted file mode 100755 index f8c0aa985..000000000 --- a/runtime/imp/build.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/bash - -# On CS machines, load the module 'java/maven' - -set -e - -### Sanity checks -if [ ! -d ../../runtime ]; then - echo "In wrong directory? Run from silver/runtime/imp as ./build.sh" - exit 1 -fi - -cd main - -### clean up generated code that's stale -if [ -d src/core ]; then - rm -rf src/core src/ide src/silver -fi - -### regenerate fresh code: - -# We need to build this with the knowledge of what's generated by the grammars -# core and ide. So use silver to generate the corresponding java files here. -java -jar ../../../jars/silver.composed.Default.jar -G . ide -rm build.xml - -### build -mvn clean package - -#cp target/edu.umn.cs.melt.ide*.jar ../../../jars/IDEPluginRuntime.jar - -### Okay, we cheated to get core and ide in. Now strip them. -mkdir temp -cd temp -jar xf ../target/edu.umn.cs.melt.ide*.jar -rm -r ide core silver -jar cmf META-INF/MANIFEST.MF ../IDEPluginRuntime.jar * -cd .. -rm -r temp -mv IDEPluginRuntime.jar ../../../jars/ - -# Avoid cleaning up generated code too eagerly, let the next run do it. -#rm -r src/core src/ide - -# unnecessary, but for symmetry, leave 'main' -cd .. - diff --git a/runtime/imp/main/META-INF/MANIFEST.MF b/runtime/imp/main/META-INF/MANIFEST.MF deleted file mode 100644 index 9a73994ee..000000000 --- a/runtime/imp/main/META-INF/MANIFEST.MF +++ /dev/null @@ -1,25 +0,0 @@ -Manifest-Version: 1.0 -Bundle-ManifestVersion: 2 -Bundle-Name: Runtime for Silver-based IDE -Bundle-SymbolicName: edu.umn.cs.melt.ide -Bundle-Version: 1.0.0 -Bundle-Vendor: zhoux738@umn.edu -Export-Package: edu.umn.cs.melt.ide.copper;version="1.0.0", - edu.umn.cs.melt.ide.copper.coloring;version="1.0.0", - edu.umn.cs.melt.ide.silver.misc;version="1.0.0", - edu.umn.cs.melt.ide.silver.property;version="1.0.0", - edu.umn.cs.melt.ide.util;version="1.0.0", - edu.umn.cs.melt.ide.silver.property.ui;version="1.0.0" -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Bundle-ClassPath: . -Require-Bundle: org.eclipse.core.runtime, - org.eclipse.core.resources, - org.eclipse.imp.runtime, - org.eclipse.ui, - org.eclipse.jface.text, - org.eclipse.jdt.core, - org.eclipse.ui.editors, - org.eclipse.ui.workbench.texteditor, - org.eclipse.ui.console, - org.eclipse.ui.ide, - org.eclipse.ant.core diff --git a/runtime/imp/main/build.properties b/runtime/imp/main/build.properties deleted file mode 100644 index b107977f4..000000000 --- a/runtime/imp/main/build.properties +++ /dev/null @@ -1,3 +0,0 @@ -source.. = src/ -bin.includes = META-INF/,\ - . diff --git a/runtime/imp/main/pom.xml b/runtime/imp/main/pom.xml deleted file mode 100644 index 327492736..000000000 --- a/runtime/imp/main/pom.xml +++ /dev/null @@ -1,135 +0,0 @@ - - - 4.0.0 - edu.umn.cs.melt - edu.umn.cs.melt.ide - 1.0.0 - eclipse-plugin - Runtime for Silver-based IDE - Runtime for Silver-based IDE - - - - - 0.22.0 - - - - - - org.eclipse.tycho - tycho-maven-plugin - ${tycho-version} - true - - - org.eclipse.tycho - target-platform-configuration - ${tycho-version} - - consider - p2 - - - - org.eclipse.tycho - tycho-compiler-plugin - ${tycho-version} - - UTF-8 - - - - - edu.umn.cs.melt - silver - 1.0.0 - system - ${basedir}/../../../jars/SilverRuntime.jar - - - edu.umn.cs.melt - copper - 1.0.0 - system - ${basedir}/../../../jars/CopperRuntime.jar - - - - - - - - - - - helios - p2 - http://download.eclipse.org/releases/helios/ - - - imp - p2 - http://melt.cs.umn.edu/downloads/imp-mirror/ - - - - diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/SourcePositionLocator.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/SourcePositionLocator.java deleted file mode 100644 index ba4cee42c..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/SourcePositionLocator.java +++ /dev/null @@ -1,96 +0,0 @@ -package edu.umn.cs.melt.ide.copper; - -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; -import org.eclipse.imp.model.ICompilationUnit; -import org.eclipse.imp.parser.IParseController; -import org.eclipse.imp.parser.ISourcePositionLocator; - -import common.Node; -import common.Terminal; - - -public class SourcePositionLocator - -implements ISourcePositionLocator { - - private final IParseController fParseController; - - public SourcePositionLocator(IParseController parseController) { - fParseController= parseController; - } - - @Override - public Object findNode(Object astRoot, int offset) { - return findNode(astRoot, offset, offset); - } - - @Override - public Object findNode(Object astRoot, int startOffset, int endOffset) { - - //System.out.println("\nRequested findNode. Not implemented.\n"); - // TODO: only possible solution to this problem is to wrap the ast with parameters, - // to be consumed by another service. - // e.g. imp asks "what's ast at start, end?" and we say - // AstPosition(ast, start, end) as the result. - // then when imp says, "what's the definition site of this ast node?" - // we get an AstPosition, unwrap to ast, then ask ast about definition site - // at start, end. - - return null; - } - - @Override - public int getStartOffset(Object entity) { - - if (entity instanceof Terminal) { - return ((Terminal) entity).getStartOffset(); - } else { - System.out.println("Got asked start for " + entity.getClass().getName()); - } - - return 0; - } - - @Override - public int getEndOffset(Object entity) { - - if (entity instanceof Terminal) { - // Silver uses a position range that is inclusive at the start, and - // exclusive at the end. - // IMP / Eclipse want a range that is includive at BOTH ends. - // So subtract one. - return ((Terminal) entity).getEndOffset() - 1; - } else { - System.out.println("Got asked end for " + entity.getClass().getName()); - } - - return 0; - } - - @Override - public int getLength(Object entity) { - return getEndOffset(entity) - getStartOffset(entity); - } - - @Override - public IPath getPath(Object entity) { - - System.out.println("GetPath requested of source locator"); - - // ??? - - if(fParseController instanceof IParseController){ - return fParseController.getPath(); - } - - if (entity instanceof ICompilationUnit) { - ICompilationUnit cu= (ICompilationUnit) entity; - return cu.getPath(); - } - - return new Path(""); - } - - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/coloring/TextAttributeProvider.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/coloring/TextAttributeProvider.java deleted file mode 100644 index 30265260f..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/copper/coloring/TextAttributeProvider.java +++ /dev/null @@ -1,140 +0,0 @@ -package edu.umn.cs.melt.ide.copper.coloring; - -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; - -import org.eclipse.jface.text.TextAttribute; -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.widgets.Display; - -public class TextAttributeProvider { - - private static Map> map = new HashMap>(); - - /** - * Get an attribute. - *

- * The implementation caches the color demanded. - * - * @param display - * @param r color's Red value - * @param g color's Green value - * @param b color's Blue value - * @param isBold if font is bold - * @param isItalic if font is italic - * @return - */ - public static TextAttribute getAttribute( - Display display, int r, int g, int b, boolean isBold, boolean isItalic){ - if(display==null || r<0 || r>255 || g<0 || g>255 || b<0 || b>255){ - return null; - } - - int style = SWT.NORMAL; - if(isBold){ - style |= SWT.BOLD; - } - if(isItalic){ - style |= SWT.ITALIC; - } - - Color c = getCachedColor(display, r, g, b); - - if(c!=null){ - return new TextAttribute(c, null, style); - } else { - addColorToCache(display, r, g, b); - c = getCachedColor(display, r, g, b); - } - - return new TextAttribute(c, null, style); - } - - /** - * Reset the provider. All colors cached will be disposed. - */ - public void reset(){ - for(Entry> dispMap:map.entrySet()){ - Map colorMap = dispMap.getValue(); - for(Entry cMap:colorMap.entrySet()){ - int key = cMap.getKey(); - Color color = cMap.getValue(); - if(!isBuiltInColor(key)){ - color.dispose(); - } - } - colorMap.clear(); - } - - map.clear(); - } - - private static void addColorToCache(Display display, int r, int g, int b) { - Map colorMap = map.get(display); - if(colorMap==null){ - map.put(display, createNewColorMap(display)); - colorMap = map.get(display); - } - - int key = generateColorKey(r,g,b); - if(!colorMap.containsKey(key)){//Defensive - colorMap.put(key, new Color(display, r, g, b)); - } - } - - private static Color getCachedColor(Display display, int r, int g, int b) { - Map colorMap = map.get(display); - if(colorMap==null){ - map.put(display, createNewColorMap(display)); - colorMap = map.get(display); - } - return colorMap.get(generateColorKey(r,g,b)); - } - - private static Map createNewColorMap(Display display) { - Map colorMap = new HashMap(); - initColorMap(colorMap, display); - return colorMap; - } - - private static void initColorMap(Map map, Display display){ - for(int[] args:BUILT_IN_COLORS){ - map.put(generateColorKey(args[1],args[2],args[3]), display.getSystemColor(args[0])); - } - } - - private static Integer generateColorKey(int r, int g, int b) { - return (r<<16) + (g<<8) + b; - } - - private final static int[][] BUILT_IN_COLORS = new int[][]{ - new int[]{SWT.COLOR_WHITE, 255,255,255}, - new int[]{SWT.COLOR_BLACK, 0,0,0}, - new int[]{SWT.COLOR_RED, 250,0,0}, - new int[]{SWT.COLOR_DARK_RED, 128,0,0}, - new int[]{SWT.COLOR_GREEN, 0,255,0}, - new int[]{SWT.COLOR_DARK_GREEN, 0,128,0}, - new int[]{SWT.COLOR_YELLOW, 255,255,0}, - new int[]{SWT.COLOR_DARK_YELLOW, 128,128,0}, - new int[]{SWT.COLOR_BLUE, 0,0,255}, - new int[]{SWT.COLOR_DARK_BLUE, 0,0,128}, - new int[]{SWT.COLOR_MAGENTA, 255,0,255}, - new int[]{SWT.COLOR_DARK_MAGENTA, 128,0,128}, - new int[]{SWT.COLOR_CYAN, 0,255,255}, - new int[]{SWT.COLOR_DARK_CYAN, 0,128,128}, - new int[]{SWT.COLOR_GRAY, 192,192,192}, - new int[]{SWT.COLOR_DARK_GRAY, 128,128,128} - }; - - private boolean isBuiltInColor(int key) { - for(int[] bicolor:BUILT_IN_COLORS){ - if(generateColorKey(bicolor[1], bicolor[2], bicolor[3]) == key){ - return true; - } - } - return false; - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/Perspective.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/Perspective.java deleted file mode 100644 index abf4c58ab..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/Perspective.java +++ /dev/null @@ -1,38 +0,0 @@ -package edu.umn.cs.melt.ide.eclipse; - -import org.eclipse.ui.IFolderLayout; -import org.eclipse.ui.IPageLayout; -import org.eclipse.ui.IPerspectiveFactory; -import org.eclipse.ui.console.IConsoleConstants; - -public class Perspective implements IPerspectiveFactory { - - @Override - public void createInitialLayout(IPageLayout layout) { - //Basic settings - layout.setEditorAreaVisible(true); - layout.setFixed(false); - - //Get Editor Area - String editorArea = layout.getEditorArea(); - - //Project Explorer - layout.addView(IPageLayout.ID_PROJECT_EXPLORER, IPageLayout.LEFT, 0.20f, editorArea); - - //Bottom Folder - IFolderLayout bottom = layout.createFolder("bottom", IPageLayout.BOTTOM, 0.66f, editorArea); - - //Problem - bottom.addView(IPageLayout.ID_PROBLEM_VIEW); - - //Console - bottom.addView(IConsoleConstants.ID_CONSOLE_VIEW); - - //Progress - bottom.addView(IPageLayout.ID_PROGRESS_VIEW); - - //And a place-holder - bottom.addPlaceholder("*"); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/IPropertyPageTab.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/IPropertyPageTab.java deleted file mode 100644 index ef0901303..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/IPropertyPageTab.java +++ /dev/null @@ -1,47 +0,0 @@ -package edu.umn.cs.melt.ide.eclipse.property; - -import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.dialogs.PropertyPage; - -/** - * This interface defines the set of behaviors required of a tabular item in - * the property page. - *

- * The IDE's property page is organized using a tab folder, which contains - * a varying number of tab items. What items are contained depends on the - * contents of IDE delcaration block. - * - * @see MultiTabPropertyPage the container of these tab items. - */ -public interface IPropertyPageTab { - - /** - * Get the user-visible name of this tab. - */ - String getName(); - - /** - * Called when OK button is clicked. - * @return whether the action was successfully performed. - */ - boolean performOk(); - - /** - * Called when Defaults button is clicked. - */ - void performDefaults(); - - /** - * Fill contents into the given tab item. - * @param item provided by container. - */ - void fillInTabItem(Composite composite); - - /** - * Used to inject the containter. The implementing class may ignore this - * method (dummy impl.) if it doesn't refer to the container. - * @param pp the property page containing this tab - */ - void setPropertyPage(PropertyPage pp); - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/MultiTabPropertyPage.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/MultiTabPropertyPage.java deleted file mode 100644 index 6fe31c26c..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/MultiTabPropertyPage.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.umn.cs.melt.ide.eclipse.property; - -import org.eclipse.core.resources.IProject; -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.TabFolder; -import org.eclipse.swt.widgets.TabItem; -import org.eclipse.ui.dialogs.PropertyPage; - -import edu.umn.cs.melt.ide.impl.SVInterface; -import edu.umn.cs.melt.ide.impl.SVRegistry; - -/** - * project's property page - *

- * This page is organized using tab folder, each tab item under the folder - * backed by a class implementing {@link IPropertyPageTab}. As the containter - * of these tab items, it's injected into IPropertyPageTab using - * {@link IPropertyPageTab#setPropertyPage(MultiTabPropertyPage) - * setPropertyPage(MultiTabPropertyPage)}. - *

- * This class is generated based on what is defined in IDE declaration block. - */ -public class MultiTabPropertyPage extends PropertyPage { - - public MultiTabPropertyPage() { - SVInterface sv = SVRegistry.get(); - tabs = sv.getPropertyTabs(); - names = new String[tabs.length]; - for(int i = 0; i < tabs.length; i++) { - names[i] = tabs[i].getName(); - } - tabNum = tabs.length; - } - - private String[] names; - private IPropertyPageTab[] tabs; - private int tabNum; - - private TabFolder folder; - - @Override - protected Control createContents(Composite parent) { - - //Assembling the page - - //1) The outermost container: a tab folder - folder = new TabFolder(parent, SWT.NULL); - - //2) Create tab items - for(int i=0;i-1){ - return tabs[index]; - } - - return null; - } - - IProject getProject(){ - return (IProject) getElement().getAdapter(IProject.class); - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/TabCommons.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/TabCommons.java deleted file mode 100644 index b89333f57..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/eclipse/property/TabCommons.java +++ /dev/null @@ -1,115 +0,0 @@ -package edu.umn.cs.melt.ide.eclipse.property; - -import java.util.List; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.ui.dialogs.PropertyPage; - -import edu.umn.cs.melt.ide.impl.SVRegistry; -import edu.umn.cs.melt.ide.silver.property.ProjectProperties; -import edu.umn.cs.melt.ide.silver.property.Property; -import edu.umn.cs.melt.ide.silver.property.ui.IPropertyControlsProvider; -import edu.umn.cs.melt.ide.silver.property.ui.PropertyControl; - -public class TabCommons implements IPropertyPageTab { - - private IPropertyControlsProvider provider = SVRegistry.get().getProjectProperties(); - - private List controls; - - private PropertyPage page; - - @Override - public void fillInTabItem(Composite panel) { - GridLayout layout = new GridLayout(); - layout.numColumns = 2; - panel.setLayout(layout); - - ProjectProperties props = getProperties(); - - if(controls==null){ - controls = provider.getPropertyControls(panel); - - for(PropertyControl control:controls){ - Control info = control.getInfoControl(); - Control input = control.getInputControl(); - - Property p = props.get(control.getKey()); - if(p!=null){ - control.setValue(p.getSValue()); - } - - info.setLayoutData(new GridData()); - input.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - } - } - - } - - @Override - public boolean performOk(){ - if(!provider.validateAll()){ - return false; - } - - ProjectProperties props = getProperties(); - - if(controls!=null && props!=null){ - for(PropertyControl control:controls){ - Property p = control.getProperty(); - if(p!=null){ - props.set(p); - } - } - - IProject project = (IProject)page.getElement().getAdapter(IProject.class); - try { - project.refreshLocal(IResource.DEPTH_ONE, null); - } catch (CoreException e) { - //Ignore - } - } - - return true; - } - - @Override - public void performDefaults(){ - ProjectProperties props = getProperties(); - - if(controls!=null && props!=null){ - for(PropertyControl control:controls){ - Property p = control.getProperty(); - if(p!=null){ - control.setValue(p.getDefault()); - } - } - } - } - - @Override - public void setPropertyPage(PropertyPage page){ - this.page = page; - } - - private ProjectProperties getProperties(){ - IProject project = (IProject)page.getElement().getAdapter(IProject.class); - if (project != null) { - return ProjectProperties.getPropertyPersister(project.getLocation().toString()); - } - - return null; - } - - @Override - public String getName() { - return "Common"; - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Builder.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Builder.java deleted file mode 100644 index 1405a3212..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Builder.java +++ /dev/null @@ -1,171 +0,0 @@ -package edu.umn.cs.melt.ide.imp.builders; - -import java.util.Map; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IncrementalProjectBuilder; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; - -import common.ConsCell; -import common.DecoratedNode; -import common.IOToken; -import common.Lazy; -import common.TopNode; -import common.javainterop.ConsCellCollection; - -import core.NIOVal; -import silver.langutil.NMessage; - -import edu.umn.cs.melt.ide.silver.misc.Problem; -import edu.umn.cs.melt.ide.silver.property.ProjectProperties; - -import edu.umn.cs.melt.ide.util.ReflectedCall; - -/** - * Standard eclipse builder implementation. Example configuration: - - - - - - - - - - * - * Runs `build` and optionally `postbuild`. - * These should have Silver type `IOVal<[Message]> ::= IdeProject [IdeProperty] IO`. - */ -public class Builder extends IncrementalProjectBuilder implements IExecutableExtension { - - private ReflectedCall sv_build; - private ReflectedCall sv_postbuild; - private String markerName; - - @Override - public void setInitializationData(IConfigurationElement config, - String propertyName, Object data) throws CoreException { - if(!(data instanceof java.util.Hashtable)) - return; - - java.util.Hashtable d = (java.util.Hashtable)data; - - markerName = d.get("markerName"); - String build = d.get("silver_build"); - if(build != null) - sv_build = new ReflectedCall(build, 3); - String postbuild = d.get("silver_postbuild"); - if(postbuild != null) - sv_postbuild = new ReflectedCall(postbuild, 3); - } - - public Builder() {} - - /** - * General process: - * 1. Constructed. - * 2. `setInitializationData` with data from plugin.xml - * 3. Base class stuff gets initialized, e.g. `setProject` - * 4. We get called. - */ - @Override - synchronized protected IProject[] build(int kind, Map/* - mvn build complains??*/ args, - IProgressMonitor monitor) throws CoreException { - - final IProject project = getProject(); - - // TODO: I'm not sure if this should be here, but it was the behavior of the old builder to create this - IFolder gen_folder = project.getFolder("bin"); - if(!gen_folder.exists()) { - gen_folder.create(IResource.FORCE|IResource.DERIVED|IResource.HIDDEN, true, null); - } - - // TODO: inefficient because we re-parse the properties file every time. should cache - // Recommendation: use project.set/getSessionProperty, probably within ProjectProperties itself. - // After all, why are we doing all this getLocation toString stuff ourselves, when it could do it? - final ProjectProperties properties = - ProjectProperties.getPropertyPersister(project.getLocation().toString()); - - // Run the build - final NIOVal undecorated_build_result = - sv_build.invoke(new Object[]{project, properties.serializeToSilverType(), IOToken.singleton}); - final DecoratedNode build_result = undecorated_build_result.decorate(); - // demand evaluation of io actions - build_result.synthesized(core.Init.core_io__ON__core_IOVal); - - final ConsCell errors = (ConsCell)build_result.synthesized(core.Init.core_iovalue__ON__core_IOVal); - - // Clear old markers this builder had perhaps created. - // TODO: we should preserve existing markers that haven't changed as much as possible. oh well. - project.deleteMarkers(markerName, true, IResource.DEPTH_INFINITE); - - boolean stopBuild = renderMessages(errors, project, markerName); - - if(stopBuild || sv_postbuild == null) - return new IProject[0]; - - // TODO: look up what to do with monitor. this is a point where we could throw if canceled. - if(monitor.isCanceled()) { - throw new OperationCanceledException(); - } - - // In order to update the markers immediately, we run the post build in a separate thread, - // so we can return from this one, updating the workspace. - - // We'll have the thread run exclusively on this builder. - final Builder lock = this; - - Runnable thread = new Runnable() { - @Override - public void run() { - // Now, invoke the post-builder. - synchronized(lock) { - final NIOVal undecorate_post_result = - sv_postbuild.invoke(new Object[]{project, properties.serializeToSilverType(), IOToken.singleton}); - final DecoratedNode post_result = undecorate_post_result.decorate(); - post_result.synthesized(core.Init.core_io__ON__core_IOVal); - - final ConsCell post_errors = (ConsCell)post_result.synthesized(core.Init.core_iovalue__ON__core_IOVal); - - // TODO: it's now possible that we do need to batch these marker creations? - // because we're outside the (probable?) AVOID_UPDATE of the build function. - // (in this separate thread) - - // otoh, this should be used very rarely: normal errors are already created - // this is just special post build failures only. - try { - renderMessages(post_errors, project, markerName); - } catch (CoreException e) { - // TODO: who knows. - e.printStackTrace(); - } - } - } - }; - - // just go in the background - new Thread(thread).start(); - - // done! - return new IProject[0]; - } - - public static boolean renderMessages(ConsCell errors, IProject project, String markerName) throws CoreException { - boolean stopBuild = false; - for(NMessage msg : new ConsCellCollection(errors)) { - // it seems we do not need to worry about batching changes, as a builder gets called - // with AVOID_UPDATE. apparently. I'm guessing. from the fact that markers don't appear - // until this function returns. - Problem p = Problem.extractProblem(msg); - p.createMarker(project, markerName); - stopBuild = stopBuild || p.buildBlocker(); - } - return stopBuild; - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/EnableNature.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/EnableNature.java deleted file mode 100644 index ae91ebeb5..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/EnableNature.java +++ /dev/null @@ -1,95 +0,0 @@ -package edu.umn.cs.melt.ide.imp.builders; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IObjectActionDelegate; -import org.eclipse.ui.IWorkbenchPart; - -/** - * Action performed via menu. - * - * Example configuration: - - - - - - - - * - * Here we get `nature` passed as a hastable in `setInitializationData`. - */ -public class EnableNature implements IObjectActionDelegate, IExecutableExtension { - - private IProject project; - private String natureID; - - @Override - public void setInitializationData(IConfigurationElement config, String property, - Object data) throws CoreException { - if(!(data instanceof java.util.Hashtable)) - return; - - java.util.Hashtable d = (java.util.Hashtable)data; - - natureID = d.get("nature"); - } - - public EnableNature() {} - - /** - * General invocation process: - * 1. Construct - * 2. `setInitializationData` - * 3. unclear! `selectionChanged` must get called? - * 4. `run` gets called. - * - * Then we do this thing. - */ - @Override - public void run(IAction arg0) { - if(natureID == null) { - System.out.println("odd, tried to add null nature from menu."); - return; - } - if(project == null) { - System.out.println("tried to add to null project?"); - return; - } - try { - Nature.addToProject(project, natureID); - } catch (CoreException e) { - // TODO i dunno - e.printStackTrace(); - } - } - - @Override - public void selectionChanged(IAction action, ISelection selection) { - if (selection instanceof IStructuredSelection) { - IStructuredSelection ss = (IStructuredSelection) selection; - Object first = ss.getFirstElement(); - - if (first instanceof IProject) { - project = (IProject) first; - } else if (first instanceof IJavaProject) { - project = ((IJavaProject) first).getProject(); - } - } - } - - @Override - public void setActivePart(IAction arg0, IWorkbenchPart arg1) { - } - - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Exporter.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Exporter.java deleted file mode 100644 index 99157e049..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Exporter.java +++ /dev/null @@ -1,121 +0,0 @@ -package edu.umn.cs.melt.ide.imp.builders; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.core.runtime.jobs.Job; - -import org.eclipse.jface.action.IAction; -import org.eclipse.jface.viewers.ISelection; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.ui.IObjectActionDelegate; -import org.eclipse.ui.IWorkbenchPart; - -import common.ConsCell; -import common.DecoratedNode; -import common.IOToken; -import common.Lazy; -import common.TopNode; - -import core.NIOVal; - -import edu.umn.cs.melt.ide.silver.property.ProjectProperties; -import edu.umn.cs.melt.ide.util.ReflectedCall; - -/** - * Action performed via menu. Example configuration: - - - - - - - - - - * - * The export function should have Silver type `IOVal<[Message]> ::= IdeProject [IdeProperty] IO` - */ -public class Exporter implements IObjectActionDelegate, IExecutableExtension { - - private IProject project; - - private String name; // Language / Project name. e.g. "Silver" - private ReflectedCall sv_export; - private String markerName; - - @Override - public void setInitializationData(IConfigurationElement config, String property, - Object data) throws CoreException { - if(!(data instanceof java.util.Hashtable)) - return; - - java.util.Hashtable d = (java.util.Hashtable)data; - - name = d.get("name"); - markerName = d.get("markerName"); - sv_export = new ReflectedCall(d.get("silver_export"), 3); - } - - @Override - public void run(IAction ignored) { - if(project == null) - return; - - final ProjectProperties properties = - ProjectProperties.getPropertyPersister(project.getLocation().toString()); - - Job job = new Job("Exporting " + name + " distributable") { - - @Override - protected IStatus run(final IProgressMonitor monitor) { - - final NIOVal undecorated_export_result = - sv_export.invoke(new Object[]{project, properties.serializeToSilverType(), IOToken.singleton}); - final DecoratedNode export_result = undecorated_export_result.decorate(); - // demand evaluation of io actions - export_result.synthesized(core.Init.core_io__ON__core_IOVal); - - final ConsCell errors = (ConsCell)export_result.synthesized(core.Init.core_iovalue__ON__core_IOVal); - - try { - Builder.renderMessages(errors, project, markerName); - } catch (CoreException e) { - // TODO who knows - e.printStackTrace(); - } - - return Status.OK_STATUS; - } - - }; - - job.schedule(); - - } - - @Override - public void selectionChanged(IAction action, ISelection selection) { - if (selection instanceof IStructuredSelection) { - IStructuredSelection ss = (IStructuredSelection) selection; - Object first = ss.getFirstElement(); - - if (first instanceof IProject) { - project = (IProject) first; - } - } - - } - - @Override - public void setActivePart(IAction arg0, IWorkbenchPart arg1) { - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Nature.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Nature.java deleted file mode 100644 index 6cdd7f9dc..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/Nature.java +++ /dev/null @@ -1,135 +0,0 @@ -package edu.umn.cs.melt.ide.imp.builders; - -import org.eclipse.core.resources.ICommand; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.resources.IProjectNature; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.imp.runtime.IPluginLog; - -/** - * Generic nature for SV plugins. - * - * Adds the builder it is told to via the extension declaration of that nature that uses this class. - * - * Does essentially nothing else. - * - * Example configuration: - - - - - - - - - * - * `parameter` objects are passed in as a hashtable to `setInitializationData`. - */ -public class Nature implements IProjectNature, IExecutableExtension { - - private IProject project; - private String builderID; - - @Override - public void setInitializationData(IConfigurationElement config, String property, - Object data) throws CoreException { - if(!(data instanceof java.util.Hashtable)) - return; - - java.util.Hashtable d = (java.util.Hashtable)data; - - builderID = d.get("builder"); - // maybe this nature should actually know what natureID it has? no need yet though... - } - - /** - * Actually a fully generic function for adding a nature to a project. - * Seems odd something like this doesn't exist already somewhere. - * - * @param project The project to affect. - * @param natureID The ID of the nature to add. - */ - public static void addToProject(IProject project, String natureID) throws CoreException { - IProjectDescription description = project.getDescription(); - String[] natures = description.getNatureIds(); - - for(int i = 0; i < natures.length; i++) { - if(natures[i].equals(natureID)) { - System.out.println("nature " + natureID + " already present."); - return; - } - } - - String[] newNatures = new String[natures.length + 1]; - - System.arraycopy(natures, 0, newNatures, 0, natures.length); - newNatures[natures.length] = natureID; - - description.setNatureIds(newNatures); - project.setDescription(description, null); - } - - /** - * The actual action. This method is usually called like so: - * 1. This object is instantiated - * 2. `setInitializationData` is called with data from plugin.xml - * 3. `setProject` is called - * 4. `configure` is called - * - * So here's all the action: we add the builder to the list of - * "build commands" we find in the "project description" if it's not - * already present. That's all. - */ - @Override - public void configure() throws CoreException { - // TODO: should probably log better somehow. this whole function. - - if(builderID == null) { - System.out.println("no builderID configured for SV nature."); - return; // welp, - } - - IProjectDescription desc = getProject().getDescription(); - ICommand[] cmds = desc.getBuildSpec(); - - for(int i = 0; i < cmds.length; i++) { - if (cmds[i].getBuilderName().equals(builderID)) { - System.out.println("odd, the builder is already present."); - return; - } - } - - ICommand buildCmd = desc.newCommand(); - buildCmd.setBuilderName(builderID); - - ICommand[] newCmds = new ICommand[cmds.length+1]; - - System.arraycopy(cmds, 0, newCmds, 0, cmds.length); - newCmds[cmds.length] = buildCmd; - - desc.setBuildSpec(newCmds); - project.setDescription(desc, null); - - } - - @Override - public void deconfigure() throws CoreException { - // builder is automatically removed by the platform. do nothing - - // does require the builder to be configured as part of the nature - // in plugin.xml though. but we do that, so. - } - - @Override - public IProject getProject() { - return project; - } - @Override - public void setProject(IProject arg0) { - project = arg0; - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/PostActionHandler.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/PostActionHandler.java deleted file mode 100644 index 700496cb4..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/builders/PostActionHandler.java +++ /dev/null @@ -1,24 +0,0 @@ -package edu.umn.cs.melt.ide.imp.builders; - -import java.util.List; -import silver.langutil.NMessage; - -public interface PostActionHandler { - - /** - * Handle the IDE message list returned by Builder. - * - * @param list the message list. - * @return true if the handling is considered successful; false otherwise - */ - boolean handleBuild(List list); - - /** - * Handle the IDE message list returned by Post-Builder. - * - * @param list the message list. - * @return true if the handling is considered successful; false otherwise - */ - boolean handlePostBuild(List list); - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Colorer.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Colorer.java deleted file mode 100644 index fc0750e84..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Colorer.java +++ /dev/null @@ -1,82 +0,0 @@ -package edu.umn.cs.melt.ide.imp.services; - -import java.util.HashMap; - -import org.eclipse.imp.parser.IParseController; -import org.eclipse.imp.services.ITokenColorer; -import org.eclipse.imp.services.base.TokenColorerBase; -import org.eclipse.jface.text.IRegion; -import org.eclipse.jface.text.TextAttribute; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.swt.widgets.Display; - -import edu.umn.cs.melt.ide.copper.coloring.TextAttributeProvider; - -import common.Terminal; - -public class Colorer extends TokenColorerBase implements ITokenColorer, IExecutableExtension { - - private TextAttribute[] attributes; - private HashMap classColorings; - - public Colorer() { - super(); - } - - @Override - public void setInitializationData(IConfigurationElement config, - String propertyName, Object data) throws CoreException { - // - IConfigurationElement fonts[] = config.getChildren("font"); - this.attributes = new TextAttribute[fonts.length]; - HashMap fontIndexes = new HashMap(); - Display display = Display.getDefault(); - - for(int i = 0; i < fonts.length; i++) { - IConfigurationElement elem = fonts[i]; - String name = elem.getAttribute("name"); - int r = Integer.parseInt(elem.getAttribute("r")); - int g = Integer.parseInt(elem.getAttribute("g")); - int b = Integer.parseInt(elem.getAttribute("b")); - boolean bold = Boolean.parseBoolean(elem.getAttribute("bold")); - boolean italic = Boolean.parseBoolean(elem.getAttribute("italic")); - - this.attributes[i] = TextAttributeProvider.getAttribute(display, r, g, b, bold, italic); - fontIndexes.put(name, i); - } - - classColorings = new HashMap(); - - // - for(IConfigurationElement elem : config.getChildren("coloring")) { - String lexerclass = elem.getAttribute("lexerclass"); - String font = elem.getAttribute("font"); - int attribute_index = fontIndexes.get(font); - - classColorings.put(lexerclass, attribute_index); - } - } - - public TextAttribute getColoring(IParseController controller, Object o) { - if (o == null) - return null; - - Terminal token = (Terminal) o; - - for(String lc : token.getLexerClasses()) { - Integer i = this.classColorings.get(lc); - if(i != null) { - return this.attributes[i]; - } - } - - return super.getColoring(controller, token); - - } - - public IRegion calculateDamageExtent(IRegion seed) { - return seed; - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Console.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Console.java deleted file mode 100644 index 6f99f54f9..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/Console.java +++ /dev/null @@ -1,59 +0,0 @@ -package edu.umn.cs.melt.ide.imp.services; - -import org.eclipse.ui.console.ConsolePlugin; -import org.eclipse.ui.console.IConsole; -import org.eclipse.ui.console.IConsoleManager; -import org.eclipse.ui.console.MessageConsole; -import org.eclipse.ui.console.MessageConsoleStream; - -import edu.umn.cs.melt.ide.impl.SVInterface; -import edu.umn.cs.melt.ide.impl.SVRegistry; -import edu.umn.cs.melt.ide.silver.misc.ConsoleLoggingStream; - - -public class Console { - - private static ConsoleLoggingStream clsCache = null; - - /** - * Get the {@link ConsoleLoggingStream} associated with this IDE. - * - * @return - */ - public static ConsoleLoggingStream getConsoleLoggingStream() { - SVInterface sv = SVRegistry.get(); // perhaps in the future we'll add a parameter to this function? - String id = sv.name() + " Console"; - if(clsCache == null) { - clsCache = ConsoleLoggingStream.getStream(getConsoleStream(id), getConsoleStream(id)); - } - - return clsCache; - } - - /** - * Get a raw {@link MessageConsoleStream}. {@link #getConsoleLoggingStream()} provides - * a more convenient wrapper for this stream. - * - * @return - */ - private static MessageConsoleStream getConsoleStream(String consoleID) { - MessageConsole console = findConsole(consoleID); - ConsolePlugin.getDefault().getConsoleManager().showConsoleView(console); - return console.newMessageStream(); - } - - private static MessageConsole findConsole(String consoleName) { - IConsoleManager consoleManager = ConsolePlugin.getDefault().getConsoleManager(); - IConsole[] consoles = consoleManager.getConsoles(); - - for(int i = 0; i < consoles.length; i++) { - if(consoles[i].getName().equals(consoleName)) - return (MessageConsole)consoles[i]; - } - - MessageConsole myConsole = new MessageConsole(consoleName, null); - consoleManager.addConsoles(new IConsole[] { myConsole }); - return myConsole; - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/FoldingProvider.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/FoldingProvider.java deleted file mode 100644 index 634cd703e..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/FoldingProvider.java +++ /dev/null @@ -1,72 +0,0 @@ -package edu.umn.cs.melt.ide.imp.services; - -import java.util.HashMap; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.imp.services.base.FolderBase; -import org.eclipse.jface.text.Position; -import org.eclipse.jface.text.source.Annotation; - -import common.ConsCell; -import common.DecoratedNode; -import common.Node; -import common.javainterop.ConsCellCollection; -import core.NLocation; - -import edu.umn.cs.melt.ide.util.ReflectedCall; - -/** - * Computes a set of foldable regions in the IDE. - * Example configuration: - - - - - - * - * The function (above `silver:composed:idetest:fold`) should have - * Silver type `[Location] ::= ASTRoot`. - */ -public class FoldingProvider extends FolderBase implements IExecutableExtension { - - private String language; - private ReflectedCall silvercall; - - @Override - public void setInitializationData(IConfigurationElement config, - String propertyName, Object data) throws CoreException { - // This is deliberately not used yet, but I wanted to show how we can get info - // out of plugin.xml about what language this is. - // Right now, SVRegistry doesn't need a language, but perhaps in the future it should. - language = config.getAttribute("language"); - - for(IConfigurationElement elem : config.getChildren("silvercall")) { - silvercall = new ReflectedCall(elem.getAttribute("function"), 1); - // Do something smarter with error handling later... - break; - } - } - - @Override - protected void sendVisitorToAST( - HashMap newAnnotations, - List annotations, Object _ast) { - - ConsCell folds = silvercall.invoke(new Object[]{_ast}); - - for(NLocation loc : new ConsCellCollection(folds)) { - DecoratedNode dloc = loc.decorate(); - - int startInd = (Integer)dloc.synthesized(core.Init.core_index__ON__core_Location); - int endInd = (Integer)dloc.synthesized(core.Init.core_endIndex__ON__core_Location); - - makeAnnotation(startInd, endInd - startInd); - } - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/IdeParseResult.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/IdeParseResult.java deleted file mode 100644 index 4c3172f93..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/IdeParseResult.java +++ /dev/null @@ -1,42 +0,0 @@ -package edu.umn.cs.melt.ide.imp.services; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.jface.text.IRegion; - -import common.Node; -import common.Terminal; - -public class IdeParseResult { - private NODE value; - private List tokens; - - public IdeParseResult(NODE tree, List terminals) { - value = tree; - tokens = terminals; - } - - public NODE getTree() { - return value; - } - - public Iterator getTokenIterator(IRegion region) { - int startOffset = region.getOffset(); - int endOffset = startOffset + region.getLength(); - - List list = new ArrayList(); - Iterator iter = tokens.iterator(); - while(iter.hasNext()){ - Terminal next = iter.next(); - if(next.getStartOffset() < startOffset || next.getEndOffset() > endOffset){ - continue; - } else { - list.add(next); - } - } - - return list.iterator(); - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/ParseController.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/ParseController.java deleted file mode 100644 index c7592b2e6..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/imp/services/ParseController.java +++ /dev/null @@ -1,142 +0,0 @@ -package edu.umn.cs.melt.ide.imp.services; - -import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; -import java.util.Iterator; -import java.util.List; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.jface.text.IRegion; - -import org.eclipse.imp.parser.ISourcePositionLocator; -import org.eclipse.imp.parser.ParseControllerBase; -import org.eclipse.imp.parser.SimpleAnnotationTypeInfo; -import org.eclipse.imp.services.IAnnotationTypeInfo; -import org.eclipse.imp.services.ILanguageSyntaxProperties; -import org.eclipse.imp.language.LanguageRegistry; - -import common.Node; -import common.Terminal; - -import edu.umn.cs.melt.copper.runtime.engines.CopperParser; -import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; -import edu.umn.cs.melt.copper.runtime.logging.CopperSyntaxError; - -import edu.umn.cs.melt.ide.copper.SourcePositionLocator; -import edu.umn.cs.melt.ide.util.ReflectedCall; - -// https://github.com/impulse-org/imp.runtime/blob/master/src/org/eclipse/imp/parser/IParseController.java -// https://github.com/impulse-org/imp.runtime/blob/master/src/org/eclipse/imp/parser/ParseControllerBase.java - -/** - * Extends ParseControllerBase to use the default implementation of several - * interface methods. The following variables are inherited:
- * protected ISourceProject fProject;
- * protected IPath fFilePath;
- * protected IMessageHandler handler;
- * protected Object fCurrentAst;
- */ -public class ParseController extends ParseControllerBase implements IExecutableExtension { - - private SimpleAnnotationTypeInfo fSimpleAnnotationTypeInfo; - private SourcePositionLocator locator; - private IdeParseResult lastSuccess; - - private CopperParser parser; - // These are silver-generated functions on the parser... - // For the time being, we'll get this be reflection, but we should consider - // adding an interface or something? Can copper parsers have custom interfaces? - private ReflectedCall> getTokens; - private ReflectedCall reset; - - - @Override - public void setInitializationData(IConfigurationElement config, - String propertyName, Object data) throws CoreException { - String language = config.getAttribute("language"); - - for(IConfigurationElement elem : config.getChildren("copper")) { - String parser_class_name = elem.getAttribute("class"); - parser = (CopperParser) - ReflectedCall.newInstance(parser_class_name); - getTokens = new ReflectedCall>(parser_class_name, "getTokens", new Class[0]); - reset = new ReflectedCall(parser_class_name, "reset", new Class[0]); - // Do something smarter with error handling later... - break; - } - - // Normally this is done by ParseControllerBase's non-default constructor - // but we want to do it HERE instead of in the constructor, so whatevs: - fLanguage = LanguageRegistry.findLanguage(language); - - // We might as well just do all our initialization here. - // After all, this stuff might be influenced by plugin.xml in the future, too... - fSimpleAnnotationTypeInfo = new SimpleAnnotationTypeInfo(); - locator = new SourcePositionLocator(this); - } - - public ParseController() { - } - - /** - * Return an instance of SimpleAnnotationTypeInfo - */ - @Override - public IAnnotationTypeInfo getAnnotationTypeInfo() { - return fSimpleAnnotationTypeInfo; - } - - @Override - public ISourcePositionLocator getSourcePositionLocator() { - return locator; - } - - @Override - public ILanguageSyntaxProperties getSyntaxProperties() { - return null;//TODO - } - - @Override - public Object parse(String input, IProgressMonitor monitor) { - try { - lastSuccess = null; - handler.clearMessages(); - Reader reader = new StringReader(input); - IdeParseResult result; - synchronized(parser) { - reset.invokeOn(parser, null); - result = new IdeParseResult(parser.parse(reader, getPath().toFile().getName()), getTokens.invokeOn(parser, null)); - } - lastSuccess = result; - return fCurrentAst = result.getTree(); - } catch (CopperSyntaxError e) { - // We have a point, not an extent, so repeat start/end positions. - handler.handleSimpleMessage( - e.getMessage(), (int)e.getRealCharIndex(), (int)e.getRealCharIndex(), - e.getVirtualColumn(), e.getVirtualColumn(), - e.getVirtualLine(), e.getVirtualLine()); - } catch (CopperParserException e) { - // Not sure how else to error here? - handler.handleSimpleMessage(e.getMessage(), 0, 0, 0, 0, 1, 1); - } catch (IOException e) { - // TODO: this is a bit stupid - e.printStackTrace(); - } - - // Failure-only case - return null; - } - - //Delegate to auto-generated (enhanced) parser - @Override - public Iterator getTokenIterator(IRegion region) { - if(lastSuccess == null) - return null; - return lastSuccess.getTokenIterator(region); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVDefault.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVDefault.java deleted file mode 100644 index 2f619cdab..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVDefault.java +++ /dev/null @@ -1,57 +0,0 @@ -package edu.umn.cs.melt.ide.impl; - -import java.io.IOException; -import java.io.Reader; - -import org.eclipse.core.resources.IProject; - -import common.ConsCell; -import common.IOToken; -import common.Node; -import common.StringCatter; - -import core.NIOVal; -import core.Pioval; -import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; -import edu.umn.cs.melt.ide.eclipse.property.IPropertyPageTab; -import edu.umn.cs.melt.ide.imp.services.IdeParseResult; -import edu.umn.cs.melt.ide.silver.property.ui.IPropertyControlsProvider; - -/** - * This class exists to provide default behaviors for all methods of the interface. - * - * The implementor class should only need to provide those methods it actually uses. The others - * should either be never accessed (thus throwing exceptions below) or have completely valid - * behavior if absent (seen below). - */ -public abstract class SVDefault implements SVInterface { - - // Required methods: - @Override - public abstract String name(); - @Override - public abstract String pluginId(); - @Override - public abstract String markerErrorName(); - @Override - public abstract String getNatureId(); - @Override - public abstract String fileExtension(); - @Override - public abstract IPropertyControlsProvider getProjectProperties(); - @Override - public abstract String getInitialProjectProperties(); - @Override - public abstract IPropertyPageTab[] getPropertyTabs(); - - @Override - public IPropertyControlsProvider getNewFileProperties() { - // Should be provided if the wizard ends up in plugin.xml - throw new UnsupportedOperationException("new file properties requested by not provided by plugin"); - } - @Override - public StringCatter fileStub(ConsCell properties) { - // Should be provided if the wizard ends up in plugin.xml - throw new UnsupportedOperationException("new file stub generator requested by not provided by plugin"); - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVInterface.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVInterface.java deleted file mode 100644 index 5f4859101..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVInterface.java +++ /dev/null @@ -1,87 +0,0 @@ -package edu.umn.cs.melt.ide.impl; - -import java.io.IOException; -import java.io.Reader; - -import org.eclipse.core.resources.IProject; - -import common.ConsCell; -import common.IOToken; -import common.Node; -import common.StringCatter; -import core.NIOVal; - -import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; -import edu.umn.cs.melt.ide.eclipse.property.IPropertyPageTab; -import edu.umn.cs.melt.ide.imp.services.IdeParseResult; -import edu.umn.cs.melt.ide.silver.property.ui.IPropertyControlsProvider; - -/** - * This interface is implemented by two classes: - * 1. The SVDefault class next door. - * 2. The implementation for a language plugin generated by silver. - * - * This is pretty much nothing but a collection of function pointers to silver functions. - * They should appear in the ide plugin declaration in the silver source file. - */ -public interface SVInterface { - /** - * Gets the language name, i.e. that used in the IMP language registry. - * - * @see org.eclipse.imp.language.Language - * @see org.eclipse.imp.language.LanguageRegistry - */ - public String name(); - - /** - * Obtain this plugin's id. - * - * Should *NOT* be used to guess the ids of extension point contibutions - * (e.g. pluginId() + ".nature" or similar) becuase that's crap. - * - * SHOULD be used for trying to open up resources from inside this bundle's jar. - */ - public String pluginId(); - - /** - * Gets the name of the problem marker for this extension. - */ - public String markerErrorName(); - - /** - * Obtain the name of the nature for this plugin. - */ - public String getNatureId(); - - /** - * Get the file extension associated with this plugin. excluding the dot. e.g. "sv" - */ - public String fileExtension(); - - /** - * Obtains a list of properties to request in order to create a new file via wizard. - */ - public IPropertyControlsProvider getNewFileProperties(); - /** - * String ::= [IdeProperty] - * - * Given the properties from {@link #getNewFileProperties()}, generate the file's contents. - * - * @param properties the requested properties - * @return the file's initial contents - */ - public StringCatter fileStub(ConsCell properties); - - /** - * Obtains a list of properties for the project's configuration. - */ - public IPropertyControlsProvider getProjectProperties(); - /** - * Generate the initial project properties configuration file contents. - */ - public String getInitialProjectProperties(); - /** - * Get a set of tabs for the project's properties page. - */ - public IPropertyPageTab[] getPropertyTabs(); -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVRegistry.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVRegistry.java deleted file mode 100644 index 858d8f907..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/impl/SVRegistry.java +++ /dev/null @@ -1,41 +0,0 @@ -package edu.umn.cs.melt.ide.impl; - -/** - * The silver-generated ide plugin will register itself with us as a result of startup. - * - *

NOTE: Currently, we assume that this package is EMBEDDED WITHIN the generated plugin. - * i.e. if there are multiple silver plugins loaded into eclipse, there are multiple - * private silver ide runtimes, each private to its plugin. As a result, we assume there's - * just one implementation to care about. - * - *

This is a poor design, and should be fixed eventually, but I'm not sure what the best - * way to do it is. So for now I have not even tried to do so. - * - *

Okay: here's a better design. When we use the runtime's classes from plugin.xml, we can - * usually pass properties to it. I.e. we could say "use the silver-imp runtime builder class - * and pass it parameter 'plugin-name-to-look-up-in-registry = whatever' to use to look things - * up here. - * - *

Bonus: we can pass parameters to things instantiated by eclipse in plugin.xml. - * Downside: the IMP stuff doesn't really let us do that (according to its schema, however - * technically we can!) BUT there's still a good way: if our registry is by the same - * 'language' key as IMP's language registry, then the imp-instantiated stuff - * can just look at the already existing 'language' attribute, and know what to look up here, too! :D - */ -public class SVRegistry { - private static SVInterface plugin = null; - - public static SVInterface get() { - if(plugin == null) { - throw new UnsupportedOperationException("No plugin has been registered yet!"); - } - return plugin; - } - - public static void register(SVInterface impl) { - if(plugin != null) { - throw new UnsupportedOperationException("A plugin has already been registered!"); - } - plugin = impl; - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/ConsoleLoggingStream.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/ConsoleLoggingStream.java deleted file mode 100644 index 642a3a9d1..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/ConsoleLoggingStream.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.umn.cs.melt.ide.silver.misc; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.graphics.Color; -import org.eclipse.swt.widgets.Display; -import org.eclipse.ui.console.MessageConsoleStream; - -/** - * A wrapper of two standard {@link org.eclipse.ui.console.MessageConsoleStream}s, - * for information and error output respectively. - */ -public class ConsoleLoggingStream { - - private MessageConsoleStream infoStream; - - private MessageConsoleStream errorStream; - - final static Display DISPLAY = Display.getDefault(); - - private final static int SCREEN_WIDTH = 80; - - private static Color RED; - - private ConsoleLoggingStream( - MessageConsoleStream infoMCStream, - MessageConsoleStream errorMCStream) { - infoStream = infoMCStream; - errorStream = errorMCStream; - DISPLAY.syncExec(new Runnable() { - public void run() { - errorStream.setColor(RED); - } - }); - } - - public void info(String message){ - println(infoStream, message); - } - - public void error(String message){ - println(errorStream, message); - } - - private void println(MessageConsoleStream stream, String message) { - String[] strings = splitString(message); - for(String s:strings){ - stream.println(s); - } - } - - private String[] splitString(String message) { - int length = message.length(); - int block = length / SCREEN_WIDTH; - int totalMinusRemainder = block * SCREEN_WIDTH; - int remainderLength = length - totalMinusRemainder; - char[] chars = message.toCharArray(); - - String[] strings = new String[block+1]; - int offset = 0; - for(int i = 0; i < block; i++, offset += SCREEN_WIDTH){ - strings[i] = String.copyValueOf(chars, offset, SCREEN_WIDTH); - } - strings[block] = String.copyValueOf(chars, offset, remainderLength); - - return strings; - } - - private static void init(){ - DISPLAY.syncExec(new Runnable() { - public void run() { - RED = DISPLAY.getSystemColor(SWT.COLOR_RED); - } - }); - } - - private static ConsoleLoggingStream SINGLETON = null; - - public static ConsoleLoggingStream getStream( - MessageConsoleStream infoMCStream, - MessageConsoleStream errorMCStream) { - - if(SINGLETON == null){ - init(); - SINGLETON = new ConsoleLoggingStream(infoMCStream, errorMCStream); - } - - return SINGLETON; - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/Problem.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/Problem.java deleted file mode 100644 index ae1979508..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/misc/Problem.java +++ /dev/null @@ -1,174 +0,0 @@ -package edu.umn.cs.melt.ide.silver.misc; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IMarker; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; - -import common.DecoratedNode; -import common.Lazy; -import common.StringCatter; -import common.TopNode; - -import core.NLocation; -import silver.langutil.NMessage; - -/** - * A wrapper of problem message returned by compiler. - *

- * There are two types of problems, based on its associativity with either file - * or project. For a file problem, the user should provide project, file, and - * location information, in addition to the severity and message. - *

- * For a project problem, the user only needs to provide severity and message. - */ -public class Problem { - - /** - * If an integer value is unknown, it has this value, which shouldn't be - * interpreted literally. - */ - public static final int UNKNOWN = -1; - public static final int INFO = IMarker.SEVERITY_INFO; - public static final int WARNING = IMarker.SEVERITY_WARNING; - public static final int ERROR = IMarker.SEVERITY_ERROR; - - private IPath file; - private int column; - private int line; - private int startInd; - private int endInd; - private String message; - private int level; - private boolean projMsg = false; - - /** - * Create a file-related problem from given argument set. - * - * @param project the project under which the resource declName+fileName - * is found. - * @param filePath the file's path in format "a/b/c", relative to project root - * @param fileName the file's full name, including extension. Note the - * resource can be located under project root using {filePath + "/" + fileName} - * @param line - * @param column - * @param startInd starting index relative to start of file. Use {@link #UNKNOWN} - * if not available. - * @param endInd ending index relative to start of file. Use {@link #UNKNOWN} - * if not available. - * @param severity such as {@link #ERROR} and {@link #WARNING} - * @param message a human-oriented message that will be shown in IDE. - * @return - */ - private static Problem createFileProblem( - IPath file, - int line, int column, int startInd, int endInd, - int severity, String message){ - - return new Problem(file, column, line, message, severity, startInd, endInd, false); - } - - /** - * Create a project-level problem. - *

- * A project problem is not associated with any particular file. - * - * @param severity such as {@link #ERROR} and {@link #WARNING} - * @param message a human-oriented message that will be shown in IDE. - * @return - */ - private static Problem createProjectProblem(int severity, String message){ - return new Problem(null, UNKNOWN, UNKNOWN, message, severity, UNKNOWN, UNKNOWN, true); - } - - /** - * The most primitive constructor. - */ - private Problem(IPath file, int column, int line, String message, int level, - int startInd, int endInd, boolean projMsg) { - this.file = file; - this.column = column; - this.line = line; - this.message = message; - this.level = level; - this.startInd = startInd; - this.endInd = endInd; - this.projMsg = projMsg; - } - - public boolean buildBlocker() { - return level == ERROR; - } - - public static Problem extractProblem(NMessage ideMsg) { - DecoratedNode ideMsgDecNode = ideMsg.decorate(); - - // Extract message, severity, where from Message: - - String message = ((StringCatter)ideMsgDecNode.synthesized(silver.langutil.Init.silver_langutil_message__ON__silver_langutil_Message)).toString(); - int severity = (Integer)ideMsgDecNode.synthesized(silver.langutil.Init.silver_langutil_severity__ON__silver_langutil_Message); - - NLocation where_node = (NLocation)ideMsgDecNode.synthesized(silver.langutil.Init.silver_langutil_where__ON__silver_langutil_Message); - DecoratedNode where = where_node.decorate(); - - // Extract location information from where: - - String filename = ((StringCatter)where.synthesized(core.Init.core_filename__ON__core_Location)).toString(); - - // Treat no file as project-wide - boolean isProjMsg = filename.equals(""); - - if(isProjMsg) - return Problem.createProjectProblem(severity, message); - - int line = (Integer)where.synthesized(core.Init.core_line__ON__core_Location); - int column = (Integer)where.synthesized(core.Init.core_column__ON__core_Location); - int index = (Integer)where.synthesized(core.Init.core_index__ON__core_Location); - int endIndex = (Integer)where.synthesized(core.Init.core_endIndex__ON__core_Location); - - return Problem.createFileProblem( - new Path(filename), line, column, index, endIndex, severity, message); - - } - - public void createMarker(IProject project, String markerType) throws CoreException { - if(projMsg) { - IMarker marker = project.createMarker(markerType); - marker.setAttribute(IMarker.MESSAGE, message); - marker.setAttribute(IMarker.SEVERITY, level); - } else { - IFile f; - // If we get an absolute path to the project, strip it off. - if(project.getLocation().isPrefixOf(file)) { - f = project.getFile(file.makeRelativeTo(project.getLocation())); - } else { - f = project.getFile(file); - } - - if(!f.exists()) { - IMarker marker = project.createMarker(markerType); - marker.setAttribute(IMarker.MESSAGE, "Unknown file with error message: " + f.getFullPath().toString() + " : " + message); - marker.setAttribute(IMarker.SEVERITY, level); - return; - } - - //Reuse ErrorMarkerID for whatever kind of problem - IMarker marker = f.createMarker(markerType); - marker.setAttribute(IMarker.MESSAGE, message); - marker.setAttribute(IMarker.LINE_NUMBER, line); - int index = startInd; - if(index != Problem.UNKNOWN) { - marker.setAttribute(IMarker.CHAR_START, index); - } - index = endInd; - if(index != Problem.UNKNOWN) { - marker.setAttribute(IMarker.CHAR_END, index); - } - marker.setAttribute(IMarker.SEVERITY, level); - - } - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ProjectProperties.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ProjectProperties.java deleted file mode 100644 index 0b53d9896..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ProjectProperties.java +++ /dev/null @@ -1,235 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property; - -import ide.NIdeProperty; -import ide.PmakeIdeProperty; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; -import java.util.Properties; -import java.util.Set; -import java.util.Map.Entry; - -import common.ConsCell; - -/** - * Used to read/write properties from/to project.properties file. - *

- * Property is saved in format: - *

- *   name/type=value
- * 
- * The methods provided by this class allows the plug-in developer to update - * the properties file. If, however, a user modifies the property file - * externally, the change made through this class may be overridden. - */ -public class ProjectProperties { - - private String projectPath; - - /** Raw property file */ - private Properties properties; - - /** Parsed property file */ - private Map map; - - /** The signature of property file when last read */ - private Signature signature; - - /** null if not yet computed, otherwise the Silver value of these properties */ - private ConsCell cachedSilverValue; - - - public static ProjectProperties getPropertyPersister(String projectPath){ - return new ProjectProperties(projectPath); - } - - private ProjectProperties(String projectPath){ - this.projectPath = projectPath; - } - - /** - * Get property. - * - * @param key the property name. - * @return - */ - public Property get(String key){ - init(); - - return map.get(key); - } - - /** - * Set and persist property. - *

- * The property must be already defined in the property file. This method - * is not used to add new property for the project. - * - * @param property - */ - public void set(Property property){ - init(); - - String key = property.getName() + Property.SPLITTER + Property.Type.getString(property.getType()); - - properties.setProperty(key, property.getSValue()); - - persist(); - } - - /** - * Get all the properties. - * - * @return - */ - public Set> getAll() { - init(); - - return map.entrySet(); - } - - /** - * Get property file - */ - private File getPropertiesFile() { - return new File(projectPath, "project.properties"); - } - - private void init(){ - File configFile = getPropertiesFile(); - - if(properties!=null){ - //Check the signature - Signature newSig = createSignature(configFile); - - if(newSig.equals(signature)){ - return; - } else { - signature = newSig; - } - } - - // invalidate the cached value - cachedSilverValue = null; - - //Not initiated or file has been changed - FileInputStream fis = null; - try { - properties = new Properties(); - fis = new FileInputStream(configFile); - properties.load(fis); - } catch (FileNotFoundException e) { - // - } catch (IOException e) { - // - } finally { - if(fis!=null){ - try { - fis.close(); - } catch (IOException e) { - //Ignore - } - } - } - - map = new HashMap(); - - Set> set = properties.entrySet(); - - for(Entry entry:set){ - String key = (String) entry.getKey(); - String value = (String) entry.getValue(); - Property prop = Property.parseProperty(key, value); - - map.put(prop.getName(), prop); - - //System.out.println("Property:" + prop.getName() + ", " + prop.getSValue()); - } - } - - private void persist(){ - File configFile = getPropertiesFile(); - if(configFile==null){ - return; - } - - FileOutputStream fos = null; - try { - fos = new FileOutputStream(configFile); - properties.store(fos, null); - } catch (FileNotFoundException e) { - //Ignore - } catch (IOException e) { - //Ignore - } finally { - if(fos!=null){ - try { - fos.close(); - } catch (IOException e) { - //Ignore - } - } - } - } - - private class Signature { - - long size; - - long modified; - - Signature(long size, long modified) { - this.size = size; - this.modified = modified; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - Signature other = (Signature)obj; - if (modified != other.modified) - return false; - if (size != other.size) - return false; - return true; - } - - } - - private Signature createSignature(File f){ - return new Signature(f.length(), f.lastModified()); - } - - /** - * Converts this properties list to the silver type. - * - * @return Value of Silver type [IdeProperty] (java type ConsCell of NIdeProperty) - */ - public ConsCell serializeToSilverType() { - if(cachedSilverValue != null) - return cachedSilverValue; - - Set> set = this.getAll(); - ConsCell result = ConsCell.nil; - for(Entry entry : set) { - final Property prop = entry.getValue(); - final NIdeProperty h = new PmakeIdeProperty( - new common.StringCatter(entry.getKey()), // == prop.getName() - new common.StringCatter(prop.getType().name()), - new common.StringCatter(prop.getSValue()) - ); - result = new ConsCell(h, result); - } - return result; - } -} - - diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/Property.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/Property.java deleted file mode 100644 index e99d13a30..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/Property.java +++ /dev/null @@ -1,223 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property; - -/** - * A class corresponding to the property entry in project.properties file. - *

- * The entry has the following format: - *

- *   name/type=value
- */
-public class Property {
-
-	public static String SPLITTER = "/";
-	
-	private String name;
-	
-	private Type type;
-
-	private boolean required; //not required by default
-	
-	private String display;
-	
-	private String defaultVal;
-	
-	private String sValue;
-	
-	private int iValue;
-	
-	/**
-	 * Type of property. Mainly determining the behavior of validation.
-	 */
-	public static enum Type {
-		STRING, PATH, URL, INTEGER;
-		
-		static Type getType(String name){
-			if("string".equals(name)){
-				return STRING;
-			} else if("path".equals(name)){
-				return PATH;
-			} else if("url".equals(name)){
-				return URL;
-			} else {//if("integer".equals(name))
-				return INTEGER;
-			}
-		}
-		
-		static String getString(Type type){
-			switch(type){
-			case STRING:
-				return "string";
-			case PATH:
-				return "path";
-			case URL:
-				return "url";
-			case INTEGER:
-				return "integer";
-			}
-			
-			return "string";
-		}
-	}
-	
-	private Property(String name, Type type, String sValue, int iValue) {
-		this(name, type, sValue, iValue, "", false, "");
-	}
-	
-	private Property(
-		String name, Type type, String sValue, int iValue,
-		String defaultVal, boolean required, String display) {
-		this.name = name;
-		this.type = type;
-		this.sValue = sValue;
-		this.iValue = iValue;
-		this.defaultVal = defaultVal;
-		this.required = required;
-		this.display = display;
-	}
-	
-	/**
-	 * Parse property with format:
-     * 
-     *   name:type=value
-     * where type should be a legal value listed in Property.Type enumeration.
-     * 
-	 * @param key
-	 * @param value
-	 * @return
-	 */
-	public static Property parseProperty(String key, String value){
-		//Split name:type into two parts
-		String[] strs = key.split(SPLITTER);
-
-		if(strs.length==2){
-			Type type = Type.getType(strs[1]);
-			switch(type){
-			case STRING:
-				return makeStringProperty(strs[0], value);
-			case PATH:
-				return makePathProperty(strs[0], value);
-			case URL:
-				return makeURLProperty(strs[0], value);
-			case INTEGER:
-				return makeIntegerProperty(strs[0], Integer.parseInt(value));
-			}
-		}
-
-		//Unknown type, default to String
-		return makeStringProperty(key, value);
-	}
-	
-	public static Property makePathProperty(String name, String path){
-		return new Property(name, Type.PATH, path, 0);
-	}
-	
-	public static Property makeURLProperty(String name, String url){
-		return new Property(name, Type.URL, url, 0);
-	}
-	
-	public static Property makeStringProperty(String name, String str){
-		return new Property(name, Type.STRING, str, 0);
-	}
-	
-	public static Property makeIntegerProperty(String name, int val){
-		return new Property(name, Type.INTEGER, "", val);
-	}
-
-	public static Property makePathProperty(String name, String path, 
-		String defaultVal, String display, boolean required){
-		return new Property(name, Type.PATH, path, 0, defaultVal, required, display);
-	}
-	
-	public static Property makeURLProperty(String name, String url, 
-	String defaultVal, String display, boolean required){
-		return new Property(name, Type.URL, url, 0, defaultVal, required, display);
-	}
-	
-	public static Property makeStringProperty(String name, String str, 
-	String defaultVal, String display, boolean required){
-		return new Property(name, Type.STRING, str, 0, defaultVal, required, display);
-	}
-	
-	public static Property makeIntegerProperty(String name, int val, 
-	String defaultVal, String display, boolean required){
-		return new Property(name, Type.INTEGER, "", val, defaultVal, required, display);
-	}
-	
-	public String getName() {
-		return name;
-	}
-
-	/**
-	 * The type of this property, see 
-	 * {@link edu.umn.cs.melt.ide.silver.property.Property.Type Type}.
-	 * @return
-	 */
-	public Type getType() {
-		return type;
-	}
-
-	/**
-	 * Get the value in form of String. Will convert to String if it's not
-	 * string-typed value.
-	 */
-	public String getSValue() {
-		if(type!=Type.INTEGER){
-			return sValue;
-		} else {
-			return String.valueOf(iValue);
-		}
-	}
-
-	public void setSValue(String sValue) {
-		this.sValue = sValue;
-	}
-
-	/**
-	 * Get the value in form of integer. Return always 0 if it's not an 
-	 * integer-convertible value.
-	 */
-	public int getIValue() {
-		return iValue;
-	}
-
-	public void setIValue(int iValue) {
-		this.iValue = iValue;
-	}
-	
-	/**
-	 * Whether this property is required. Used by validation.
-	 * @return true if this property is required, i.e. must be non-empty.
-	 */
-	public boolean isRequired() {
-		return required;
-	}
-
-	/**
-	 * The name of this property to be displayed in UI.
-	 */
-	public String getDisplay() {
-		return display;
-	}
-
-	/**
-	 * The defaultValue of this property. Used also for non-string property.
-	 */
-	public String getDefault() {
-		return defaultVal;
-	}
-	
-	/**
-	 * Reset to default value
-	 */
-	public void reset(){
-		if(type!=Type.INTEGER){
-			setSValue(defaultVal);
-		} else {
-			try {
-				setIValue(Integer.parseInt(defaultVal));
-			} catch (NumberFormatException e) {
-				//Shouldn't happen. Ignored.
-			}
-		}
-	}
-}
diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/AbstractPropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/AbstractPropertyControl.java
deleted file mode 100644
index 143e9e714..000000000
--- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/AbstractPropertyControl.java
+++ /dev/null
@@ -1,43 +0,0 @@
-package edu.umn.cs.melt.ide.silver.property.ui;
-
-import org.eclipse.swt.widgets.Composite;
-
-abstract public class AbstractPropertyControl implements PropertyControl {
-
-	protected Composite panel;
-	protected String name;
-	protected String defaultVal;
-	protected String display;
-	protected boolean isRequired;
-	
-	protected AbstractPropertyControl(
-		Composite panel, String name, String display, String defaultVal, boolean isRequired){
-		this.panel = panel;
-		this.name = name;
-		this.display = display;
-		this.defaultVal = defaultVal;
-		this.isRequired = isRequired;
-	}
-	
-	protected AbstractPropertyControl(Composite panel, String name){
-		this(panel, name, name, "", false);
-	}
-
-	/**
-	 * Whether this field has been filled. A field that is not required is always
-	 * considered filled.
-	 * @param value from input component
-	 */
-	protected boolean isFilled(String value){
-		if((value==null||"".equals(value)) && isRequired){
-			return false;
-		}
-		
-		return true;
-	}
-	
-	public String getDefault(){
-		return defaultVal;
-	}
-	
-}
diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IPropertyControlsProvider.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IPropertyControlsProvider.java
deleted file mode 100644
index e81c255e9..000000000
--- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IPropertyControlsProvider.java
+++ /dev/null
@@ -1,34 +0,0 @@
-package edu.umn.cs.melt.ide.silver.property.ui;
-
-import java.util.List;
-
-import org.eclipse.swt.widgets.Composite;
-
-/**
- * The generated PropertyPage is backed up by a class implementing
- * this interface.
- * 

- * First, PropertyPage can retrieve all the control widgets from this interface - * by calling {@link #getPropertyControls(Composite)} and thus render them - * properly in property page. - *

- * Second, PropertyPage calls {@link #validateAll()} to perform a comprehensive - * input validation in the overridden {@link org.eclipse.ui.dialogs. - * PropertyPage#performOk() PropertyPage.performOk()} method. - */ -public interface IPropertyControlsProvider { - - /** - * Get all the control widgets to render. - * @param panel - * @return a list containing all the {@link PropertyControl}s. - */ - List getPropertyControls(Composite panel); - - /** - * Perform a comprehensive validation over all the input widgets. - * @return true if successful. - */ - boolean validateAll(); - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IntegerPropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IntegerPropertyControl.java deleted file mode 100644 index 59084386a..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/IntegerPropertyControl.java +++ /dev/null @@ -1,90 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property.ui; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import edu.umn.cs.melt.ide.silver.property.Property; - -public class IntegerPropertyControl extends AbstractPropertyControl { - - private Label info; - private Text input; - - public IntegerPropertyControl(Composite panel, String name){ - super(panel, name); - } - - public IntegerPropertyControl(Composite panel, String name, - String display, String defaultVal, boolean isRequired){ - super(panel, name, display, defaultVal, isRequired); - } - - @Override - public Control getInfoControl() { - if(info==null){ - info = new Label(panel, SWT.NONE); - info.setText(display); - info.setToolTipText("An integer"); - } - return info; - } - - @Override - public Control getInputControl() { - if(input==null){ - input = new Text(panel, SWT.BORDER); - } - return input; - } - - @Override - public Property getProperty() { - return Property.makeIntegerProperty( - name, Integer.parseInt(input.getText()), defaultVal, display, isRequired); - } - - @Override - public boolean validate() { - String value = input.getText(); - if(!isFilled(value)){ - input.setToolTipText("This field cannot be empty."); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - boolean valid = true; - try { - Integer.parseInt(value); - } catch (NumberFormatException e) { - valid = false; - } - - if(!valid){ - input.setToolTipText("The value is not an integer."); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - reset(); - return true; - } - - private void reset() { - input.setToolTipText(null); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_WHITE)); - } - - @Override - public String getKey() { - return name; - } - - @Override - public void setValue(String value) { - input.setText(value); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PathPropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PathPropertyControl.java deleted file mode 100644 index dc86b1386..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PathPropertyControl.java +++ /dev/null @@ -1,100 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property.ui; - -import java.io.File; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; -import org.eclipse.swt.widgets.Button; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.DirectoryDialog; -import org.eclipse.swt.widgets.Shell; -import org.eclipse.swt.widgets.Text; - -import edu.umn.cs.melt.ide.silver.property.Property; - -public class PathPropertyControl extends AbstractPropertyControl { - - private Button info; - private Text input; - - public PathPropertyControl(Composite panel, String name){ - super(panel, name); - } - - public PathPropertyControl(Composite panel, String name, - String display, String defaultVal, boolean isRequired){ - super(panel, name, display, defaultVal, isRequired); - } - - @Override - public Control getInfoControl() { - if(info==null){ - info = new Button(panel, SWT.NONE); - info.setText(display); - info.setToolTipText("A local file system path"); - final Shell shell = panel.getShell(); - info.addSelectionListener(new SelectionAdapter(){ - @Override - public void widgetSelected(SelectionEvent e) { - DirectoryDialog dirDialog = new DirectoryDialog(shell); - dirDialog.setText("Select " + name); - input.setText(dirDialog.open()); - } - - }); - } - return info; - } - - @Override - public Control getInputControl() { - if(input==null){ - input = new Text(panel, SWT.BORDER); - } - return input; - } - - @Override - public Property getProperty() { - return Property.makePathProperty( - name, input.getText(), defaultVal, display, isRequired); - } - - @Override - public String getKey() { - return name; - } - - @Override - public void setValue(String value) { - input.setText(value); - } - - @Override - public boolean validate() { - String value = input.getText(); - if(!isFilled(value)){ - input.setToolTipText("This field cannot be empty."); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - File f = new File(value); - if(!f.exists()){ - input.setToolTipText("Path doesn't exist"); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - reset(); - return true; - } - - private void reset() { - input.setToolTipText(null); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_WHITE)); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PropertyControl.java deleted file mode 100644 index 943073bd9..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/PropertyControl.java +++ /dev/null @@ -1,55 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property.ui; - -import org.eclipse.swt.widgets.Control; - -import edu.umn.cs.melt.ide.silver.property.Property; - -/** - * The control widget set for a property. - *

- * Each PropetyControl consists of several {@link org.eclipse.swt.widgets. - * Control Controls}, provides method for validation, and is backed up by a - * {@link edu.umn.cs.melt.ide.silver.property.Property Property}. - */ -public interface PropertyControl { - - /** - * Get the control widget displaying information about this field. - * Typically a label or button. - * @return - */ - Control getInfoControl(); - - /** - * Get the control widget for user to input values. Typically a text. - * @return - */ - Control getInputControl(); - - /** - * Get the backing property - * @return - */ - Property getProperty(); - - /** - * Get the key of property - * @return - */ - String getKey(); - - /** - * Validate the input. This validation is context-agnostic. To perform - * validation based on the input from other controls, you should modify - * validateAll() in the generated LANGPropertyPage class. - * @return true if no error. - */ - boolean validate(); - - /** - * Set value to this property. - * @param - */ - void setValue(String value); - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/TextPropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/TextPropertyControl.java deleted file mode 100644 index 8f189b22b..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/TextPropertyControl.java +++ /dev/null @@ -1,76 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property.ui; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import edu.umn.cs.melt.ide.silver.property.Property; - -public class TextPropertyControl extends AbstractPropertyControl { - - private Label info; - private Text input; - - public TextPropertyControl(Composite panel, String name){ - super(panel, name); - } - - public TextPropertyControl(Composite panel, String name, - String display, String defaultVal, boolean isRequired){ - super(panel, name, display, defaultVal, isRequired); - } - - @Override - public Control getInfoControl() { - if(info==null){ - info = new Label(panel, SWT.NONE); - info.setText(display); - } - return info; - } - - @Override - public Control getInputControl() { - if(input==null){ - input = new Text(panel, SWT.BORDER); - } - return input; - } - - @Override - public Property getProperty() { - return Property.makeStringProperty( - name, input.getText(), defaultVal, display, isRequired); - } - - @Override - public boolean validate() { - String value = input.getText(); - if(!isFilled(value)){ - input.setToolTipText("This field cannot be empty."); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - reset(); - return true; - } - - private void reset() { - input.setToolTipText(null); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_WHITE)); - } - - @Override - public String getKey() { - return name; - } - - @Override - public void setValue(String value) { - input.setText(value); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/URLPropertyControl.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/URLPropertyControl.java deleted file mode 100644 index b9dee2715..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/silver/property/ui/URLPropertyControl.java +++ /dev/null @@ -1,98 +0,0 @@ -package edu.umn.cs.melt.ide.silver.property.ui; - -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import edu.umn.cs.melt.ide.silver.property.Property; - -public class URLPropertyControl extends AbstractPropertyControl { - - private Label info; - private Text input; - - public URLPropertyControl(Composite panel, String name){ - super(panel, name); - } - - public URLPropertyControl(Composite panel, String name, - String display, String defaultVal, boolean isRequired){ - super(panel, name, display, defaultVal, isRequired); - } - - @Override - public Control getInfoControl() { - if(info==null){ - info = new Label(panel, SWT.NONE); - info.setText(display); - info.setToolTipText("A URL (http/s, ftp or file)"); - } - return info; - } - - @Override - public Control getInputControl() { - if(input==null){ - input = new Text(panel, SWT.BORDER); - } - return input; - } - - @Override - public Property getProperty() { - return Property.makeURLProperty( - name, input.getText(), defaultVal, display, isRequired); - } - - @Override - public boolean validate() { - String value = input.getText(); - if(!isFilled(value)){ - input.setToolTipText("This field cannot be empty."); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - if(!match(value)){ - input.setToolTipText("The value is not a valid URL"); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_RED)); - return false; - } - - reset(); - return true; - } - - @Override - public String getKey() { - return name; - } - - @Override - public void setValue(String value) { - input.setText(value); - } - - private static Pattern URL_PATTERN = null; - - private boolean match(String value) { - if(URL_PATTERN==null){ - URL_PATTERN = Pattern.compile( - "^(https?|ftp|file)://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]"); - } - - Matcher matcher = URL_PATTERN.matcher(value); - return matcher.matches(); - } - - private void reset() { - input.setToolTipText(null); - input.setBackground(panel.getDisplay().getSystemColor(SWT.COLOR_WHITE)); - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/util/ReflectedCall.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/util/ReflectedCall.java deleted file mode 100644 index e64b4c3f9..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/util/ReflectedCall.java +++ /dev/null @@ -1,99 +0,0 @@ -package edu.umn.cs.melt.ide.util; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Arrays; - -import common.Node; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.Status; - -import core.NIOVal; -import core.NMaybe; -import core.Pioval; -import core.Pjust; -import core.Pnothing; - -/** - * Helper to call a Silver function from Java code. - * - * Intention is for this object to be instantiated from `setInitializationData` - * where CoreException is reasonable. We get as far as we can in the constructor - * and then jam and error from the later call into RuntimeException. - */ -public final class ReflectedCall { - private Method method; - - private static String to_sv_class(String silver_function) { - int i = silver_function.lastIndexOf(":"); - String before = silver_function.substring(0, i); - String after = silver_function.substring(i + 1, silver_function.length()); - return before.replace(":", ".") + ".P" + after; - } - private static Class[] sv_args(int arity) { - Class[] arg_types = new Class[arity]; - Arrays.fill(arg_types, Object.class); - return arg_types; - } - - // "core:map", 2 - public ReflectedCall(String silver_function, int arity) throws CoreException { - this(to_sv_class(silver_function), "invoke", sv_args(arity)); - } - // "core.Pmap", "invoke", {Object, Object} - public ReflectedCall(String class_name, String method_name, Class[] arg_types) throws CoreException { - try { - Class cls = (Class)Class.forName(class_name); - this.method = cls.getMethod(method_name, arg_types); - } catch (ClassNotFoundException e) { - // TODO: figure out correct pluginId? - throw new CoreException( - new Status(Status.ERROR, "edu.umn.cs.melt.eclipse", "Cannot find " + class_name, e)); - } catch (NoSuchMethodException e) { - // TODO: figure out correct pluginId? - throw new CoreException( - new Status(Status.ERROR, "edu.umn.cs.melt.eclipse", "Cannot find valid invocation for " + method_name, e)); - } - } - - // call this static method - public T invoke(Object[] args) { - try { - return (T) method.invoke(null, args); - } catch (InvocationTargetException | IllegalAccessException e) { - // We don't do CoreException here because we might not be called in a context where - // that's acceptable to throw (it's a checked exception). - throw new RuntimeException("Reflected call failed", e); - } - } - // call this NONstatic method - public T invokeOn(Object obj, Object[] args) { - try { - return (T) method.invoke(obj, args); - } catch (InvocationTargetException | IllegalAccessException e) { - // We don't do CoreException here because we might not be called in a context where - // that's acceptable to throw (it's a checked exception). - throw new RuntimeException("Reflected call failed", e); - } - } - - // Instantiate using zero-arg constructor an object of `class_name` - public static Object newInstance(String class_name) throws CoreException { - try { - return Class.forName(class_name).getConstructor().newInstance(); - } catch (ClassNotFoundException e) { - // TODO: figure out correct pluginId? - throw new CoreException( - new Status(Status.ERROR, "edu.umn.cs.melt.eclipse", "Cannot find " + class_name, e)); - } catch (NoSuchMethodException e) { - // TODO: figure out correct pluginId? - throw new CoreException( - new Status(Status.ERROR, "edu.umn.cs.melt.eclipse", "Cannot find valid invocation for 0-arg constructor", e)); - } catch (InstantiationException | InvocationTargetException | IllegalAccessException e) { - // TODO: figure out correct pluginId? - throw new CoreException( - new Status(Status.ERROR, "edu.umn.cs.melt.eclipse", "Cannot invoke constructor", e)); - } - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/util/Util.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/util/Util.java deleted file mode 100644 index a3c0238e9..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/util/Util.java +++ /dev/null @@ -1,270 +0,0 @@ -package edu.umn.cs.melt.ide.util; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.net.URL; -import java.util.Arrays; - -import org.eclipse.ant.core.AntRunner; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.FileLocator; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Platform; - -import common.ConsCell; -import common.IOToken; -import common.StringCatter; -import common.javainterop.ConsCellCollection; -import core.NIOVal; -import core.NMaybe; -import core.Pioval; -import core.Pjust; -import core.Pnothing; - -import edu.umn.cs.melt.ide.imp.services.Console; -import edu.umn.cs.melt.ide.impl.SVRegistry; - -/** - * This utility class is an API collection used mainly by Silver code (through - * foreign code block). - */ -public final class Util { - - /* IDE Console */ - - public static IOToken info(String msg) { - Console.getConsoleLoggingStream().info(msg); - return IOToken.singleton; - } - - public static IOToken error(String msg){ - Console.getConsoleLoggingStream().error(msg); - return IOToken.singleton; - } - - /* Apache Ant tool */ - - public static IOToken ant(StringCatter buildFile, StringCatter arguments, StringCatter target){ - - AntRunner runner = new AntRunner(); - runner.setBuildFileLocation(buildFile.toString()); - runner.setArguments(arguments.toString()); - if(!"".equals(target.toString().trim())) { - runner.setExecutionTargets(new String[]{ target.toString() }); - } - try { - runner.run(); - } catch (CoreException e) { - e.printStackTrace(); - } - - return IOToken.singleton; - } - - /* Project-related operations */ - - /** - * Refresh project with given name. Return false if found. Newly found project - * will be cached. - * - * @param projectName The name of the project to refresh - */ - public static IOToken refresh(StringCatter projectName) { - - try { - ResourcesPlugin.getWorkspace().getRoot().getProject(projectName.toString()).refreshLocal(IResource.DEPTH_INFINITE, null); - } catch (CoreException e) { - // Dunno what to do - e.printStackTrace(); - } - - return IOToken.singleton; - } - - public static NIOVal getProjectName(Object/*IProject*/ _project, Object ioin) { - IProject project = (IProject)_project; - return new Pioval(ioin, new StringCatter(project.getName())); - } - - public static IOToken refreshProject(Object/*IProject*/ _project, Object ioin) { - IProject project = (IProject)_project; - try { - project.refreshLocal(IResource.DEPTH_INFINITE, null); - } catch (CoreException e) { - // who knows - e.printStackTrace(); - } - return IOToken.singleton; - } - - public static NIOVal getAbsoluteProjectPath(Object/*IProject*/ _project, Object ioin) { - IProject project = (IProject)_project; - return new Pioval(ioin, new StringCatter(project.getLocation().toOSString())); - } - - public static NIOVal getGeneratedProjectPath(Object/*IProject*/ _project, Object ioin) { - IProject project = (IProject)_project; - return new Pioval(ioin, new StringCatter(project.getLocation().toOSString() - + IPath.SEPARATOR + "bin"));//FIXME - get this from IDE plug-in - } - - public static NIOVal getProjectMembers(Object/*IProject*/ _project, Object ioin) { - IProject project = (IProject)_project; - NMaybe result; - try { - result = new Pjust(ConsCellCollection.fromList(Arrays.asList(project.members()))); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = new Pnothing(); - } - return new Pioval(ioin, result); - } - - public static NIOVal deleteResource(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = true; - try { - resource.delete(true, null); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = false; - } - return new Pioval(ioin, result); - } - - public static NIOVal copyResource(Object/*IResource*/ _resource, StringCatter dest, Object ioin) { - IResource resource = (IResource)_resource; - IPath path = resource.getProject().getFullPath().append(dest.toString()); - boolean result = true; - try { - resource.copy(path, true, null); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = false; - } - return new Pioval(ioin, result); - } - - public static NIOVal moveResource(Object/*IResource*/ _resource, StringCatter dest, Object ioin) { - IResource resource = (IResource)_resource; - IPath path = resource.getProject().getFullPath().append(dest.toString()); - boolean result = true; - try { - resource.move(path, true, null); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = false; - } - return new Pioval(ioin, result); - } - - public static NIOVal createResource(Object/*IProject*/ _project, StringCatter file, StringCatter contents, Object ioin) { - IProject project = (IProject)_project; - IFile f = project.getFile(file.toString()); - NMaybe result; - try { - f.create(new ByteArrayInputStream(contents.toString().getBytes()), true, null); - result = new Pjust(f); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = new Pnothing(); - } - return new Pioval(ioin, result); - } - - public static NIOVal createFolderResource(Object/*IProject*/ _project, StringCatter file, Object ioin) { - IProject project = (IProject)_project; - IFolder f = project.getFolder(file.toString()); - NMaybe result; - try { - f.create(true, true, null); - result = new Pjust(f); - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = new Pnothing(); - } - return new Pioval(ioin, result); - } - - public static NIOVal getRelativePath(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - return new Pioval(ioin, new StringCatter(resource.getProjectRelativePath().toOSString())); - } - public static NIOVal getAbsolutePath(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - return new Pioval(ioin, new StringCatter(resource.getLocation().toOSString())); - } - - public static NIOVal getResourceMembers(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - ConsCell result; - try { - if(resource instanceof IFolder) { - IFolder f = (IFolder) resource; - result = ConsCellCollection.fromList(Arrays.asList(f.members())); - } else { - result = ConsCell.nil; - } - } catch (CoreException e) { - // TODO dunno? - e.printStackTrace(); - result = ConsCell.nil; - } - return new Pioval(ioin, result); - } - - public static NIOVal resourceIsFolder(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource instanceof IFolder; - return new Pioval(ioin, result); - } - public static NIOVal resourceIsFile(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource instanceof IFile; - return new Pioval(ioin, result); - } - public static NIOVal resourceIsLinked(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource.isLinked(); - return new Pioval(ioin, result); - } - public static NIOVal resourceIsHidden(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource.isHidden(); - return new Pioval(ioin, result); - } - public static NIOVal resourceIsDerived(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource.isDerived(); - return new Pioval(ioin, result); - } - public static NIOVal resourceExists(Object/*IResource*/ _resource, Object ioin) { - IResource resource = (IResource)_resource; - boolean result = resource.exists(); - return new Pioval(ioin, result); - } - - public static NIOVal getIdeResource(StringCatter id, Object ioin) { - URL url = Platform.getBundle(SVRegistry.get().pluginId()).getEntry("resource/" + id.toString()); - String fileurl = ""; - try { - // Evidently, when it's a file url, getFile gets the whole path string - fileurl = FileLocator.toFileURL(url).getFile().toString(); - } catch (IOException e) { - // TODO Not sure what to do ~ - e.printStackTrace(); - } - return new Pioval(ioin, new StringCatter(fileurl)); - } -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewProjectWizard.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewProjectWizard.java deleted file mode 100644 index ea7908e86..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewProjectWizard.java +++ /dev/null @@ -1,130 +0,0 @@ -package edu.umn.cs.melt.ide.wizard; - -import java.io.ByteArrayInputStream; -import java.net.URI; - -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IProjectDescription; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.ui.INewWizard; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.dialogs.WizardNewProjectCreationPage; -import org.eclipse.ui.wizards.newresource.BasicNewProjectResourceWizard; - -import edu.umn.cs.melt.ide.imp.builders.Nature; -import edu.umn.cs.melt.ide.impl.SVRegistry; - -/** - * The New Project Wizard is used to create a project. In general - * it follows these steps: - *

- * (1) show a page for user to name the project
- * (2) create a project folder with the given name in current workspace
- * (3) create a property file
- * (4) add the nature to the project (note nature is associated with builder)
- */ -public class NewProjectWizard extends Wizard implements INewWizard, IExecutableExtension { - - private WizardNewProjectCreationPage page1; - - private IConfigurationElement configElement; - - public NewProjectWizard() { - setWindowTitle(SVRegistry.get().name()); - } - - @Override - public void init(IWorkbench workbench, IStructuredSelection selection) { - - } - - @Override - public void addPages() { - super.addPages(); - - String name = SVRegistry.get().name(); - - page1 = new WizardNewProjectCreationPage("New " + name + " Project Wizard"); - page1.setTitle(name + " Project"); - page1.setDescription("Create new " + name + " project"); - - addPage(page1); - } - - @Override - public boolean performFinish() { - //Get user's input - String name = page1.getProjectName(); - URI location = null; - if (!page1.useDefaults()) { - location = page1.getLocationURI(); - } - - //Create project in current workspace - createProject(name, location); - - //Update perspective - BasicNewProjectResourceWizard.updatePerspective(configElement); - - return true; - } - - @Override - public void setInitializationData( - IConfigurationElement config, String propertyName, Object data) throws CoreException { - configElement = config; - } - - /** - * Create a project and add nature to it. - * - * @param location - * @param projectName - */ - private void createProject(String projectName, URI location) { - //Get the project handle by given name - IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName); - - if (!project.exists()) { - //Set the description - URI projectLocation = location; - IProjectDescription desc = - project.getWorkspace().newProjectDescription(project.getName()); - if (location != null && - location.equals(ResourcesPlugin.getWorkspace().getRoot().getLocationURI())) { - projectLocation = null; - } - desc.setLocationURI(projectLocation); - - //Create and open the project - try { - project.create(desc, null); - if (!project.isOpen()) { - project.open(null); - } - - //Create properties file - project.getFile("project.properties").create( - new ByteArrayInputStream(SVRegistry.get().getInitialProjectProperties().getBytes()), true, null); - } catch (CoreException e) { - page1.setErrorMessage(e.getMessage()); - e.printStackTrace(); - return; - } - } - - //Add the nature, if not added yet - try { - Nature.addToProject(project, SVRegistry.get().getNatureId()); - } catch (CoreException e) { - e.printStackTrace(); - } - } - -} - diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFilePage.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFilePage.java deleted file mode 100644 index d4766c9aa..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFilePage.java +++ /dev/null @@ -1,466 +0,0 @@ -package edu.umn.cs.melt.ide.wizard; - -import ide.NIdeProperty; -import ide.PmakeIdeProperty; - -import java.util.List; - -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.resources.IWorkspaceRoot; -import org.eclipse.core.resources.ResourcesPlugin; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IAdaptable; -import org.eclipse.core.runtime.IPath; -import org.eclipse.core.runtime.Path; -import org.eclipse.imp.runtime.PluginBase; -import org.eclipse.jface.resource.ImageDescriptor; -import org.eclipse.jface.viewers.ArrayContentProvider; -import org.eclipse.jface.viewers.ISelectionChangedListener; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.viewers.ITreeContentProvider; -import org.eclipse.jface.viewers.LabelProvider; -import org.eclipse.jface.viewers.SelectionChangedEvent; -import org.eclipse.jface.viewers.StructuredViewer; -import org.eclipse.jface.viewers.TreeViewer; -import org.eclipse.jface.viewers.Viewer; -import org.eclipse.jface.viewers.ViewerFilter; -import org.eclipse.jface.wizard.WizardPage; -import org.eclipse.swt.SWT; -import org.eclipse.swt.events.ModifyEvent; -import org.eclipse.swt.events.ModifyListener; -import org.eclipse.swt.graphics.Image; -import org.eclipse.swt.layout.FillLayout; -import org.eclipse.swt.layout.GridData; -import org.eclipse.swt.layout.GridLayout; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.Label; -import org.eclipse.swt.widgets.Text; - -import common.ConsCell; - -import edu.umn.cs.melt.ide.impl.SVRegistry; -import edu.umn.cs.melt.ide.silver.property.Property; -import edu.umn.cs.melt.ide.silver.property.ui.IPropertyControlsProvider; -import edu.umn.cs.melt.ide.silver.property.ui.PropertyControl; - -public class NewSourceFilePage extends WizardPage { - - /** - * The project or folder under which this file is to be added. - */ - private IResource parentResource; - - /** - * The panel displaying all the controls - */ - private Composite panel; - - /** - * This control contains path relative to workspace root, without the leading '/'. - * That is, the first segment is the name of project. - */ - private Text parentFolderText; - - private Text fileNameText; - - private StructuredViewer tv; - - private IPropertyControlsProvider provider; - - private IStructuredSelection initialSelection; - - /** - * Controls used to input user-defined properties - */ - private List propertyControls; - - public NewSourceFilePage(String pageName, - IStructuredSelection selection) { - super(pageName); - provider = SVRegistry.get().getNewFileProperties(); - initialSelection = selection; - } - - @Override - public void createControl(Composite parent) { - // Prepare some layouts to be used in this page - GridLayout gdLayoutC1 = new GridLayout(); - gdLayoutC1.numColumns = 1; - - GridLayout gdLayoutC2 = new GridLayout(); - gdLayoutC2.numColumns = 2; - - FillLayout fillLayout = new FillLayout(); - - // Assemble the page - panel = new Composite(parent, SWT.NONE); - panel.setLayout(gdLayoutC1); - panel.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); - - // row 1: information about the text control below - Label r1_label = new Label(panel, SWT.NONE); - r1_label.setText("Enter or select the parent folder:"); - - // row 2: the text control indicating which project this file is to be - // added to - parentFolderText = new Text(panel, SWT.BORDER); - parentFolderText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - parentFolderText.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - updatePageComplete(); - } - }); - - // row 3: a sub panel - Composite sub_panel_projs = new Composite(panel, SWT.NONE); - sub_panel_projs.setLayout(gdLayoutC1); - sub_panel_projs.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); - - // A tree viewer for project list - // Note it's put inside another panel (sub_panel_projs) for filling layout - Composite panel_3 = new Composite(sub_panel_projs, SWT.NONE); - panel_3.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); - panel_3.setLayout(fillLayout); - - tv = new TreeViewer(panel_3, SWT.SINGLE | SWT.BORDER); - tv.setLabelProvider(new ResourceLabelProvider()); - tv.setContentProvider(new ResourceTreeProvider()); - tv.setInput(ResourcesPlugin.getWorkspace().getRoot().getProjects()); - tv.addFilter(new ViewerFilter() { - // Show only appropriate projects and folders - @Override - public boolean select(Viewer viewer, Object parentElement, Object element) { - if (element instanceof IProject) { - IProject project = (IProject) element; - if(hasValidNature(project)){ - return true; - } - } else if (element instanceof IFolder) { - return true; - } - - return false; - } - }); - - // Add selection change listener: update selected project - tv.addSelectionChangedListener(new ISelectionChangedListener() { - @Override - public void selectionChanged(SelectionChangedEvent event) { - IStructuredSelection sel = (IStructuredSelection) event.getSelection(); - IResource resource = (IResource) sel.getFirstElement(); - NewSourceFilePage.this.parentResource = resource; - updateParentFolderText(); - } - }); - - updateParentFolderText(); - - // row 4: a sub panel - Composite sub_panel_file = new Composite(panel, SWT.NONE); - sub_panel_file.setLayout(gdLayoutC2); - sub_panel_file.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); - - // row 4 (left): information about the text control below - Label r2_label = new Label(sub_panel_file, SWT.NONE); - r2_label.setText("File name:"); - - // row 4 (right): the text control for the name of new file - fileNameText = new Text(sub_panel_file, SWT.BORDER); - fileNameText.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - fileNameText.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - updatePageComplete(); - } - }); - - // row 5+: a sub panel for customized property controls - Composite sub_panel_properties = new Composite(panel, SWT.NONE); - sub_panel_properties.setLayout(gdLayoutC2); - sub_panel_properties.setLayoutData(new GridData(GridData.FILL_HORIZONTAL | GridData.FILL_VERTICAL)); - propertyControls = provider.getPropertyControls(sub_panel_properties); - if(propertyControls!=null){ - for(PropertyControl control:propertyControls){ - Control info = control.getInfoControl(); - Control input = control.getInputControl(); - info.setLayoutData(new GridData()); - input.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); - - if(input instanceof Text){ - Text text = (Text) input; - text.addModifyListener(new ModifyListener() { - @Override - public void modifyText(ModifyEvent e) { - updatePageComplete(); - } - }); - } - } - } - - setControl(panel); - setPageComplete(false); - - initialize(); - } - - /** - * Get destination parent project or folder - * @return either an IProject or an IFolder - */ - IResource getParentFolder(){ - return parentResource; - } - - /** - * Get name of file to create - * @return - */ - String getFileName(){ - return fileNameText.getText(); - } - - /** - * Get properties in a [NIdeProperty] - */ - ConsCell getNIdePerpertyArray() { - ConsCell list = ConsCell.nil; - for(PropertyControl control: propertyControls){ - // this reverses the order, but that's okay I think - Property prop = control.getProperty(); - NIdeProperty item = new PmakeIdeProperty( - new common.StringCatter(prop.getName()), - new common.StringCatter(prop.getType().name()), - new common.StringCatter(prop.getSValue()) - ); - list = new ConsCell(item, list); - } - return list; - } - - private static class ResourceTreeProvider extends ArrayContentProvider implements ITreeContentProvider { - - @Override - public Object[] getChildren(Object parentElement) { - if (parentElement instanceof IProject) { - IProject project = (IProject) parentElement; - try { - return project.members(); - } catch (CoreException e) { - e.printStackTrace(); - } - } - - if (parentElement instanceof IFolder) { - IFolder folder = (IFolder) parentElement; - try { - return folder.members(); - } catch (CoreException e) { - e.printStackTrace(); - } - } - return null; - } - - @Override - public Object getParent(Object element) { - if (element instanceof IFolder) { - IFolder folder = (IFolder) element; - return folder.getParent(); - } - return null; - } - - @Override - public boolean hasChildren(Object element) { - if (element instanceof IProject || element instanceof IFolder) { - return true; - } - return false; - } - } - - private static class ResourceLabelProvider extends LabelProvider { - - private static final ImageDescriptor FLDR = - PluginBase.imageDescriptorFromPlugin(SVRegistry.get().pluginId(), "icons/fldr_obj.gif"); - - private static Image FLDR_IMG; - - @Override - public Image getImage(Object resource) { - if (FLDR_IMG == null) { - FLDR_IMG = FLDR.createImage(); - } - return FLDR_IMG; - } - - @Override - public String getText(Object resource) { - IResource res = (IResource) resource; - return res.getName(); - } - } - - private void updateParentFolderText() { - if (parentResource != null) { - if(parentResource instanceof IProject) { - parentFolderText.setText(parentResource.getName()); - } else { - parentFolderText.setText( - parentResource.getProject().getName() + - Path.SEPARATOR + - parentResource.getProjectRelativePath()); - } - } - } - - private boolean hasTriedFinishing = false; - - boolean attemptCompletion() { - hasTriedFinishing = true; - return updatePageComplete(); - } - - /** - * Update the status of page completion - */ - private boolean updatePageComplete() { - if(!hasTriedFinishing){ - setPageComplete(true); - return true; - } - - if (parentFolderText == null || fileNameText == null){ - setPageComplete(false); - return false; - } - - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - - String parentRelPath = parentFolderText.getText(); - if (parentRelPath == null || "".equals(parentRelPath)) { - setPageComplete(false); - setErrorMessage("the parent folder is not set"); - return false; - } - - String parentAbsPath = root.getFullPath().append(parentRelPath).toPortableString(); - IPath parentResPath = Path.fromPortableString(parentAbsPath); - - if(isProject(parentRelPath)){ - // It's a project - IProject[] projects = root.getProjects(); - boolean found = false; - for(IProject proj : projects){ - if(proj.getName().equals(parentRelPath)){ - found = true; - this.parentResource = proj; - break; - } - } - if(!found){ - setPageComplete(false); - setErrorMessage("the project '" + parentResPath + "' doesn't exist"); - return false; - } else if(!(hasValidNature((IProject) this.parentResource))){ - setPageComplete(false); - setErrorMessage("the project '" + parentResPath + "' is not a " + SVRegistry.get().name() + " project"); - return false; - } - } else { // It's a folder - IFolder folder = null; - try { - folder = root.getFolder(parentResPath); - } catch (Exception e) { - e.printStackTrace(); - } - if(folder == null){ - setPageComplete(false); - setErrorMessage("the parent folder '" + parentResPath + "' is invalid"); - return false; - } - if(!folder.exists()){ - setPageComplete(false); - setErrorMessage("the parent folder '" + parentResPath + "' doesn't exist"); - return false; - } - } - - if (fileNameText.getText() == null || "".equals(fileNameText.getText())) { - setPageComplete(false); - setErrorMessage("the file name is not set"); - return false; - } - - // Check user-defined properties - if(!provider.validateAll()){ - setErrorMessage("Invalid properties"); - setPageComplete(false); - return false; - } - - setErrorMessage(null); - setPageComplete(true); - - return true; - } - - /** - * Determine whether the parent folder is an IDE project. - * @param parentRelPath - * @return - */ - private boolean isProject(String parentRelPath){ - int firstSplitter = parentRelPath.indexOf('/'); - if(firstSplitter==-1){ - return true; - } else { - char[] chars = parentRelPath.substring(firstSplitter).toCharArray(); - boolean hasOnlySeparater = true; - for(char c : chars){ - if(c != Path.SEPARATOR) { - hasOnlySeparater = false; - break; - } - } - return hasOnlySeparater; - } - } - - private boolean hasValidNature(IProject project){ - try { - if (project.hasNature(SVRegistry.get().getNatureId())) { - return true; - } - } catch (CoreException e) { - // Internal problem. Ignore. - } - - return false; - } - - private void initialize() { - if (initialSelection != null) { - Object element = initialSelection.getFirstElement(); - - if (element instanceof IProject) { - parentResource = (IProject) element; - } else if (element instanceof IAdaptable) { - IAdaptable adaptable = (IAdaptable) element; - Object adapter = adaptable.getAdapter(IResource.class); - parentResource = (IResource) adapter; - } - } - - if (parentResource != null) { - updateParentFolderText(); - } - } - -} diff --git a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFileWizard.java b/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFileWizard.java deleted file mode 100644 index 4bb81ed42..000000000 --- a/runtime/imp/main/src/edu/umn/cs/melt/ide/wizard/NewSourceFileWizard.java +++ /dev/null @@ -1,122 +0,0 @@ -package edu.umn.cs.melt.ide.wizard; - -import java.io.ByteArrayInputStream; - -import org.eclipse.core.resources.IFile; -import org.eclipse.core.resources.IFolder; -import org.eclipse.core.resources.IProject; -import org.eclipse.core.resources.IResource; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IConfigurationElement; -import org.eclipse.core.runtime.IExecutableExtension; -import org.eclipse.jface.viewers.IStructuredSelection; -import org.eclipse.jface.wizard.Wizard; -import org.eclipse.ui.IEditorDescriptor; -import org.eclipse.ui.INewWizard; -import org.eclipse.ui.ISelectionService; -import org.eclipse.ui.IWorkbench; -import org.eclipse.ui.IWorkbenchPage; -import org.eclipse.ui.PlatformUI; -import org.eclipse.ui.part.FileEditorInput; - -import common.ConsCell; - -import edu.umn.cs.melt.ide.impl.SVRegistry; - -/** - * The New Source File Wizard is used to create a source file, - * with extension name being given by the plugin, under a given project. - *
- * The user can also define properties when creating a new source file. The source file - * stub generator, defined in IDE declaration block, consults with these properties to - * determine how the stub file should be generated. - */ -public class NewSourceFileWizard extends Wizard implements INewWizard, IExecutableExtension { - - private String EXT = "." + SVRegistry.get().fileExtension(); - - private NewSourceFilePage page1; - - public NewSourceFileWizard() { - setWindowTitle(SVRegistry.get().name()); - } - - @Override - public void init(IWorkbench workbench, IStructuredSelection selection) { - - } - - @Override - public void addPages() { - super.addPages(); - - ISelectionService service = - PlatformUI.getWorkbench().getActiveWorkbenchWindow().getSelectionService(); - - IStructuredSelection selection = (IStructuredSelection) - service.getSelection("org.eclipse.ui.navigator.ProjectExplorer"); - - // Sometimes users may be (by mistake) using JDT, the perspective of which - // uses a package explorer (id=org.eclipse.jdt.ui.PackageExplorer), instead of project explorer. - // We won't handle this typical misuse here. - - String name = SVRegistry.get().name(); - - page1 = new NewSourceFilePage("New " + name + " Source File Wizard", selection); - page1.setTitle(name + " Source File"); - page1.setDescription("Create new " + name + " source file"); - - addPage(page1); - } - - // Add a new file to specified parent folder - @Override - public boolean performFinish() { - if(!page1.attemptCompletion()){ - return false; - } - - String name = page1.getFileName() + EXT; - IResource parent = page1.getParentFolder(); - IFile file = null; - if(parent instanceof IProject){ - file = ((IProject) parent).getFile(name); - } else { - file = ((IFolder) parent).getFile(name); - } - - if(!file.exists()){ - try { - // Generate stub - ConsCell properties = page1.getNIdePerpertyArray(); - String stub = SVRegistry.get().fileStub(properties).toString(); - - // Create file - file.create(new ByteArrayInputStream(stub.getBytes()), false, null); - - // Open file - IWorkbench workbench = PlatformUI.getWorkbench(); - IEditorDescriptor desc = workbench.getEditorRegistry().getDefaultEditor(file.getName()); - IWorkbenchPage page = workbench.getActiveWorkbenchWindow().getActivePage(); - page.openEditor(new FileEditorInput(file), desc.getId()); - - return true; - } catch (CoreException e) { - page1.setErrorMessage(e.getMessage()); - e.printStackTrace(); - } - } else { - page1.setErrorMessage("A source file with same name exists already."); - } - - return false; - } - - @Override - public void setInitializationData( - IConfigurationElement config, String propertyName, Object data) throws CoreException { - - } - -} - diff --git a/runtime/java/.classpath b/runtime/java/.classpath deleted file mode 100644 index aa0952137..000000000 --- a/runtime/java/.classpath +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - diff --git a/runtime/java/.project b/runtime/java/.project deleted file mode 100644 index ea25e23af..000000000 --- a/runtime/java/.project +++ /dev/null @@ -1,24 +0,0 @@ - - - common - - - - - - org.eclipse.jdt.core.javabuilder - - - - - - org.eclipse.jdt.core.javanature - - - - generated_src - 2 - /home/krame505/lrk/PL_research/silver/generated/src - - - diff --git a/runtime/java/README.md b/runtime/java/README.md deleted file mode 100644 index 6ab814077..000000000 --- a/runtime/java/README.md +++ /dev/null @@ -1,21 +0,0 @@ - -To edit the Silver runtime from an Eclipse IDE, - -1. Run a successful `./deep-rebuild` at Silver project root. - This is necessary to generate translations for the Silver standard library - and other things referenced by the runtime. - (Failure to do this, or later having run clean, will result in errors related - to not finding classes from `core` and `lib`.) - -2. In Eclipse, `File -> Import`, `General -> Existing Projects into Workspace`. - Give it the path to `silver/runtime/java`. - -3. Go to `Window -> Preferences` (NOTE: **WINDOW** preferences, other - ways to get to preferences may not show this option! Confusingly.) - and navigate to `Java -> Build Path -> Classpath Variables`. - Then create a new `SILVER_HOME` variable, set to where you have checked out - Silver. (e.g. `/home/tedinski/repos/silver`) - - (The purpose of this is to ensure the eclipse project configuration that's - checked into version control does not need to change from person to person.) - diff --git a/runtime/java/build.xml b/runtime/java/build.xml index c466518f7..c23afa2ff 100644 --- a/runtime/java/build.xml +++ b/runtime/java/build.xml @@ -9,7 +9,8 @@ - + + @@ -17,26 +18,24 @@ - + - + - + + - - - + + - - diff --git a/runtime/java/src/common/AppTypeRep.java b/runtime/java/src/common/AppTypeRep.java new file mode 100644 index 000000000..997265ca5 --- /dev/null +++ b/runtime/java/src/common/AppTypeRep.java @@ -0,0 +1,81 @@ +package common; + +import java.util.*; + +/** + * Representation of an applied Silver type, used for run-time type checking in reification. + * + * @author krame505 + */ +public class AppTypeRep extends TypeRep { + /** + * The contstructor and argument types. + */ + public final TypeRep cons, arg; + + /** + * Create a AppTypeRep + * + * @param cons The applied type. + * @param arg The argument type. + */ + public AppTypeRep(final TypeRep cons, final TypeRep arg) { + this.cons = cons; + this.arg = arg; + } + + @Override + protected final boolean unifyPartial(final TypeRep other) { + return other instanceof AppTypeRep && unify(cons, ((AppTypeRep)other).cons) && unify(arg, ((AppTypeRep)other).arg); + } + + @Override + public final String typeName() { + return cons.typeName(); + } + + @Override + public final String toString() { + List args = new LinkedList<>(); + TypeRep a = this; + for (; a instanceof AppTypeRep; a = ((AppTypeRep)a).cons) { + args.add(0, ((AppTypeRep)a).arg); + } + Iterator it = args.iterator(); + String result = ""; + if (a instanceof BaseTypeRep && ((BaseTypeRep)a).name.equals("[]")) { + result = "[" + it.next() + "]"; + } else if (a instanceof FunctionTypeRep) { + FunctionTypeRep fn = (FunctionTypeRep)a; + String argsToString = ""; + if (fn.params > 0 && it.hasNext()) { + argsToString += it.next(); + } + int i; + for (i = 1; i < fn.params && it.hasNext(); i++) { + argsToString += " " + it.next(); + } + for (; i < fn.params; i++) { + argsToString += " _"; + } + for (i = 0; i < fn.namedParams.length && it.hasNext(); i++) { + argsToString += "; " + fn.namedParams[i] + "::" + it.next(); + } + for (; i < fn.namedParams.length; i++) { + argsToString += "; " + fn.namedParams[i] + "::_"; + } + String resultToString = it.hasNext()? it.next().toString() : "_"; + result = "(" + resultToString + " ::= " + argsToString + ")"; + } else { + result = a.toString(); + } + if (it.hasNext()) { + result += "<" + it.next(); + while (it.hasNext()) { + result += " " + it.next(); + } + result += ">"; + } + return result; + } +} diff --git a/runtime/java/src/common/AttributeSection.java b/runtime/java/src/common/AttributeSection.java deleted file mode 100644 index 23ca51957..000000000 --- a/runtime/java/src/common/AttributeSection.java +++ /dev/null @@ -1,79 +0,0 @@ -package common; - -/** - * Returns a function reference (a NodeFactory) that accesses the attribute - * provided to the constructor. Used to implement attribute sections (.pp) - * - * e.g. (.pp) :: (String ::= Decorated Expr) - * - * @param The return type of the attribute access function - * @author tedinski - */ -public class AttributeSection extends NodeFactory { - - private final int index; - - /** - * @param i The index of the synthesized attribute to access. - */ - public AttributeSection(final int i) { - index = i; - } - - @Override - public T invoke(final Object[] args, final Object[] notApplicable) { - DecoratedNode context = (DecoratedNode) Util.demand(args[0]); - return (T)context.synthesized(index); - } - - @Override - public final FunctionTypeRep getType() { - // TODO: I have no idea how to make this work - throw new common.exceptions.SilverError("Runtime type checking of attribute sections is not supported"); - } - - /** - * Identical to AttributeSection, but operates on an undecorated argument instead. - * - * @author tedinski - */ - public static class Undecorated extends NodeFactory { - - private final int index; - private final DecoratedNode trueParent; - - /** - * @param i The index of the synthesized attribute to access. - */ - public Undecorated(final int i) { - index = i; - trueParent = TopNode.singleton; - } - /** - * Strictly speaking, this shouldn't be necessary, however, it's preferrable to - * do this (providing the node that the attribute section (.attr) appears within) - * than to have the baffling error "inherited attributes not provided by " - * show up. This *could* be fixed by well-definedness analysis. Someday. - * - * @param i The index of the synthesized attribute to access. - * @param parent The node that this attribute section is being created within. - */ - public Undecorated(final int i, final DecoratedNode parent) { - index = i; - trueParent = parent; - } - - @Override - public T invoke(final Object[] args, final Object[] notApplicable) { - DecoratedNode context = ((Node) Util.demand(args[0])).decorate(trueParent, (common.Lazy[])null); - return (T)context.synthesized(index); - } - - @Override - public final FunctionTypeRep getType() { - // TODO: I have no idea how to make this work - throw new common.exceptions.SilverError("Runtime type checking of attribute sections is not supported"); - } - - } -} diff --git a/runtime/java/src/common/BaseTypeRep.java b/runtime/java/src/common/BaseTypeRep.java index a5349727f..44dc761c9 100644 --- a/runtime/java/src/common/BaseTypeRep.java +++ b/runtime/java/src/common/BaseTypeRep.java @@ -1,66 +1,37 @@ package common; /** - * Representation of a function Silver type, used for run-time type checking in reification. + * Representation of a nonterminal or primitive Silver type, used for run-time type checking in reification. * * @author krame505 */ public class BaseTypeRep extends TypeRep { /** - * The name of the type, minus any type parameters - e.g. "Integer" or "Decorated core:Pair". + * The name of the type - e.g. "Integer" or "silver:core:Pair". */ - public final String baseName; + public final String name; /** - * The type parameters given to the base type. - */ - public final TypeRep[] params; - - /** - * Create a BaseTypeRep + * Create a BaseTypeRep. * - * @param baseName The base name of the type. - * @param params The type parameters given to the base type. + * @param name The name of the type. */ - public BaseTypeRep(final String baseName, final TypeRep[] params) { - this.baseName = baseName; - this.params = params; + public BaseTypeRep(final String name) { + this.name = name; } - /** - * Create a BaseTypeRep without type parameters. - * - * @param baseName The base name of the type. - */ - public BaseTypeRep(final String baseName) { - this(baseName, new TypeRep[0]); + @Override + protected final boolean unifyPartial(final TypeRep other) { + return other instanceof BaseTypeRep && name.equals(((BaseTypeRep)other).name); } @Override - protected final boolean unifyPartial(final TypeRep other) { - if (!(other instanceof BaseTypeRep) || !baseName.equals(((BaseTypeRep)other).baseName)) { - return false; - } - // Any types with the same name should have the same number of type parameters - assert params.length == ((BaseTypeRep)other).params.length; - for (int i = 0; i < params.length; i++) { - if (!TypeRep.unify(params[i], ((BaseTypeRep)other).params[i])) { - return false; - } - } - return true; + public final String typeName() { + return name; } @Override public final String toString() { - if (params.length == 0) { - return baseName; - } else { - String paramsToString = params[0].toString(); - for (int i = 1; i < params.length; i++) { - paramsToString += " " + params[i].toString(); - } - return baseName + "<" + paramsToString + ">"; - } + return name; } } diff --git a/runtime/java/src/common/CollectionAttribute.java b/runtime/java/src/common/CollectionAttribute.java index ea2b554ab..7d463b6f4 100644 --- a/runtime/java/src/common/CollectionAttribute.java +++ b/runtime/java/src/common/CollectionAttribute.java @@ -1,70 +1,71 @@ -package common; - -import java.util.ArrayList; - -import common.exceptions.MissingDefinitionException; -import common.exceptions.TraceException; - -/** - * This class is a specialized Lazy that allows additional elements to be inserted - * during start-up initialization. These elements are then used by the custom - * eval method (note that it's abstract!) to produce the final value. - * - *

The nice thing about this design is that using a collection attribute is no - * different than any other. The difference is all in initialization. - * - * @author tedinski, bodin - */ -public abstract class CollectionAttribute implements Lazy { - - private ArrayList pieces; - private Lazy base; - - public CollectionAttribute() { - this.pieces = new ArrayList(); - } - - public CollectionAttribute(final int index) { - this(); - this.base = new BaseDefault(index); - } - - public final Lazy getBase() { - return base; - } - - public final void setBase(final Lazy base) { - this.base = base; - } - - public final ArrayList getPieces() { - return pieces; - } - - public final void addPiece(final Lazy attribute) { - pieces.add(attribute); - } - - public abstract Object eval(final DecoratedNode context); - - private static class BaseDefault implements Lazy { - private final int index; - BaseDefault(final int index) { - this.index=index; - } - @Override - public Object eval(final DecoratedNode context) { - final Node un = context.undecorate(); - if(un.hasForward()) { - try { - return context.forward().synthesized(index); - } catch(Throwable t) { - throw new TraceException("Error evaluating base of collection attribute " + un.getNameOfSynAttr(index) + " via forward of " + un.getName(),t); - } - } - - throw new MissingDefinitionException("No base defined for collection attribute " + un.getNameOfSynAttr(index) + " in " + un.getName()); - } - - } -} +package common; + +import java.util.ArrayList; + +import common.exceptions.MissingDefinitionException; +import common.exceptions.TraceException; + +/** + * This class is a specialized Lazy that allows additional elements to be inserted + * during start-up initialization. These elements are then used by the custom + * eval method (note that it's abstract!) to produce the final value. + * + *

The nice thing about this design is that using a collection attribute is no + * different than any other. The difference is all in initialization. + * + * @author tedinski, bodin + */ +public abstract class CollectionAttribute implements Lazy { + + private ArrayList pieces; + private Lazy base; + public boolean appliedNTFix; + + public CollectionAttribute() { + this.pieces = new ArrayList(); + } + + public CollectionAttribute(final int index) { + this(); + this.base = new BaseDefault(index); + } + + public final Lazy getBase() { + return base; + } + + public final void setBase(final Lazy base) { + this.base = base; + } + + public final ArrayList getPieces() { + return pieces; + } + + public final void addPiece(final Lazy attribute) { + pieces.add(attribute); + } + + public abstract Object eval(final DecoratedNode context); + + private static class BaseDefault implements Lazy { + private final int index; + BaseDefault(final int index) { + this.index=index; + } + @Override + public Object eval(final DecoratedNode context) { + final Node un = context.undecorate(); + if(un.hasForward()) { + try { + return context.forward().synthesized(index); + } catch(Throwable t) { + throw new TraceException("Error evaluating base of collection attribute " + un.getNameOfSynAttr(index) + " via forward of " + un.getName(),t); + } + } + + throw new MissingDefinitionException("No base defined for collection attribute " + un.getNameOfSynAttr(index) + " in " + un.getName()); + } + + } +} diff --git a/runtime/java/src/common/ConsCell.java b/runtime/java/src/common/ConsCell.java index 6236bb8b8..9c2879c4c 100644 --- a/runtime/java/src/common/ConsCell.java +++ b/runtime/java/src/common/ConsCell.java @@ -1,6 +1,7 @@ package common; import java.util.List; +import java.util.LinkedList; import common.exceptions.*; @@ -48,6 +49,24 @@ public static ConsCell fromList(List l) { } return cons; } + + /** + * Creates a Java List from a cons list. + * + * @param l The cons list. + * @return An equivalent Java List. + */ + public static List toList(ConsCell cons) { + List lst = new LinkedList(); + while(! cons.nil()) { + @SuppressWarnings("unchecked") + T val = (T) cons.head(); + + cons = cons.tail(); + lst.add(val); + } + return lst; + } /** * Obtain the head of the cons cell, possibly evaluating that thunk to get it. @@ -91,20 +110,44 @@ public boolean nil() { * @return The length of the list. */ public int length() { - return 1 + tail().length(); + // return 1 + tail().length(); // Blows up the stack on big lists + + ConsCell x = this; + int count = 0; + + while (!(x instanceof NilConsCell)) { + x = x.tail(); + count++; + } + + return count; } @Override - public ListTypeRep getType() { + public AppTypeRep getType() { try { // The type of a list is the type of its tail, but the type of its tail may be [a]. // Unify the parameter with the type of the head to constrain this type variable. - ListTypeRep tailType = tail().getType(); - if (!TypeRep.unify(tailType.param, Reflection.getType(head()))) { - throw new SilverInternalError("Unification failed."); - } else { - return tailType; + + // AppTypeRep tailType = tail().getType(); // Blows up the stack on big lists + // if (!TypeRep.unify(tailType.arg, Reflection.getType(head()))) { + // throw new SilverInternalError("Unification failed."); + // } else { + // return tailType; + // } + + TypeRep tvar = new VarTypeRep(); + ConsCell x = this; + + while (!(x instanceof NilConsCell)) { + if (!TypeRep.unify(tvar, Reflection.getType(x.head()))) { + throw new SilverInternalError("Failed to construct list type - got a non-homogenous list!"); + } + x = x.tail(); } + + return new AppTypeRep(new BaseTypeRep("[]"), tvar); + } catch (SilverException e) { throw new TraceException("While constructing type of list", e); } @@ -131,9 +174,10 @@ public ConsCell tail() { public int length() { return 0; } + @Override - public ListTypeRep getType() { - return new ListTypeRep(new VarTypeRep()); + public AppTypeRep getType() { + return new AppTypeRep(new BaseTypeRep("[]"), new VarTypeRep()); } } diff --git a/runtime/java/src/common/CopperUtil.java b/runtime/java/src/common/CopperUtil.java new file mode 100644 index 000000000..661e412a6 --- /dev/null +++ b/runtime/java/src/common/CopperUtil.java @@ -0,0 +1,386 @@ +package common; + +import common.exceptions.SilverInternalError; +import common.javainterop.ConsCellCollection; +import edu.umn.cs.melt.copper.compiletime.dumpers.XMLSpecDumper; +import edu.umn.cs.melt.copper.compiletime.logging.CompilerLogger; +import edu.umn.cs.melt.copper.compiletime.pipeline.AuxiliaryMethods; +import edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.*; +import edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.visitors.ParserSpecProcessor; +import edu.umn.cs.melt.copper.main.CopperDumpControl; +import edu.umn.cs.melt.copper.main.CopperDumpType; +import edu.umn.cs.melt.copper.main.CopperIOType; +import edu.umn.cs.melt.copper.main.CopperPipelineType; +import edu.umn.cs.melt.copper.main.ParserCompiler; +import edu.umn.cs.melt.copper.main.ParserCompilerParameters; +import edu.umn.cs.melt.copper.runtime.engines.semantics.VirtualLocation; +import edu.umn.cs.melt.copper.runtime.io.Location; +import edu.umn.cs.melt.copper.runtime.logging.CopperException; + +import java.io.File; +import java.io.IOException; +import java.text.ParseException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; + +import silver.core.Init; +import silver.core.NIOVal; +import silver.core.NLocation; + +public final class CopperUtil { + private static Location svToCuLocation(String sourceGrammar, + NLocation locTerm) { + // datatypes when... + DecoratedNode locTree = locTerm.decorate(); + + String fileName = + String.format("%s/%s", sourceGrammar, + locTree.synthesized( + Init.silver_core_filename__ON__silver_core_Location)); + Integer line = (Integer)locTree.synthesized( + Init.silver_core_line__ON__silver_core_Location); + Integer column = (Integer)locTree.synthesized( + Init.silver_core_column__ON__silver_core_Location); + + return new VirtualLocation(fileName, line, column); + } + + public static NIOVal compile(ParserBean parser, String packageName, + String parserName, Boolean runMDA, + String outFile, Boolean dumpHtml, + String dumpHtmlTo, Boolean xmlDump, + IOToken tok) { + if (xmlDump) { + try { + new XMLSpecDumper(parser).dump(CopperDumpType.XML_SPEC, System.out); + } catch (IOException exc) { + throw new SilverInternalError( + "Failed to dump XML for Copper specification", exc); + } + } + + ParserCompilerParameters params = new ParserCompilerParameters(); + params.setPackageName(packageName); + params.setParserName(parserName); + if (!runMDA) { + params.setOutputFile(new java.io.File(outFile)); + params.setOutputType(CopperIOType.FILE); + } + params.setUsePipeline(CopperPipelineType.GRAMMARBEANS); + params.setRunMDA(runMDA); + params.setDumpOutputType(CopperIOType.FILE); + params.setDumpFile(new File(dumpHtmlTo)); + params.setDumpFormat(CopperDumpType.HTML); + params.setDump(dumpHtml? CopperDumpControl.ON : CopperDumpControl.ERROR_ONLY); + + CompilerLogger logger = AuxiliaryMethods.getOrMakeLogger(params); + + try { + ParserSpecProcessor.normalizeParser(parser, logger); + return tok.wrap(ParserCompiler.compile(parser, params)); + } catch (CopperException exc) { + throw new SilverInternalError("Failed to compile Copper specification", + exc); + } + } + + public static CharacterSetRegex makeCharRange(String lo, String hi) { + CharacterSetRegex re = new CharacterSetRegex(); + re.addRange(lo.charAt(0), hi.charAt(0)); + return re; + } + + public static CharacterSetRegex makeSingleChar(String ch) { + CharacterSetRegex re = new CharacterSetRegex(); + re.addLooseChar(ch.charAt(0)); + return re; + } + + public static DisambiguationFunction + makeDisambiguationFunction(String sourceGrammar, NLocation location, + String id, String code, + ConsCellCollection members, + Boolean applicableToSubsets) { + try { + DisambiguationFunction f = new DisambiguationFunction(); + f.setLocation(svToCuLocation(sourceGrammar, location)); + f.setName(id); + f.setCode(code); + Set memberSet = + new HashSet(); + members.iterator().forEachRemaining(memberSet::add); + f.setMembers(memberSet); + f.setApplicableToSubsets(applicableToSubsets); + return f; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing DisambiguationFunction", + exc); + } + } + + public static CopperElementReference + makeElementReference(String sourceGrammar, NLocation location, + String grammarName, String name) { + try { + return CopperElementReference.ref( + CopperElementName.newName(grammarName), name, + svToCuLocation(sourceGrammar, location)); + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing ElementReference", exc); + } + } + + public static ExtendedParserBean + makeExtendedParserBean(String sourceGrammar, NLocation location, String id, + String pp, CopperElementReference startSymbol, + ConsCellCollection startLayout, + ConsCellCollection interfaceNames, + String parserClassAuxCode, String parserInitCode, + String preambleCode, Grammar hostGrammar, + Grammar extGrammar) { + try { + ExtendedParserBean parserBean = new ExtendedParserBean(); + parserBean.setLocation(svToCuLocation(sourceGrammar, location)); + parserBean.setName(id); + parserBean.setDisplayName(pp); + parserBean.setUnitary(false); + parserBean.setStartSymbol(startSymbol); + + Set startLayoutSet = new HashSet<>(); + startLayout.iterator().forEachRemaining(startLayoutSet::add); + + Set interfaceNameSet = new HashSet(); + interfaceNames.stream().map(StringCatter::toString).forEach(interfaceNameSet::add); + parserBean.setInterfaceNames(interfaceNameSet); + + parserBean.setStartLayout(startLayoutSet); + parserBean.setParserClassAuxCode(parserClassAuxCode); + parserBean.setParserInitCode(parserInitCode); + parserBean.setPreambleCode(preambleCode); + parserBean.addGrammar(hostGrammar); + parserBean.addGrammar(extGrammar); + parserBean.setHostGrammar(hostGrammar); + return parserBean; + } catch (CopperException exc) { + throw new SilverInternalError( + "CopperException while constructing ExtendedParserBean", exc); + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing ExtendedParserBean", exc); + } + } + + public static ExtensionGrammar makeExtensionGrammar( + String sourceGrammar, NLocation location, String id, + ConsCellCollection grammarElements, + ConsCellCollection markingTerminals, + ConsCellCollection bridgeProductions, + ConsCellCollection glueDisambiguationFunctions) { + try { + ExtensionGrammar grammar = new ExtensionGrammar(); + grammar.setLocation(svToCuLocation(sourceGrammar, location)); + grammar.setName(id); + grammarElements.iterator().forEachRemaining((ele) -> { + try { + grammar.addGrammarElement(ele); + } catch (CopperException exc) { + throw new SilverInternalError( + "CopperException while adding elements to ExtensionGrammar", exc); + } + }); + + for (CopperElementReference name : markingTerminals) + grammar.addMarkingTerminal(name.getName()); + for (CopperElementReference name : bridgeProductions) + grammar.addBridgeProduction(name.getName()); + for (CopperElementReference name : glueDisambiguationFunctions) + grammar.addGlueDisambiguationFunction(name.getName()); + + return grammar; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing ExtensionGrammar", exc); + } + } + + public static Grammar + makeGrammar(String sourceGrammar, NLocation location, String id, + ConsCellCollection grammarElements) { + try { + Grammar grammar = new Grammar(); + grammar.setLocation(svToCuLocation(sourceGrammar, location)); + grammar.setName(id); + grammarElements.iterator().forEachRemaining((ele) -> { + try { + grammar.addGrammarElement(ele); + } catch (CopperException exc) { + throw new SilverInternalError( + "CopperException while adding elements to Grammar", exc); + } + }); + return grammar; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing Grammar", exc); + } + } + + public static NonTerminal makeNonTerminal(String sourceGrammar, + NLocation location, String id, + String pp, String type_) { + try { + NonTerminal nt = new NonTerminal(); + nt.setLocation(svToCuLocation(sourceGrammar, location)); + nt.setName(id); + nt.setDisplayName(pp); + nt.setReturnType(type_); + return nt; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing NonTerminal", exc); + } + } + + public static ParserBean + makeParserBean(String sourceGrammar, NLocation location, String id, String pp, + CopperElementReference startSymbol, + ConsCellCollection startLayout, + ConsCellCollection interfaceNames, + String parserClassAuxCode, String parserInitCode, + String preambleCode, Grammar grammar) { + try { + ParserBean parserBean = new ParserBean(); + parserBean.setLocation(svToCuLocation(sourceGrammar, location)); + parserBean.setName(id); + parserBean.setDisplayName(pp); + parserBean.setUnitary(true); + parserBean.setStartSymbol(startSymbol); + + Set startLayoutSet = new HashSet<>(); + startLayout.iterator().forEachRemaining(startLayoutSet::add); + parserBean.setStartLayout(startLayoutSet); + + Set interfaceNameSet = new HashSet<>(); + interfaceNames.stream().map(StringCatter::toString).forEach(interfaceNameSet::add); + parserBean.setInterfaceNames(interfaceNameSet); + + parserBean.setParserClassAuxCode(parserClassAuxCode); + parserBean.setParserInitCode(parserInitCode); + parserBean.setPreambleCode(preambleCode); + parserBean.addGrammar(grammar); + return parserBean; + } catch (CopperException exc) { + throw new SilverInternalError( + "CopperException while constructing ParserBean", exc); + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing ParserBean", exc); + } + } + + public static ParserAttribute makeParserAttribute(String sourceGrammar, + NLocation location, + String id, String type_, + String code) { + try { + ParserAttribute attr = new ParserAttribute(); + attr.setLocation(svToCuLocation(sourceGrammar, location)); + attr.setName(id); + attr.setDisplayName(id); + attr.setAttributeType(type_); + attr.setCode(code); + return attr; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing ParserAttribute", exc); + } + } + + public static Production + makeProduction(String sourceGrammar, NLocation location, String id, + Integer precedence, CopperElementReference operator, + String code, CopperElementReference lhs, + ConsCellCollection rhsConsList, + ConsCellCollection prodLayout) { + try { + Production prod = new Production(); + prod.setLocation(svToCuLocation(sourceGrammar, location)); + prod.setName(id); + prod.setDisplayName(id); + if (precedence != null) + prod.setPrecedence(precedence); + prod.setOperator(operator); + prod.setCode(code); + prod.setLhs(lhs); + ArrayList rhs = new ArrayList<>(rhsConsList); + prod.setRhs(rhs); + ArrayList rhsVarNames = new ArrayList(); + for (int i = 0; i < rhs.size(); i++) + rhsVarNames.add(String.format("rhsVar_%d", i)); + prod.setRhsVarNames(rhsVarNames); + prod.setLayout(new HashSet<>(prodLayout)); + return prod; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing Production", exc); + } + } + + // The full path needs to be given, since common.Terminal exists... + public static edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Terminal + makeTerminal(String sourceGrammar, NLocation location, String id, String pp, + Regex regex, Integer precedence, String associativity, + String type_, String code, + ConsCellCollection classes, + CopperElementReference prefix, + ConsCellCollection submits, + ConsCellCollection dominates) { + try { + edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Terminal terminal = + new edu.umn.cs.melt.copper.compiletime.spec.grammarbeans.Terminal(); + terminal.setLocation(svToCuLocation(sourceGrammar, location)); + terminal.setName(id); + terminal.setDisplayName(pp); + terminal.setRegex(regex); + // TODO: operatorClass? + terminal.setOperatorPrecedence(precedence); + switch (associativity) { + case "left": + terminal.setOperatorAssociativity(OperatorAssociativity.LEFT); + break; + case "right": + terminal.setOperatorAssociativity(OperatorAssociativity.RIGHT); + break; + default: + terminal.setOperatorAssociativity(OperatorAssociativity.NONASSOC); + break; + } + terminal.setReturnType(type_); + terminal.setCode(code); + classes.iterator().forEachRemaining(terminal::addTerminalClass); + terminal.setPrefix(prefix); + submits.iterator().forEachRemaining(terminal::addSubmitsTo); + dominates.iterator().forEachRemaining(terminal::addDominates); + return terminal; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing Terminal", exc); + } + } + + public static TerminalClass makeTerminalClass(String sourceGrammar, + NLocation location, String id) { + try { + TerminalClass out = new TerminalClass(); + out.setLocation(svToCuLocation(sourceGrammar, location)); + out.setName(id); + return out; + } catch (ParseException exc) { + throw new SilverInternalError( + "Copper ParseException while constructing TerminalClass", exc); + } + } +} diff --git a/runtime/java/src/common/Decorable.java b/runtime/java/src/common/Decorable.java new file mode 100644 index 000000000..1c5d6e197 --- /dev/null +++ b/runtime/java/src/common/Decorable.java @@ -0,0 +1,19 @@ +package common; + +/** + * Things that can be decorated. + * @see Node + * @see DecoratedNode + * + * @author lucas + */ +public interface Decorable { + /** + * Decorate this node with additional inherited attributes. + * + * @param parent The DecoratedNode creating this one. (Whether this is a child or a local (or other) of that node.) + * @param inhs A map from attribute names to Lazys that define them. These Lazys will be supplied with 'parent' as their context for evaluation. + * @return A DecoratedNode with the attributes supplied. + */ + public DecoratedNode decorate(final DecoratedNode parent, final Lazy[] inhs); +} diff --git a/runtime/java/src/common/DecoratedNode.java b/runtime/java/src/common/DecoratedNode.java index 147f40139..8e56bfebe 100644 --- a/runtime/java/src/common/DecoratedNode.java +++ b/runtime/java/src/common/DecoratedNode.java @@ -1,6 +1,7 @@ package common; import common.exceptions.MissingDefinitionException; +import common.exceptions.SilverException; import common.exceptions.TraceException; /** @@ -11,7 +12,7 @@ * @author tedinski * @see Node */ -public class DecoratedNode implements Typed { +public class DecoratedNode implements Decorable, Typed { // TODO list: // - Delete parent / forwardParent. Or coalesce them (only NEED for debugging purposes, if inh become thunks!) // - Delete forwardValue (make it a local/production attribute) @@ -27,6 +28,8 @@ public class DecoratedNode implements Typed { // don't exist (syn#15 when there's only 10) // We only try to nicely report errors that are the fault of the user writing // broken code. (e.g. "no equation for syn") + + public OriginContext originCtx; //OriginContext of the "invocation" this is /** * The "undecorated" form of this DecoratedNode. (Never null) @@ -79,11 +82,12 @@ public class DecoratedNode implements Typed { /** * The inherited attributes supplied to this DecoratedNode, to be evaluated with context 'parent'. + * Not final, because we make a copy of the array if this DecoratedNode is extra-decorated. * * @see #inherited(String) * @see #inheritedValues */ - protected final Lazy[] inheritedAttributes; + protected Lazy[] inheritedAttributes; /** * A cache of the values of local attributes on this node. (incl. locals and prod) @@ -116,6 +120,7 @@ public class DecoratedNode implements Typed { final Lazy[] inhs, final DecoratedNode forwardParent) { this.self = self; this.parent = parent; + this.originCtx = parent!=null?parent.originCtx:null; this.inheritedAttributes = inhs; this.forwardParent = forwardParent; @@ -127,6 +132,7 @@ public class DecoratedNode implements Typed { // STATS: Uncomment to enable statistics //Statistics.dnSpawn(self!=null?self.getClass():TopNode.class); + // System.err.println("TRACE: " + (parent == null? "null" : parent.getDebugID()) + " creating decorated " + getDebugID()); } /** * Initialize a DecorateNode created by a FunctionNode. @@ -164,6 +170,32 @@ public final Node undecorate() { return self; } + /** + * Decorate this (partially decorated) node with additional inherited attributes. + * + * @param parent The DecoratedNode extra-decorating this one. (Whether this is a child or a local (or other) of that node.) + * @param inhs A map from attribute names to Lazys that define them. These Lazys will be supplied with 'parent' as their context for evaluation. + * @return A DecoratedNode with the additional attributes supplied, referencing this DecoratedNode as 'base'. + */ + @Override + public DecoratedNode decorate(final DecoratedNode parent, final Lazy[] inhs) { + // System.err.println("TRACE: " + parent.getDebugID() + " extra-decorating " + getDebugID()); + if (inhs != null) { + inheritedAttributes = inheritedAttributes.clone(); // Avoid modifying the static inh array from the original parent Node + for(int i = 0; i < inhs.length; i++) { + final int attribute = i; + if(inhs[attribute] != null) { + if(inheritedAttributes[attribute] == null) { + inheritedAttributes[attribute] = (context) -> inhs[attribute].eval(parent); + } else { + throw new SilverException(parent.getDebugID() + " cannot decorate " + getDebugID() + " with inh '" + self.getNameOfInhAttr(attribute) + "' as this attribute has already been provided by " + this.parent.getDebugID() + "."); + } + } + } + } + return this; + } + /** * Returns the child of this DecoratedNode, without potentially decorating it. * @@ -173,8 +205,9 @@ public final Node undecorate() { * @param child The number of the child to obtain. * @return The unmodified value of the child. */ - public Object childAsIs(final int child) { - return self.getChild(child); + @SuppressWarnings("unchecked") + public T childAsIs(final int child) { + return (T) self.getChild(child); } /** @@ -199,16 +232,16 @@ public DecoratedNode childDecorated(final int child) { } return (DecoratedNode)o; } - + /** * Create the DecoratedNode for a child. * Separate function to keep {@link #childDecorated} small and inlineable. * This is, after all, the "slow path." */ private final DecoratedNode createDecoratedChild(final int child) { - return ((Node)self.getChild(child)).decorate(this, self.getChildInheritedAttributes(child)); + return ((Decorable)self.getChild(child)).decorate(this, self.getChildInheritedAttributes(child)); } - + /** * Get the value of a local, caching it for re-use. * @@ -217,7 +250,8 @@ private final DecoratedNode createDecoratedChild(final int child) { * @param attribute The full name of the local to obtain. * @return The value of the local. */ - public Object localAsIs(final int attribute) { + @SuppressWarnings("unchecked") + public T localAsIs(final int attribute) { Object o = this.localValues[attribute]; if(o == null) { o = evalLocalAsIs(attribute); @@ -228,7 +262,7 @@ public Object localAsIs(final int attribute) { // not recommended due to IO objects (IOString, etc) this.localValues[attribute] = o; } - return o; + return (T) o; } /** @@ -239,14 +273,14 @@ private final Object evalLocalAsIs(final int attribute) { return self.getLocal(attribute).eval(this); } catch(Throwable t) { throw handleLocalError(attribute, t); - } + } } /** * Error generation code for local attribute evaluation. * Kept separate so as not to impact inlining decisions. */ - private final RuntimeException handleLocalError(final int attribute, final Throwable t) { + private final SilverException handleLocalError(final int attribute, final Throwable t) { // Rather than checking in the fast path, we try to reconstruct what went wrong in the slow path. if(self.getLocal(attribute) == null) { return new MissingDefinitionException("Local '" + self.getNameOfLocalAttr(attribute) + "' not defined in " + getDebugID()); @@ -280,7 +314,7 @@ public DecoratedNode localDecorated(final int attribute) { * Another case of keeping the slow paths out of here, so it can be inlined. */ private final DecoratedNode evalLocalDecorated(final int attribute) { - return ((Node)evalLocalAsIs(attribute)).decorate(this, self.getLocalInheritedAttributes(attribute)); + return ((Decorable)evalLocalAsIs(attribute)).decorate(this, self.getLocalInheritedAttributes(attribute)); } /** @@ -290,8 +324,10 @@ private final DecoratedNode evalLocalDecorated(final int attribute) { * @param attribute The full name of the attribute. * @return The value of the attribute. */ - public Object synthesized(final int attribute) { - //System.err.println("TRACE: " + name + " demanding syn attribute: " + attribute); + @SuppressWarnings("unchecked") + public T synthesized(final int attribute) { + // common.Util.stackProbe(); + // System.err.println("TRACE: " + getDebugID() + " demanding syn attribute: " + self.getNameOfSynAttr(attribute)); Object o = this.synthesizedValues[attribute]; if(o == null) { @@ -302,7 +338,7 @@ public Object synthesized(final int attribute) { // CACHE : comment out to disable caching for synthesized attributes this.synthesizedValues[attribute] = o; } - return o; + return (T) o; } private final Object evalSyn(final int attribute) { @@ -310,6 +346,19 @@ private final Object evalSyn(final int attribute) { Lazy l = self.getSynthesized(attribute); if(l != null) { try { + if(l instanceof CollectionAttribute) { + // This is necessary to ensure collection + // attribute equations in aspect default + // productions work; see + // https://github.com/melt-umn/silver/issues/290 + CollectionAttribute prodAttr = (CollectionAttribute)l; + if(!prodAttr.appliedNTFix) { + prodAttr.appliedNTFix = true; + CollectionAttribute ntAttr = (CollectionAttribute)self.getDefaultSynthesized(attribute); + if(ntAttr != null) + prodAttr.getPieces().addAll(ntAttr.getPieces()); + } + } return l.eval(this); } catch(Throwable t) { throw new TraceException("While evaling syn '" + self.getNameOfSynAttr(attribute) + "' in " + getDebugID(), t); @@ -318,7 +367,7 @@ private final Object evalSyn(final int attribute) { try { return forward().synthesized(attribute); } catch(Throwable t) { - throw new TraceException("While evaling syn via forward in " + getDebugID(), t); + throw new TraceException("While evaling syn '" + self.getNameOfSynAttr(attribute) + "' via forward in " + getDebugID(), t); } } else { l = self.getDefaultSynthesized(attribute); @@ -329,7 +378,7 @@ private final Object evalSyn(final int attribute) { throw new TraceException("While evaling default for '" + self.getNameOfSynAttr(attribute) + "' in " + getDebugID(), t); } } else { - throw new MissingDefinitionException("Synthesized attribute '" + self.getNameOfSynAttr(attribute) + "' not defined in " + getDebugID()); + throw new MissingDefinitionException("Synthesized attribute '" + self.getNameOfSynAttr(attribute) + "' not defined in " + getDebugID()); } } } @@ -381,8 +430,10 @@ private final RuntimeException handleFwdError(Throwable t) { * * @see #inheritedForwarded(String) */ - public Object inherited(final int attribute) { - //System.err.println("TRACE: " + name + " demanding inh attribute: " + attribute); + @SuppressWarnings("unchecked") + public T inherited(final int attribute) { + // common.Util.stackProbe(); + // System.err.println("TRACE: " + getDebugID() + " demanding inh attribute: " + self.getNameOfInhAttr(attribute)); Object o = this.inheritedValues[attribute]; if(o == null) { @@ -396,7 +447,7 @@ public Object inherited(final int attribute) { // we're recomputing this.inheritedValues[attribute] = o; } - return o; + return (T) o; } private final Object evalInhSomehow(final int attribute) { @@ -412,12 +463,12 @@ private final Object evalInhViaFwdP(final int attribute) { throw handleInhFwdPError(attribute, t); } } - private final RuntimeException handleInhFwdPError(final int attribute, Throwable t) { + private final SilverException handleInhFwdPError(final int attribute, Throwable t) { //This seems impossible since we're checking if forwardParent==null earlier up there! //if(forwardParent == null) { // return new MissingDefinitionException("Inherited attribute '" + self.getNameOfInhAttr(attribute) + "' not provided to " + getDebugID() + " by " + parent.getDebugID()); //} - return new TraceException("While evaling inh via forward in " + getDebugID(), t); + return new TraceException("While evaling inh '" + self.getNameOfInhAttr(attribute) + "' via forward in " + getDebugID(), t); } private final Object evalInhHere(final int attribute) { try { @@ -426,14 +477,19 @@ private final Object evalInhHere(final int attribute) { throw handleInhHereError(attribute, t); } } - private final RuntimeException handleInhHereError(final int attribute, Throwable t) { + private final SilverException handleInhHereError(final int attribute, Throwable t) { // We specifically have to check here for inheritedAttributes == null, because // that's what happens when we don't supply any inherited attributes... // That is, unlike the unconditional access earlier for inheritedValues[attribute] // (which could be null if *no inherited attributes occur at all* on this // node), this could be the result of correctly compiled, but wrongly written user // code. - if(inheritedAttributes == null || inheritedAttributes[attribute] == null) { + // Also, we need to check for attribute >= inheritedAttributes.length, because + // with inherited occurs-on constraints don't know how big to make the array, + // only the largest supplied attribute index, + // so the user omitting some inherited equations for attributes with higher indices + // could mean the resulting array that we are passed is too short. Sigh. + if(inheritedAttributes == null || attribute >= inheritedAttributes.length || inheritedAttributes[attribute] == null) { return new MissingDefinitionException("Inherited attribute '" + self.getNameOfInhAttr(attribute) + "' not provided to " + getDebugID() + " by " + parent.getDebugID()); } return new TraceException("While evaling inh '" + self.getNameOfInhAttr(attribute) + "' in " + getDebugID(), t); @@ -447,7 +503,8 @@ private final RuntimeException handleInhHereError(final int attribute, Throwable * @return The value of the attribute. */ protected Object inheritedForwarded(final int attribute) { - //System.err.println("TRACE: " + name + " demanding FORWARDED inh attribute: " + attribute); + // common.Util.stackProbe(); + // System.err.println("TRACE: " + getDebugID() + " demanding FORWARDED inh attribute: " + self.getNameOfInhAttr(attribute)); // No cache look up here. There is only one forward production. It will call this method // a maximum of once for each attribute, since it will cache the result. @@ -464,7 +521,7 @@ protected Object inheritedForwarded(final int attribute) { } } - private final RuntimeException handleInhFwdError(final int attribute, Throwable t) { + private final SilverException handleInhFwdError(final int attribute, Throwable t) { return new TraceException("While evaling inh '" + self.getNameOfInhAttr(attribute) + "' for forward in " + getDebugID(), t); } @@ -528,9 +585,8 @@ public final Object contextInheritedLazy(final int index) { } @Override - public final BaseTypeRep getType() { - final BaseTypeRep selfTypeRep = self.getType(); - return new BaseTypeRep("Decorated " + selfTypeRep.baseName, selfTypeRep.params); + public final DecoratedTypeRep getType() { + return new DecoratedTypeRep(self.getType()); } /** @@ -542,11 +598,11 @@ public final String getDebugID() { String qualifier; if(self == null) { return ""; - } else if(self instanceof core.Alocation) { - DecoratedNode loc = ((core.Alocation)self).getAnno_core_location().decorate(TopNode.singleton, (Lazy[])null); - String file = loc.synthesized(core.Init.core_filename__ON__core_Location).toString(); - int line = (Integer)loc.synthesized(core.Init.core_line__ON__core_Location); - int col = (Integer)loc.synthesized(core.Init.core_column__ON__core_Location); + } else if(self instanceof silver.core.Alocation) { + DecoratedNode loc = ((silver.core.Alocation)self).getAnno_silver_core_location().decorate(TopNode.singleton, (Lazy[])null); + String file = loc.synthesized(silver.core.Init.silver_core_filename__ON__silver_core_Location).toString(); + int line = (Integer)loc.synthesized(silver.core.Init.silver_core_line__ON__silver_core_Location); + int col = (Integer)loc.synthesized(silver.core.Init.silver_core_column__ON__silver_core_Location); qualifier = ", " + file + ":" + Integer.toString(line) + ":" + Integer.toString(col); } else { qualifier = ""; diff --git a/runtime/java/src/common/DecoratedTypeRep.java b/runtime/java/src/common/DecoratedTypeRep.java new file mode 100644 index 000000000..f691f4514 --- /dev/null +++ b/runtime/java/src/common/DecoratedTypeRep.java @@ -0,0 +1,37 @@ +package common; + +/** + * Representation of a nonterminal reference Silver type, used for run-time type checking in reification. + * + * @author krame505 + */ +public class DecoratedTypeRep extends TypeRep { + /** + * The undecorated type. + */ + public final TypeRep param; + + /** + * Create a DecoratedTypeRep + * + * @param param The undecorated type. + */ + public DecoratedTypeRep(final TypeRep param) { + this.param = param; + } + + @Override + protected final boolean unifyPartial(final TypeRep other) { + return other instanceof DecoratedTypeRep && unify(param, ((DecoratedTypeRep)other).param); + } + + @Override + public final String typeName() { + return param.typeName(); + } + + @Override + public final String toString() { + return "Decorated " + param; + } +} diff --git a/runtime/java/src/common/Decorator.java b/runtime/java/src/common/Decorator.java index 2452a0d59..ebc7b292d 100644 --- a/runtime/java/src/common/Decorator.java +++ b/runtime/java/src/common/Decorator.java @@ -15,9 +15,9 @@ * @author tedinski */ abstract public class Decorator { - abstract public void decorate(Class production); + abstract public void decorate(RTTIManager.Prodleton production); - public static void applyDecorators(List nonterminal, Class production) { + public static void applyDecorators(List nonterminal, RTTIManager.Prodleton production) { for( Decorator decorator : nonterminal ) { decorator.decorate(production); } @@ -28,42 +28,32 @@ public static void applyDecorators(List nonterminal, Class product } // Default actions for autocopy - static protected void decorateAutoCopy(final Class production, final String attribute) { + static protected void decorateAutoCopy(final RTTIManager.Prodleton production, final String attribute) { // Find the index of the inh attribute on this production's NT int attrindex; - try { - String[] oi = (String[])production.getField("occurs_inh").get(null); - attrindex = Arrays.asList(oi).indexOf(attribute); - if(attrindex == -1) - throw new SilverInternalError("Attribute doesn't occur on NT it is supposed to?"); - } catch(Throwable t) { - throw new SilverInternalError("Error while applying autocopy decorators.", t); - } + String[] oi = production.getNonterminalton().getOccursInh(); + attrindex = Arrays.asList(oi).indexOf(attribute); + if(attrindex == -1) + throw new SilverInternalError("Attribute doesn't occur on NT it is supposed to?"); // Create the lazy that we'll be putting on children. Lazy eq = new acLazy(attrindex); // Get information about the children - Class childTypes[]; + String childTypes[]; Lazy[][] inheritedAttributes; - try { - childTypes = (Class[]) production.getField("childTypes").get(null); - inheritedAttributes = (Lazy[][])production.getField("childInheritedAttributes").get(null); - } catch (Throwable t) { - throw new SilverInternalError("Attempting to decorate a nonproduction?",t); - } + childTypes = production.getChildTypes(); + inheritedAttributes = production.getChildInheritedAttributes(); for(int i = 0; i < childTypes.length; i++) { + if (childTypes[i] == null) continue; // Not a nonterminal + String[] occurs; - try { - occurs = (String[])childTypes[i].getField("occurs_inh").get(null); - } catch (NoSuchFieldException e) { - // This is a non-error. We expect there to be children that aren't NTs - continue; - } catch (Throwable t) { - throw new SilverInternalError("Problem fetching class information through reflection.",t); - } + RTTIManager.Nonterminalton nt = RTTIManager.getNonterminalton(childTypes[i]); + if (nt == null) throw new SilverInternalError("Cannot find nonterminal " + childTypes[i]); + occurs = nt.getOccursInh(); + int loc = Arrays.asList(occurs).indexOf(attribute); if(loc != -1) { // The nonterminal of this child contains the attribute in question diff --git a/runtime/java/src/common/FunctionNode.java b/runtime/java/src/common/FunctionNode.java index 482e191ba..f2cb34042 100644 --- a/runtime/java/src/common/FunctionNode.java +++ b/runtime/java/src/common/FunctionNode.java @@ -1,6 +1,7 @@ package common; import common.exceptions.SilverInternalError; +import silver.core.*; /** * FunctionNode is a Node, but with a few methods "removed". @@ -17,13 +18,11 @@ */ public abstract class FunctionNode extends Node { - /** - * The normal way of decorating a function node. - * - * @return A "decorated" form of this FunctionNode - */ - public final DecoratedNode decorate() { - return new DecoratedNode(getNumberOfChildren(), getNumberOfLocalAttrs(), this); + // Used only when needing origins info on lazily evaluated locals in functions :/ + public DecoratedNode decorate(OriginContext originCtx) { + DecoratedNode tmp = decorate(); + tmp.originCtx = originCtx; + return tmp; } @Override @@ -86,4 +85,8 @@ public final Object getAnno(final String name) { public final common.BaseTypeRep getType() { throw new SilverInternalError("Function nodes do not have a type!"); } + + public RTTIManager.Prodleton getProdleton() { + throw new SilverInternalError("Function nodes do not have prodletons!"); + } } diff --git a/runtime/java/src/common/FunctionTypeRep.java b/runtime/java/src/common/FunctionTypeRep.java index 569cd558a..8a63f90ba 100644 --- a/runtime/java/src/common/FunctionTypeRep.java +++ b/runtime/java/src/common/FunctionTypeRep.java @@ -1,85 +1,61 @@ package common; /** - * Representation of a (possibly parametric) basic Silver type, used for run-time type checking in reification. + * Representation of a Silver function type, used for run-time type checking in reification. * * @author krame505 */ public class FunctionTypeRep extends TypeRep { /** - * The result type of the function. + * The number of parameters to the function. */ - public final TypeRep result; - - /** - * The types of the parameters to the function. - */ - public final TypeRep[] params; + public final int params; /** * The names of the named parameters to the function. */ - public final String[] namedParamNames; - - /** - * The types of the named parameters to the function. - */ - public final TypeRep[] namedParamTypes; + public final String[] namedParams; /** * Create a FunctionTypeRep. - * - * @param result The result type of the function. - * @param params The parameter types of the function. - * @param namedParamNames The names of the named parameters to the function. - * @param namedParamTypes The types of the named parameters to the function. + * + * @param params The number of parameters to the function. + * @param namedParams The names of the named parameters to the function. */ - public FunctionTypeRep(final TypeRep result, final TypeRep[] params, final String[] namedParamNames, final TypeRep[] namedParamTypes) { - this.result = result; + public FunctionTypeRep(final int params, final String[] namedParams) { this.params = params; - this.namedParamNames = namedParamNames; - this.namedParamTypes = namedParamTypes; - - assert namedParamNames.length == namedParamTypes.length; + this.namedParams = namedParams; } @Override protected final boolean unifyPartial(final TypeRep other) { if (!(other instanceof FunctionTypeRep) || - !TypeRep.unify(result, ((FunctionTypeRep)other).result) || - params.length != ((FunctionTypeRep)other).params.length || - namedParamNames.length != ((FunctionTypeRep)other).namedParamNames.length) { + params != ((FunctionTypeRep)other).params || + namedParams.length != ((FunctionTypeRep)other).namedParams.length) { return false; } - - for (int i = 0; i < params.length; i++) { - if (!TypeRep.unify(params[i], ((FunctionTypeRep)other).params[i])) { + for (int i = 0; i < namedParams.length; i++) { + if (!namedParams[i].equals(((FunctionTypeRep)other).namedParams[i])) { return false; } } - - for (int i = 0; i < namedParamNames.length; i++) { - if (!namedParamNames[i].equals(((FunctionTypeRep)other).namedParamNames[i]) || - !TypeRep.unify(namedParamTypes[i], ((FunctionTypeRep)other).namedParamTypes[i])) { - return false; - } - } - return true; } + @Override + public final String typeName() { + return "::="; + } + @Override public final String toString() { String paramsToString = ""; - if (params.length > 0) { - paramsToString += params[0].toString(); - } - for (int i = 1; i < params.length; i++) { - paramsToString += " " + params[i].toString(); + for (int i = 0; i < params; i++) { + paramsToString += " _"; } - for (int i = 0; i < namedParamNames.length; i++) { - paramsToString += "; " + namedParamNames[i] + "::" + namedParamTypes[i].toString(); + for (int i = 0; i < namedParams.length; i++) { + paramsToString += "; " + namedParams[i] + "::_"; } - return "(" + result.toString() + " ::= " + paramsToString + ")"; + return "(_ ::=" + paramsToString + ")"; } } diff --git a/runtime/java/src/common/IOToken.java b/runtime/java/src/common/IOToken.java index d47bfdd81..58ae606d4 100644 --- a/runtime/java/src/common/IOToken.java +++ b/runtime/java/src/common/IOToken.java @@ -1,131 +1,165 @@ package common; -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.Writer; +import common.exceptions.SilverExit; +import java.io.*; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.LinkOption; import java.nio.file.Path; import java.nio.file.Paths; - -import common.exceptions.SilverExit; -import core.NIOVal; -import core.Pioval; +import silver.core.NIOVal; +import silver.core.Pioval; +import silver.core.Pjust; +import silver.core.Pnothing; /** * This class represents the `IO` type in Silver. - * + * *

By convention we translate a Silver IO call of the form * `IO ::= args IO` to a method with the signature * `IOToken f(args)`. We return `this` or `this.wrap` * and the final `IO` argument is used to invoke the method. - * + * *

`iotoken.print(str)` means the `IOToken` must be demanded * before the method can be invoked, which is the desired invariant. - * + * *

Silver types are used in argument wherever possible, except for * primitives like `int`. - * + * * @author tedinski */ public final class IOToken implements Typed { - public static final IOToken singleton = new IOToken(); - - private IOToken() {} - - /** - * Function that return IOToken can return `this`. - * Those that return `IOVal<>` can return `this.wrap(...)`. - */ - private NIOVal wrap(Object arg) { - return new Pioval(this, arg); - } - - /** - * Used for `unsafeTrace` - */ - public Object identity(Object arg) { - return arg; - } + public static final IOToken singleton = new IOToken(); - /** - *

IO ::= val::Integer i::IO
- */ - public IOToken exit(int status) { - throw new SilverExit(status); + private IOToken() {} + + /** + * Function that return IOToken can return `this`. + * Those that return `IOVal<>` can return `this.wrap(...)`. + */ + public NIOVal wrap(Object arg) { return new Pioval(this, arg); } + + /** + * Used for `unsafeTrace` + */ + public Object identity(Object arg) { return arg; } + + /** + *
IO ::= val::Integer i::IO
+ */ + public IOToken exit(int status) { throw new SilverExit(status); } + + /** + *
IO ::= s::String i::IO
+ */ + public IOToken print(StringCatter str) { + // TODO: Should we avoid demanding StringCatter objects? + System.out.print(str.toString()); + return this; + } + + /** + *
IO ::= s::String i::IO
+ */ + public IOToken eprint(StringCatter str) { + // TODO: Should we avoid demanding StringCatter objects? + System.err.print(str.toString()); + return this; + } + + // Used by readLineStdin + private static BufferedReader our_stdin = null; + /** + *
IOVal ::= i::IO
+ */ + public NIOVal readLineStdin() { + try { + if (our_stdin == null) { + // Persist this, since it might buffer bytes for the NEXT line + our_stdin = new BufferedReader(new InputStreamReader(System.in)); + } + String line = our_stdin.readLine(); + if (line == null) + return this.wrap(new Pnothing()); + else + return this.wrap(new Pjust(new StringCatter(line))); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + /** + *
IOVal ::= s::String i::IO
+ */ + public NIOVal mkdir(StringCatter filepath) { + return this.wrap(new File(filepath.toString()).mkdirs()); + } + + /** + * Invokes an external command, channeling all stdin/out/err to the console + * normally. + * + * N.B. uses 'bash' to invoke the command. There are two major reasons: + * (1) allows redirects in shell command, which is useful + * (2) because this command takes just a single string, we must somehow deal + * with spaces. e.g. 'touch "abc 123"' bash take care of interpreting the + * quotes for us. + * + * Unfortunate platform dependency though. + * + *
IOVal ::= s::String i::IO
+ * + * @param shell_command A string for back to interpret and execute. + * @return The exit status of the process. + */ + public NIOVal system(StringCatter shell_command) { + try { + ProcessBuilder pb = + new ProcessBuilder("bash", "-c", shell_command.toString()); + pb.inheritIO(); + Process p = pb.start(); + return this.wrap(p.waitFor()); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + } } /** - *
IO ::= s::String i::IO
+ *
IO ::= file::String contents::String i::IO
*/ - public IOToken print(StringCatter str) { - // TODO: Should we avoid demanding StringCatter objects? - System.out.print(str.toString()); - return this; + public IOToken writeFile(StringCatter filename, StringCatter content) { + return writeFileActual(filename, content, false); } - // Used by readLineStdin - private static BufferedReader our_stdin = null; - /** - *
IOVal ::= i::IO
- */ - public NIOVal readLineStdin() { + public NIOVal readByteFile(StringCatter filename) { try { - if(our_stdin == null) { - // Persist this, since it might buffer bytes for the NEXT line - our_stdin = new BufferedReader(new InputStreamReader(System.in)); - } - return this.wrap(new StringCatter(our_stdin.readLine())); - } catch (IOException e) { + Path path = Paths.get(filename.toString()); + byte[] data = Files.readAllBytes(path); + return this.wrap(data); + } catch (Exception e) { throw new RuntimeException(e); } } /** - *
IOVal ::= s::String i::IO
- */ - public NIOVal mkdir(StringCatter filepath) { - return this.wrap(new File(filepath.toString()).mkdirs()); - } - - /** - * Invokes an external command, channeling all stdin/out/err to the console normally. - * - * N.B. uses 'bash' to invoke the command. There are two major reasons: - * (1) allows redirects in shell command, which is useful - * (2) because this command takes just a single string, we must somehow deal with spaces. - * e.g. 'touch "abc 123"' bash take care of interpreting the quotes for us. - * - * Unfortunate platform dependency though. - * - *
IOVal ::= s::String i::IO
- * - * @param shell_command A string for back to interpret and execute. - * @return The exit status of the process. + *
IO ::= file::String contents::ByteArray i::IO
*/ - public NIOVal system(StringCatter shell_command) { + public IOToken writeByteFile(StringCatter filename, byte[] content) { try { - ProcessBuilder pb = new ProcessBuilder("bash", "-c", shell_command.toString()); - pb.inheritIO(); - Process p = pb.start(); - return this.wrap(p.waitFor()); + File outputFile = new File(filename.toString()); + FileOutputStream outputStream = new FileOutputStream(outputFile); + outputStream.write(content); + outputStream.flush(); + return this; } catch (Exception e) { throw new RuntimeException(e); } } - /** - *
IO ::= file::String contents::String i::IO
- */ - public IOToken writeFile(StringCatter filename, StringCatter content) { - return writeFileActual(filename, content, false); - } - /** *
IO ::= file::String contents::String i::IO
*/ @@ -355,9 +389,15 @@ private static int currentTime() { return (int)(System.currentTimeMillis() / 1000); } + /** + *
IOVal ::= r::RandomGen i::IO
+ */ + public NIOVal runRandomGen(final OriginContext ctx, silver.core.NRandomGen r) { + return this.wrap(common.RandomGen.runRandomGen(ctx, r)); + } + @Override public TypeRep getType() { - return new BaseTypeRep("core:IO"); + return new BaseTypeRep("silver:core:IO"); } - } diff --git a/runtime/java/src/common/Lazy.java b/runtime/java/src/common/Lazy.java index 3c8a31e97..2eaf53d4d 100644 --- a/runtime/java/src/common/Lazy.java +++ b/runtime/java/src/common/Lazy.java @@ -1,15 +1,27 @@ -package common; - -/** - * Lazy is the interface used to create function pointers, essentially. - * - *

Typically, they are only used to supply - * *static* arrays of certain kinds in nonterminal classes. e.g. - * "To evaluate this attribute on this nonterminal, use this function pointer." - * - * @author tedinski, bodin - */ -public interface Lazy { - // TODO: probably make this Lazy... - public Object eval(DecoratedNode context); -} +package common; + +/** + * Lazy is the interface used to create function pointers, essentially. + * + *

Typically, they are only used to supply + * *static* arrays of certain kinds in nonterminal classes. e.g. + * "To evaluate this attribute on this nonterminal, use this function pointer." + * + * @author tedinski, bodin + */ +public interface Lazy { + // TODO: probably make this Lazy... + public Object eval(DecoratedNode context); + + public static class Trap implements Lazy { + String m; + + public Trap(String m) { + this.m = m; + } + + public Object eval(DecoratedNode context) { + return Util.error(m); + } + } +} diff --git a/runtime/java/src/common/ListTypeRep.java b/runtime/java/src/common/ListTypeRep.java deleted file mode 100644 index da65776fa..000000000 --- a/runtime/java/src/common/ListTypeRep.java +++ /dev/null @@ -1,32 +0,0 @@ -package common; - -/** - * Representation of a list Silver type, used for run-time type checking in reification. - * - * @author krame505 - */ -public class ListTypeRep extends TypeRep { - /** - * The type parameter for the list. - */ - public final TypeRep param; - - /** - * Create a BaseTypeRep - * - * @param param The type parameter for the list. - */ - public ListTypeRep(final TypeRep param) { - this.param = param; - } - - @Override - protected boolean unifyPartial(final TypeRep other) { - return other instanceof ListTypeRep && TypeRep.unify(param, ((ListTypeRep)other).param); - } - - @Override - public final String toString() { - return "[" + param.toString() + "]"; - } -} diff --git a/runtime/java/src/common/Markdown.java b/runtime/java/src/common/Markdown.java new file mode 100644 index 000000000..6e1d56514 --- /dev/null +++ b/runtime/java/src/common/Markdown.java @@ -0,0 +1,42 @@ +package common; + +import common.javainterop.ConsCellCollection; +import java.util.ArrayList; +import org.commonmark.node.AbstractVisitor; +import org.commonmark.node.FencedCodeBlock; +import org.commonmark.node.SourceSpan; +import org.commonmark.parser.IncludeSourceSpans; +import org.commonmark.parser.Parser; +import silver.core.Ploc; +import silver.core.Ppair; + +/** + * The Markdown class contains some utilities that are too groty to put inline + * in the compiler. + * + * @author remexre + */ +public final class Markdown { + public static ConsCell extractCodeBlocks(String path, String markdown) { + ArrayList out = new ArrayList(); + Parser.builder() + .includeSourceSpans(IncludeSourceSpans.BLOCKS) + .build() + .parse(markdown) + .accept(new AbstractVisitor() { + public void visit(FencedCodeBlock node) { + SourceSpan span = node.getSourceSpans().get(0); + + // TODO: SourceSpan only provides line/column, but it provides a + // length in UTF-16 code units; do we have a utility for this? + Ploc location = new Ploc(path, span.getLineIndex(), span.getColumnIndex(), + span.getLineIndex(), span.getColumnIndex(), 0, 0); + + out.add(new Ppair(new StringCatter(node.getInfo()), + new Ppair(location, + new StringCatter(node.getLiteral())))); + } + }); + return ConsCellCollection.fromIterator(out.iterator()); + } +} diff --git a/runtime/java/src/common/Node.java b/runtime/java/src/common/Node.java index d21d8fd26..a47067576 100644 --- a/runtime/java/src/common/Node.java +++ b/runtime/java/src/common/Node.java @@ -1,214 +1,212 @@ -package common; - -/** - * Node represents undecorated nodes. That is, we have children, but no inherited attributes, yet. - * - *

This is subclassed by nonterminals (which are then subclassed by productions, which implement - * the abstract methods). Generally speaking, nothing is used on this class externally except the - * various decorate methods, and by the DecoratedNode class. - * - * @author tedinski - * @see DecoratedNode - */ -public abstract class Node implements Typed { - - // Common manipulators of Node objects. - - /** - * The normal way of decorating a node. - * (child and local) - * - * @param parent The DecoratedNode creating this one. (Whether this is a child or a local (or other) of that node.) - * @param inhs A map from attribute names to Lazys that define them. These Lazys will be supplied with 'parent' as their context for evaluation. - * @return A "decorated" form of this Node - */ - public final DecoratedNode decorate(final DecoratedNode parent, final Lazy[] inhs) { - return new DecoratedNode(getNumberOfChildren(), - getNumberOfInhAttrs(), - getNumberOfSynAttrs(), - getNumberOfLocalAttrs(), - this, parent, inhs, null); - } - - /** - * The way of decorating a forward node. - * (fwd only) - * - * @param parent The "true parent" of this node (same as the fwdParent's parent) - * @param fwdParent The DecoratedNode that forwards to the one we are about to create. We will pass inherited attribute access requests to this node. - * @return A "decorated" form of this Node - */ - public final DecoratedNode decorate(final DecoratedNode parent, final DecoratedNode fwdParent) { - return new DecoratedNode(getNumberOfChildren(), - getNumberOfInhAttrs(), - getNumberOfSynAttrs(), - getNumberOfLocalAttrs(), - this, parent, null, fwdParent); - } - - /** - * A convenience method unused by generate Silver code, but useful when working with - * the Silver runtime from Java. - * - * @return A node decorated with no inherited attributes, without a parent. - */ - public DecoratedNode decorate() { - return decorate(TopNode.singleton, (Lazy[])null); - } - - // These methods are to be provided by the *nonterminal* - - /** - * Used to create arrays of appropriate size in DecoratedNode. - * - * @return The number of synthesized attributes that occur on this nonterminal type. - */ - public abstract int getNumberOfSynAttrs(); - - /** - * Used for debugging, stack traces especially. - * - * @param index The index of the synthesized attribute to return the name of - * @return The name of the synthesized attribute at this index - */ - public abstract String getNameOfSynAttr(final int index); - - /** - * When a production does not forward and lacks an equation for a synthesized attribute, this is consulted instead. - * - * @param index Any synthesized attribute on this Node - * @return A Lazy to evaluate on a decorated form of this Node, to get the DEFAULT value of the attribute. - */ - public abstract Lazy getDefaultSynthesized(final int index); - - /** - * Used to create arrays of appropriate size in DecoratedNode. - * - * @return The number of inherited attributes that occur on this nonterminal type. - */ - public abstract int getNumberOfInhAttrs(); - - /** - * Used for debugging, stack traces especially. - * - * @param index The index of the inherited attribute to return the name of - * @return The name of the inherited attribute at this index - */ - public abstract String getNameOfInhAttr(final int index); - - /** - * Used for reflection. - * - * @return An array of the names of all annotations. - */ - public abstract String[] getAnnoNames(); - - /** - * Used for reflection. - * - * @param name The name of any annotation returned by getAnnoNames() - * @return The annotation object. (Thunk already evaluated) - */ - public abstract Object getAnno(final String name); - - - // These methods are to be provided by the *production* - - /** - * Used for debugging, and pattern matching. - * - * @return The full name of this Node. (e.g. "silver:definition:core:baseExpr") - */ - public abstract String getName(); - - /** - * Returns the number of children, same way array length does. - * - *

For example, a Node with 2 children allows you to request child 0 and 1. - * - * @return The number of children this Node has. - */ - public abstract int getNumberOfChildren(); - - /** - * Access a (raw) child of this Node. - * - * @param child A number in the range 0 - getNumberofChildren() - * @return The child object. (Thunk already evaluated) - * @see Node#getNumberOfChildren() - */ - public abstract Object getChild(final int child); - - /** - * @param child A number in the range 0 - getNumberofChildren() - * @return The child object, WITHOUT forcing the Thunk, if any. - */ - public abstract Object getChildLazy(final int child); - - /** - * @param key The child index to look up the inherited attributes. - * @return An array containing the inherited attributes supplied to that child - */ - public abstract Lazy[] getChildInheritedAttributes(final int index); - - /** - * Used to create arrays of appropriate size in DecoratedNode. - * - * @return The number of local and production attributes that occur on this production - */ - public abstract int getNumberOfLocalAttrs(); - - /** - * Used for debugging, stack traces especially. - * - * @param index The index of the local attribute to return the name of - * @return The name of the local attribute at this index - */ - public abstract String getNameOfLocalAttr(final int index); - - /** - * @param name The index of a local or production attribute on this Node - * @return A Lazy to evaluate on a decorated form of this Node, to get the value of the attribute - */ - public abstract Lazy getLocal(final int index); - - /** - * @param key The index for a local, to retrieve inherited attributes for. - * @return An array containing the inherited attributes supplied to that local - */ - public abstract Lazy[] getLocalInheritedAttributes(final int index); - - /** - * Reports whether or not this production forwards. - * - * @return true is {@link #evalForward} can be called, false if that immediately throws. - */ - public abstract boolean hasForward(); - - /** - * It may help conceptually to imagine this returns a Lazy that the DN then calls with itself as context. - * We've simply merged this into this method to avoid an unnecessary Lazy. - * - * @param context The DN of this node, to use to evaluate the forward equation. - * @return The Node that context forwards to. - */ - public abstract Node evalForward(final DecoratedNode context); - - /** - * Get any overridden attributes for this node's forward. (e.g. forwarding with { inh = foo; }) - * - * @param index The inherited attribute requested by a forwarded-to Node. - * @return A Lazy to evaluate on a decorated form of this Node, to get the value of this attribute provided to the forward. - */ - public abstract Lazy getForwardInheritedAttributes(final int index); - - /** - * @param index Any synthesized attribute on this Node - * @return A Lazy to evaluate on a decorated form of this Node, to get the value of the attribute - */ - public abstract Lazy getSynthesized(final int index); - - // Override with a more specific return type - @Override - public abstract BaseTypeRep getType(); -} +package common; + +/** + * Node represents undecorated nodes. That is, we have children, but no inherited attributes, yet. + * + *

This is subclassed by nonterminals (which are then subclassed by productions, which implement + * the abstract methods). Generally speaking, nothing is used on this class externally except the + * various decorate methods, and by the DecoratedNode class. + * + * @author tedinski + * @see DecoratedNode + */ +public abstract class Node implements Decorable, Typed { + // Common manipulators of Node objects. + + /** + * The normal way of decorating a node. + * (child and local) + * + * @param parent The DecoratedNode creating this one. (Whether this is a child or a local (or other) of that node.) + * @param inhs A map from attribute names to Lazys that define them. These Lazys will be supplied with 'parent' as their context for evaluation. + * @return A "decorated" form of this Node + */ + @Override + public final DecoratedNode decorate(final DecoratedNode parent, final Lazy[] inhs) { + return new DecoratedNode(getNumberOfChildren(), + getNumberOfInhAttrs(), + getNumberOfSynAttrs(), + getNumberOfLocalAttrs(), + this, parent, inhs, null); + } + + /** + * The way of decorating a forward node. + * (fwd only) + * + * @param parent The "true parent" of this node (same as the fwdParent's parent) + * @param fwdParent The DecoratedNode that forwards to the one we are about to create. We will pass inherited attribute access requests to this node. + * @return A "decorated" form of this Node + */ + public final DecoratedNode decorate(final DecoratedNode parent, final DecoratedNode fwdParent) { + return new DecoratedNode(getNumberOfChildren(), + getNumberOfInhAttrs(), + getNumberOfSynAttrs(), + getNumberOfLocalAttrs(), + this, parent, null, fwdParent); + } + + /** + * A convenience method unused by generate Silver code, but useful when working with + * the Silver runtime from Java. + * + * @return A node decorated with no inherited attributes, without a parent. + */ + public DecoratedNode decorate() { + return decorate(TopNode.singleton, (Lazy[])null); + } + + // These methods are to be provided by the *nonterminal* + + /** + * Used to create arrays of appropriate size in DecoratedNode. + * + * @return The number of synthesized attributes that occur on this nonterminal type. + */ + public abstract int getNumberOfSynAttrs(); + + /** + * Used for debugging, stack traces especially. + * + * @param index The index of the synthesized attribute to return the name of + * @return The name of the synthesized attribute at this index + */ + public abstract String getNameOfSynAttr(final int index); + + /** + * When a production does not forward and lacks an equation for a synthesized attribute, this is consulted instead. + * + * @param index Any synthesized attribute on this Node + * @return A Lazy to evaluate on a decorated form of this Node, to get the DEFAULT value of the attribute. + */ + public abstract Lazy getDefaultSynthesized(final int index); + + /** + * Used to create arrays of appropriate size in DecoratedNode. + * + * @return The number of inherited attributes that occur on this nonterminal type. + */ + public abstract int getNumberOfInhAttrs(); + + /** + * Used for debugging, stack traces especially. + * + * @param index The index of the inherited attribute to return the name of + * @return The name of the inherited attribute at this index + */ + public abstract String getNameOfInhAttr(final int index); + + /** + * Used for reflection. + * + * @return An array of the names of all annotations. + */ + public abstract String[] getAnnoNames(); + + /** + * Used for reflection. + * + * @param name The name of any annotation returned by getAnnoNames() + * @return The annotation object. (Thunk already evaluated) + */ + public abstract Object getAnno(final String name); + + + // These methods are to be provided by the *production* + + /** + * Used for debugging, and pattern matching. + * + * @return The full name of this Node. (e.g. "silver:definition:core:baseExpr") + */ + public abstract String getName(); + + /** + * Returns the number of children, same way array length does. + * + *

For example, a Node with 2 children allows you to request child 0 and 1. + * + * @return The number of children this Node has. + */ + public abstract int getNumberOfChildren(); + + /** + * Access a (raw) child of this Node. + * + * @param child A number in the range 0 - getNumberofChildren() + * @return The child object. (Thunk already evaluated) + * @see Node#getNumberOfChildren() + */ + public abstract Object getChild(final int child); + + /** + * @param child A number in the range 0 - getNumberofChildren() + * @return The child object, WITHOUT forcing the Thunk, if any. + */ + public abstract Object getChildLazy(final int child); + + /** + * @param key The child index to look up the inherited attributes. + * @return An array containing the inherited attributes supplied to that child + */ + public abstract Lazy[] getChildInheritedAttributes(final int index); + + /** + * Used to create arrays of appropriate size in DecoratedNode. + * + * @return The number of local and production attributes that occur on this production + */ + public abstract int getNumberOfLocalAttrs(); + + /** + * Used for debugging, stack traces especially. + * + * @param index The index of the local attribute to return the name of + * @return The name of the local attribute at this index + */ + public abstract String getNameOfLocalAttr(final int index); + + /** + * @param name The index of a local or production attribute on this Node + * @return A Lazy to evaluate on a decorated form of this Node, to get the value of the attribute + */ + public abstract Lazy getLocal(final int index); + + /** + * @param key The index for a local, to retrieve inherited attributes for. + * @return An array containing the inherited attributes supplied to that local + */ + public abstract Lazy[] getLocalInheritedAttributes(final int index); + + /** + * Reports whether or not this production forwards. + * + * @return true is {@link #evalForward} can be called, false if that immediately throws. + */ + public abstract boolean hasForward(); + + /** + * It may help conceptually to imagine this returns a Lazy that the DN then calls with itself as context. + * We've simply merged this into this method to avoid an unnecessary Lazy. + * + * @param context The DN of this node, to use to evaluate the forward equation. + * @return The Node that context forwards to. + */ + public abstract Node evalForward(final DecoratedNode context); + + /** + * Get any overridden attributes for this node's forward. (e.g. forwarding with { inh = foo; }) + * + * @param index The inherited attribute requested by a forwarded-to Node. + * @return A Lazy to evaluate on a decorated form of this Node, to get the value of this attribute provided to the forward. + */ + public abstract Lazy getForwardInheritedAttributes(final int index); + + /** + * @param index Any synthesized attribute on this Node + * @return A Lazy to evaluate on a decorated form of this Node, to get the value of the attribute + */ + public abstract Lazy getSynthesized(final int index); + + public abstract RTTIManager.Prodleton getProdleton(); +} diff --git a/runtime/java/src/common/NodeFactory.java b/runtime/java/src/common/NodeFactory.java index b0a046d10..578e63155 100644 --- a/runtime/java/src/common/NodeFactory.java +++ b/runtime/java/src/common/NodeFactory.java @@ -1,5 +1,8 @@ package common; +import silver.core.NOriginInfo; + + /** * NodeFactories are used when we take references to Silver functions. * @@ -13,11 +16,7 @@ public abstract class NodeFactory implements Typed { * Invoke a function or production. * @return The return value (or node constructed.) */ - public abstract T invoke(final Object[] args, final Object[] namedArgs); - - // Override with a more specific return type - @Override - public abstract FunctionTypeRep getType(); + public abstract T invoke(final common.OriginContext originCtx, final Object[] args, final Object[] namedArgs); // Below are just utilities diff --git a/runtime/java/src/common/OriginContext.java b/runtime/java/src/common/OriginContext.java new file mode 100644 index 000000000..af8ec76c1 --- /dev/null +++ b/runtime/java/src/common/OriginContext.java @@ -0,0 +1,118 @@ +package common; + +import silver.core.*; + +import java.util.*; + +import common.exceptions.*; + + +/** + * Implementation of "the stuff on the left of the turnstile" that needs to be + * passed along for origin tracking. This would be a value type if Java + * supported them. + * + * @author louisg + */ +public final class OriginContext { + public enum Variety { + NORMAL, MAINFUNCTION, FFI, PARSERACTION, GLOBAL + } + + public final Variety variety; + public final Node lhs; + public final NOriginNote[] rules; + + private OriginContext(final Variety variety, final Node lhs, final NOriginNote[] rules) { + this.variety = variety; + this.lhs = lhs; + this.rules = rules; + } + + public static final OriginContext ENTRY_CONTEXT = + new OriginContext(Variety.MAINFUNCTION, null, null); + + public static final OriginContext FFI_CONTEXT = + new OriginContext(Variety.FFI, null, null); + + public static final OriginContext PARSERACTION_CONTEXT = + new OriginContext(Variety.PARSERACTION, null, null); + + public static final OriginContext GLOBAL_CONTEXT = + new OriginContext(Variety.GLOBAL, null, null); + + public OriginContext(final Node lhs, final NOriginNote[] rules) { + this(Variety.NORMAL, lhs, rules); + } + + public OriginContext(final OriginContext old, final NOriginNote[] newRules) { + this(old.variety, old.lhs, mergeRulesArr(old.rules, newRules)); + } + + private static NOriginNote[] mergeRulesArr(final NOriginNote[] a, final NOriginNote[] b) { + if (a==null) return b; + if (b==null) return a; + + NOriginNote[] result = new NOriginNote[a.length + b.length]; + System.arraycopy(a, 0, result, 0, a.length); + System.arraycopy(b, 0, result, a.length, b.length); + return result; + } + + public ConsCell rulesAsSilverList() { + if (this.rules==null) return ConsCell.nil; + + ConsCell res = ConsCell.nil; + for(int i=this.rules.length-1; i!=-1; i--) { + res = new ConsCell(this.rules[i], res); + } + return res; + } + + public NOriginInfo makeNewConstructionOrigin(boolean isContractum) { + switch (this.variety) { + case NORMAL: + return new silver.core.PoriginOriginInfo(OriginsUtil.SET_AT_CONSTRUCTION_OIT, this.lhs, this.rulesAsSilverList(), isContractum); + + case MAINFUNCTION: + return new silver.core.PotherOriginInfo(OriginsUtil.SET_FROM_ENTRY_OIT, new common.StringCatter("Main Function"), this.rulesAsSilverList()); + + case FFI: + return new silver.core.PotherOriginInfo(OriginsUtil.SET_FROM_FFI_OIT, new common.StringCatter("Called from FFI"), this.rulesAsSilverList()); + + case PARSERACTION: + return new silver.core.PotherOriginInfo(OriginsUtil.SET_FROM_PARSER_ACTION_OIT, new common.StringCatter("Called inside a parser action block"), this.rulesAsSilverList()); + + case GLOBAL: + return new silver.core.PotherOriginInfo(OriginsUtil.SET_IN_GLOBAL_OIT, new common.StringCatter("Built in a global"), this.rulesAsSilverList()); + } + throw new RuntimeException("Impossible state: this.variety not recognized."); + } + + @SuppressWarnings("unchecked") + public T attrAccessCopy(final T arg) { + switch (this.variety) { + case NORMAL: //We only copy if this is a 'normal' origin (i.e. it originates from a node) + return (T)arg.copy(this.lhs, this.rulesAsSilverList()); + + default: + return arg; + } + } + + // Used by code that does some manipulation on a type-erased generic object that might be a nonterminal. + @SuppressWarnings("unchecked") + public T attrAccessCopyPoly(final T arg) { + if (arg instanceof Tracked) return (T) attrAccessCopy((Tracked)arg); + return arg; + } + + // Same as above but preserves laziness + @SuppressWarnings("unchecked") + public Object attrAccessCopyPolyThunk(final Object t) { + if (t instanceof Thunk) + return new Thunk(() -> attrAccessCopyPoly(((Thunk)t).eval())); + else + return attrAccessCopyPoly(t); + } +} \ No newline at end of file diff --git a/runtime/java/src/common/OriginsUtil.java b/runtime/java/src/common/OriginsUtil.java new file mode 100644 index 000000000..990ca3c66 --- /dev/null +++ b/runtime/java/src/common/OriginsUtil.java @@ -0,0 +1,146 @@ +package common; + + +import silver.core.*; +import common.exceptions.*; +import java.util.*; + +/** + * Implementation of helper functions on Nonterminals used by Origin-tracking code + * + * @author louisg + */ +public final class OriginsUtil { + + // Re-useable instances of the various OIT markers + public static PsetAtConstructionOIT SET_AT_CONSTRUCTION_OIT = new PsetAtConstructionOIT(); + public static PsetAtNewOIT SET_AT_NEW_OIT = new PsetAtNewOIT(); + public static PsetAtForwardingOIT SET_AT_FORWARDING_OIT = new PsetAtForwardingOIT(); + public static PsetAtAccessOIT SET_AT_ACCESS_OIT = new PsetAtAccessOIT(); + public static PsetFromParserOIT SET_FROM_PARSER_OIT = new PsetFromParserOIT(); + public static PsetFromParserActionOIT SET_FROM_PARSER_ACTION_OIT = new PsetFromParserActionOIT(); + public static PsetFromFFIOIT SET_FROM_FFI_OIT = new PsetFromFFIOIT(); + public static PsetFromReflectionOIT SET_FROM_REFLECTION_OIT = new PsetFromReflectionOIT(); + public static PsetFromReificationOIT SET_FROM_REIFICATION_OIT = new PsetFromReificationOIT(); + public static PsetFromEntryOIT SET_FROM_ENTRY_OIT = new PsetFromEntryOIT(); + public static PsetInGlobalOIT SET_IN_GLOBAL_OIT = new PsetInGlobalOIT(); + + // Sexperify code. Horrible ugly hack to serialize + spit out silver objects as python expressions + private static String ids(final Object arg){ + return Integer.toString(System.identityHashCode(arg)); + } + + private static String pySanitize(String arg) { + return arg.replace("\\","\\\\").replace("\"", "\\\"").replace("\n","\\n"); + } + + private static String sexprifyObj(List seen, Object arg) { + + if (arg instanceof DecoratedNode) arg = ((DecoratedNode)arg).undecorate(); + + String id = ids(arg); + if (seen.contains(id)) return "[" + id + "]"; + seen.add(id); + + String r = "[" + id + ", "; + if (arg instanceof Integer) + r += "'!Integer', " + arg.toString(); + else if (arg instanceof Float) + r += "'!Float', " + arg.toString(); + else if (arg instanceof Boolean) + r += "'!Boolean', " + arg.toString(); + else if (arg instanceof StringCatter) + r += "'!String', \""+pySanitize(arg.toString())+"\""; + else if (arg instanceof Terminal){ + Terminal t = (Terminal) arg; + r += "'!Terminal', '"+pySanitize(t.getName())+"', \""+pySanitize(t.lexeme.toString())+"\", "+sexprifyObj(seen, t.location); + } else if (arg instanceof Node) { + Node n = (Node) arg; + r += "'" + pySanitize(n.getName()) + "', ["; + for (int i=0; i seen = new ArrayList(); + return new StringCatter(sexprifyObj(seen, arg)); + } + + public static NOriginInfo getOriginOrNull(final Object arg) { + if (arg instanceof Tracked) return ((Tracked)arg).getOrigin(); + return null; + } + + // Implementation of the stdlib origins helpers + + public static silver.core.NMaybe polyGetOrigin(Object o) { + if (o instanceof DecoratedNode) o = ((DecoratedNode)o).undecorate(); + NOriginInfo r = getOriginOrNull(o); + if (r == null) return new silver.core.Pnothing(); + return new silver.core.Pjust(r); + } + + public static silver.core.NMaybe getOriginLink(silver.core.NOriginInfo o) { + if (o instanceof PoriginOriginInfo) + return new silver.core.Pjust(((PoriginOriginInfo)o).getChild_origin()); + + if (o instanceof PoriginAndRedexOriginInfo) + return new silver.core.Pjust(((PoriginAndRedexOriginInfo)o).getChild_origin()); + + return new silver.core.Pnothing(); + } + + // Misc helper + + public static ArrayList arrayListOfArray(T[] a) { + ArrayList l = new ArrayList(); + Collections.addAll(l, a); + return l; + } + + public static Object duplicatePoly(Object x, OriginContext c) { + if (x instanceof Tracked) return ((Tracked)x).duplicate(c); + else return x; + } +} diff --git a/runtime/java/src/common/PartialNameNodeFactory.java b/runtime/java/src/common/PartialNameNodeFactory.java index ba4c4158f..aa74cf0e5 100644 --- a/runtime/java/src/common/PartialNameNodeFactory.java +++ b/runtime/java/src/common/PartialNameNodeFactory.java @@ -2,6 +2,7 @@ import java.util.*; + /** * Here we are concerning ONLY with partially applying named arguments. * @@ -49,7 +50,7 @@ public PartialNameNodeFactory(final NodeFactory ref, } @Override - public T invoke(final Object[] restargs, final Object[] namedArgs) { + public T invoke(final common.OriginContext originCtx, final Object[] restargs, final Object[] namedArgs) { // STEP 1 : cut 'args' down to the true args we'll be supplying to 'ref' final int numConverted = iConvertedToOrdered.length; final int newArgsLength = restargs.length - numConverted; @@ -83,18 +84,27 @@ public T invoke(final Object[] restargs, final Object[] namedArgs) { } } - return ref.invoke(finalArgs, fullNamedArgs); + return ref.invoke(originCtx, finalArgs, fullNamedArgs); } @Override - public final FunctionTypeRep getType() { - FunctionTypeRep baseType = ref.getType(); + public final TypeRep getType() { + // Unpack the function type + List typeArgs = new LinkedList<>(); + TypeRep a = ref.getType(); + for (; a instanceof AppTypeRep; a = ((AppTypeRep)a).cons) { + typeArgs.add(0, ((AppTypeRep)a).arg); + } + FunctionTypeRep fnType = (FunctionTypeRep)a; + List params = typeArgs.subList(0, fnType.params); + List namedParamTypes = typeArgs.subList(fnType.params, fnType.params + fnType.namedParams.length); + TypeRep resultType = typeArgs.get(fnType.params + fnType.namedParams.length); - // Construct new parameter array by copying params and appending named parameters converted to ordered params - final TypeRep[] newParams = new TypeRep[baseType.params.length + iConvertedToOrdered.length]; - System.arraycopy(baseType.params, 0, newParams, 0, baseType.params.length); + // Take existing params and append named parameters converted to ordered params + List newArgs = new LinkedList<>(); + newArgs.addAll(params); for (int i = 0; i < iConvertedToOrdered.length; i++) { - newParams[baseType.params.length + i] = baseType.namedParamTypes[iConvertedToOrdered[i]]; + newArgs.add(namedParamTypes.get(iConvertedToOrdered[i])); } // Build a set of converted parameter indices @@ -103,22 +113,31 @@ public final FunctionTypeRep getType() { iConvertedToOrderedSet.add(i); } // Construct new named parameter arrays by copying items not supplied or converted - final String[] newNamedParamNames = new String[baseType.namedParamNames.length - (iConvertedToOrdered.length + iSuppliedHere.length)]; - final TypeRep[] newNamedParamTypes = new TypeRep[newNamedParamNames.length]; + final String[] newNamedParams = new String[fnType.namedParams.length - (iConvertedToOrdered.length + iSuppliedHere.length)]; + final List newNamedParamTypes = new ArrayList<>(newNamedParams.length); int i = 0, j = 0, k = 0; - while (k < newNamedParamNames.length) { + while (k < newNamedParams.length) { if (i < iSuppliedHere.length && i + j + k == iSuppliedHere[i]) { i++; } else if (iConvertedToOrderedSet.contains(j)) { j++; } else { - newNamedParamNames[k] = baseType.namedParamNames[i + j + k]; - newNamedParamTypes[k] = baseType.namedParamTypes[i + j + k]; + newNamedParams[k] = fnType.namedParams[i + j + k]; + newNamedParamTypes.set(k, namedParamTypes.get(i + j + k)); k++; } } - return new FunctionTypeRep(baseType.result, newParams, newNamedParamNames, newNamedParamTypes); + // Add the remaining named params and result to the type arg list + newArgs.addAll(newNamedParamTypes); + newArgs.add(resultType); + + // Re-pack the function type + TypeRep result = new FunctionTypeRep(fnType.params + iConvertedToOrdered.length, newNamedParams); + for (TypeRep arg : newArgs) { + result = new AppTypeRep(result, arg); + } + return result; } @Override diff --git a/runtime/java/src/common/PartialNodeFactory.java b/runtime/java/src/common/PartialNodeFactory.java index 234f097c5..b84c0c08f 100644 --- a/runtime/java/src/common/PartialNodeFactory.java +++ b/runtime/java/src/common/PartialNodeFactory.java @@ -1,5 +1,8 @@ package common; +import java.util.*; + + /** * Takes a NodeFactory, and transforms it into a NodeFactory with a few * argument positions filled in already. (i.e. partial function application) @@ -42,7 +45,7 @@ public PartialNodeFactory(final int[] indices, final Object[] args, final NodeFa } @Override - public T invoke(final Object[] restargs, final Object[] namedArgs) { + public T invoke(final common.OriginContext originCtx, final Object[] restargs, final Object[] namedArgs) { final int fullsize = args.length + restargs.length; final Object[] fullargs = new Object[fullsize]; @@ -57,25 +60,44 @@ public T invoke(final Object[] restargs, final Object[] namedArgs) { } } // We pass through namedArgs unchanged here. - return ref.invoke(fullargs, namedArgs); + return ref.invoke(originCtx, fullargs, namedArgs); } @Override - public final FunctionTypeRep getType() { - final FunctionTypeRep baseType = ref.getType(); - - final TypeRep[] newParams = new TypeRep[baseType.params.length - indices.length]; + public final TypeRep getType() { + // Unpack the function type + List typeArgs = new LinkedList<>(); + TypeRep a = ref.getType(); + for (; a instanceof AppTypeRep; a = ((AppTypeRep)a).cons) { + typeArgs.add(0, ((AppTypeRep)a).arg); + } + FunctionTypeRep fnType = (FunctionTypeRep)a; + List params = typeArgs.subList(0, fnType.params); + List namedParamTypes = typeArgs.subList(fnType.params, fnType.params + fnType.namedParams.length); + TypeRep resultType = typeArgs.get(fnType.params + fnType.namedParams.length); + + // Copy the unapplied named args + List newArgs = new LinkedList<>(); int i = 0, j = 0; - while (j < newParams.length) { + while (j < fnType.params - indices.length) { if (i < indices.length && indices[i] == i + j) { i++; } else { - newParams[j] = baseType.params[i + j]; + newArgs.add(params.get(i + j)); j++; } } - // We pass through namedParams unchanged here. - return new FunctionTypeRep(baseType.result, newParams, baseType.namedParamNames, baseType.namedParamTypes); + + // We pass through namedParams and result unchanged here. + newArgs.addAll(namedParamTypes); + newArgs.add(resultType); + + // Re-pack the function type + TypeRep result = new FunctionTypeRep(fnType.params - indices.length, fnType.namedParams); + for (TypeRep arg : newArgs) { + result = new AppTypeRep(result, arg); + } + return result; } @Override diff --git a/runtime/java/src/common/RTTIManager.java b/runtime/java/src/common/RTTIManager.java new file mode 100644 index 000000000..57240efb1 --- /dev/null +++ b/runtime/java/src/common/RTTIManager.java @@ -0,0 +1,118 @@ +package common; + +import java.util.*; + +import common.exceptions.SilverError; + +/** + * Collects maps between fully-qualified silver names (some:package:NT) and singletons for terminals, + * nonterminals, and productions. The singletons represent the terminal/nonterminal/production itself, + * not instances thereof. So functions on the singletons grant access to static fields/methods on the + * terminal/nonterminal/production. These are meant to be usable for all the same functions we + * previously did via java reflection. So a Terminalton is akin to a Class, a + * Nonterminalton to a Class, and Prodleton to a Class + * where .getNonterminalton() -> Nonterminalton. These are registered into these lookup + * tables at silver init time. + * + * This is used for the following: + * - silver reflection (and transitively, reflective de/serialization, rewriting stuff, etc) + * - decorators (read: implementing autocopy) + * - native de/serialization (see Reflection.java:nativeSerialize) + * + * @author louisg + */ +public final class RTTIManager { + private static final int estimateProductionCount = 4096; // guesstimates from the silver compiler + private static final int estimateNonterminalCount = 512; + private static final int estimateTerminalCount = 512; + + private static final Map> productionsByName = new HashMap>(estimateProductionCount, 0.5f); + private static final Map> nonterminalsByName = new HashMap>(estimateNonterminalCount, 0.5f); + private static final Map> terminalsByName = new HashMap>(estimateTerminalCount, 0.5f); + + + public static void registerNonterminal(Nonterminalton nton) { + nonterminalsByName.put(nton.getName(), nton); + } + + public static Nonterminalton getNonterminalton(String name) { + return nonterminalsByName.get(name); + } + + + public static void registerProduction(Prodleton pton) { + productionsByName.put(pton.getName(), pton); + } + + public static Prodleton getProdleton(String name) { + return productionsByName.get(name); + } + + + public static void registerTerminal(Terminalton tton) { + terminalsByName.put(tton.getName(), tton); + } + + public static Terminalton getTerminalton(String name) { + return terminalsByName.get(name); + } + + + public static void registerOccurs(String nt, String attr, int index) { + getNonterminalton(nt).occursIndices.put(attr, index); + } + + + // Represents a production (not an instance of the production, but the production itself.) + // So implements reify and access to static fields (childtypes, childinheritedattributes) on + // the nonterminal. + // T = the production type (PSomething) + public static abstract class Prodleton { + public abstract T reify( + final silver.core.NAST origAST, + final common.ConsCell rules, + final common.TypeRep resultType, + final silver.core.NAST[] childASTs, + final String[] annotationNames, + final silver.core.NAST[] annotationASTs); + + public abstract T constructDirect( + final Object[] children, + final Object[] annos); // NO reify or other checking, used by native[De]Serialize + + public abstract String getName(); + public abstract Nonterminalton getNonterminalton(); + + public abstract String getTypeUnparse(); // Nominally opaque representation of the type + // for making sure that something nativeSerialized + // is still valid. + public abstract int getChildCount(); + public abstract int getAnnoCount(); + + public abstract String[] getChildTypes(); + public abstract Lazy[][] getChildInheritedAttributes(); + } + + // Represents a terminal (not an instance of the terminal, but the terminal itself.) + // T = the terminal type (TSomething) + public static abstract class Terminalton { + public abstract T construct(final StringCatter lexeme, final silver.core.NLocation location); + public abstract String getName(); + } + + // Represents a nonterminal (not a production or instance, but the nonterminal type itself.) + // T = the nonterminal type (NSomething) + public static abstract class Nonterminalton { + public abstract String[] getOccursInh(); + public abstract String getName(); + + private final Map occursIndices = new HashMap(16, 0.5f); + public int getOccursIndex(String attrName) { + if (!occursIndices.containsKey(attrName)) { + throw new SilverError("Attribute " + attrName + " does not occur on " + getName() + "."); + } + return occursIndices.get(attrName); + } + } + +} diff --git a/runtime/java/src/common/RandomGen.java b/runtime/java/src/common/RandomGen.java new file mode 100644 index 000000000..2513f1b56 --- /dev/null +++ b/runtime/java/src/common/RandomGen.java @@ -0,0 +1,60 @@ +package common; + +import java.util.*; +import java.util.function.Function; + +import common.exceptions.*; +import silver.core.*; + +public final class RandomGen { + public static Object runRandomGen(final OriginContext ctx, final int seed, final NRandomGen r) { + return runRandomGen(ctx, new Random(seed), r); + } + + public static Object runRandomGen(final OriginContext ctx, final NRandomGen r) { + return runRandomGen(ctx, new Random(), r); + } + + // "Interpret" a RandomGen computation using the specified Random generator + @SuppressWarnings("unchecked") + public static Object runRandomGen(final OriginContext ctx, final Random rng, final NRandomGen r) { + if (r instanceof PmapRandomGen) { + final Object val = runRandomGen(ctx, rng, (NRandomGen)r.getChild(1)); + return ((NodeFactory)r.getChild(0)).invoke(ctx, new Object[] {val}, null); + } else if (r instanceof PapRandomGen) { + final NodeFactory fn = (NodeFactory)runRandomGen(ctx, rng, (NRandomGen)r.getChild(0)); + final Object val = runRandomGen(ctx, rng, (NRandomGen)r.getChild(1)); + return fn.invoke(ctx, new Object[] {val}, null); + } else if (r instanceof PpureRandomGen) { + return r.getChild(0); + } else if (r instanceof PbindRandomGen) { + final Object val = runRandomGen(ctx, rng, (NRandomGen)r.getChild(0)); + return runRandomGen(ctx, rng, ((NodeFactory)r.getChild(1)).invoke(ctx, new Object[] {val}, null)); + } else if (r instanceof PrandomInteger) { + return rng.nextInt(); + } else if (r instanceof PrandomRangeInteger) { + return randomRangeInteger((Integer)r.getChild(0), (Integer)r.getChild(1), rng); + } else if (r instanceof PrandomFloat) { + return rng.nextFloat(); + } else if (r instanceof PrandomBoolean) { + return rng.nextBoolean(); + } else if (r instanceof PrandomToken_) { + return rng.nextLong(); + } else { + throw new SilverError("Unsupported RandomGen constructor: " + r.getName()); // Not SilverInternalError, someone could define a new RandomGen production + } + } + + public static int randomRangeInteger(final int lower, final int upper, final Random rng) { + if (lower > upper) { + throw new SilverError("Empty Integer range [" + lower + ", " + upper + "]"); + } + return rng.nextInt(upper - lower + 1) + lower; // TODO: I think this could be more robust against signed overflow. + } + + public static Ppair evalRandomTokenOp(final long token, Function op) { + final Random rng = new Random(token); + final Object result = op.apply(rng); + return new Ppair(result, rng.nextLong()); + } +} diff --git a/runtime/java/src/common/Reflection.java b/runtime/java/src/common/Reflection.java index 9a35be7bc..3b4fbdcd6 100644 --- a/runtime/java/src/common/Reflection.java +++ b/runtime/java/src/common/Reflection.java @@ -1,11 +1,10 @@ package common; -import java.lang.reflect.*; import java.util.*; import common.exceptions.*; -import core.*; -import core.reflect.*; +import silver.core.*; +import java.io.*; /** * Implementation of the Silver reflection library @@ -31,7 +30,8 @@ public static TypeRep getType(final Object o) { } else if(o instanceof Thunk) { throw new SilverInternalError("Runtime type of an unevaluated Thunk should never be demanded."); } else { - // Not an internal error, since foreign types not implementing Typed will trigger this + // Not an internal error, since foreign types not implementing Typed will trigger this, + // but should only be possible with applyAST. throw new SilverError("Runtime type checking of object requires class " + o.getClass().getName() + " to implement Typed."); } } @@ -64,47 +64,49 @@ public static NMaybe reflectTypeName(final Object o) { /** * Implementation of the reflect operation for an arbitrary type. * + * @param rules origins context of the invocation * @param o The object to reflect. * @return The reflected AST. */ - public static NAST reflect(final Object o) { + public static NAST reflect(final ConsCell rules, Object o) { + silver.core.NOriginInfo origin = (rules!=null)?new silver.core.PoriginOriginInfo(OriginsUtil.SET_FROM_REFLECTION_OIT, o, rules, true):null; if(o instanceof Node) { Node n = (Node)o; - NASTs children = new PnilAST(); + NASTs children = new PnilAST(origin); for (int i = n.getNumberOfChildren() - 1; i >= 0; i--) { - Object value = reflect(n.getChild(i)); - children = new PconsAST(value, children); + Object value = reflect(rules, n.getChild(i)); + children = new PconsAST(origin, value, children); } String[] annotationNames = n.getAnnoNames(); - NNamedASTs annotations = new PnilNamedAST(); + NNamedASTs annotations = new PnilNamedAST(origin); for (int i = annotationNames.length - 1; i >= 0; i--) { String name = annotationNames[i]; - Object value = reflect(n.getAnno(name)); - annotations = new PconsNamedAST(new PnamedAST(new StringCatter(name), value), annotations); + Object value = reflect(rules, n.getAnno(name)); + annotations = new PconsNamedAST(origin, new PnamedAST(origin, new StringCatter(name), value), annotations); } - return new PnonterminalAST(new StringCatter(n.getName()), children, annotations); + return new PnonterminalAST(origin, new StringCatter(n.getName()), children, annotations); } else if(o instanceof Terminal) { Terminal t = (Terminal)o; - return new PterminalAST(new StringCatter(t.getName()), t.lexeme, t.location); + return new PterminalAST(origin, new StringCatter(t.getName()), t.lexeme, t.location); } else if(o instanceof ConsCell) { - return new PlistAST(reflectList((ConsCell)o)); + return new PlistAST(origin, reflectList(rules, origin, (ConsCell)o)); } else if(o instanceof StringCatter) { - return new PstringAST((StringCatter)o); + return new PstringAST(origin, (StringCatter)o); } else if(o instanceof Integer) { - return new PintegerAST((Integer)o); + return new PintegerAST(origin, (Integer)o); } else if(o instanceof Float) { - return new PfloatAST((Float)o); + return new PfloatAST(origin, (Float)o); } else if(o instanceof Boolean) { - return new PbooleanAST((Boolean)o); + return new PbooleanAST(origin, (Boolean)o); } else { - return new PanyAST(o); + return new PanyAST(origin, o); } } - private static NASTs reflectList(final ConsCell l) { + private static NASTs reflectList(ConsCell rules, silver.core.NOriginInfo origin, final ConsCell l) { if (!l.nil()) { - return new PconsAST(reflect(l.head()), reflectList(l.tail())); + return new PconsAST(origin, reflect(rules, l.head()), reflectList(rules, origin, l.tail())); } else { - return new PnilAST(); + return new PnilAST(origin); } } @@ -115,9 +117,9 @@ private static NASTs reflectList(final ConsCell l) { * @param ast The AST to reify. * @return An Either object containing either an error message or a constructed object. */ - public static NEither reifyChecked(final TypeRep resultType, final NAST ast) { + public static NEither reifyChecked(final ConsCell rules, final TypeRep resultType, final NAST ast) { try { - return new Pright(reify(resultType, ast)); + return new Pright(reify(rules, resultType, ast)); } catch (SilverException e) { Throwable rootCause = SilverException.getRootCause(e); if (rootCause instanceof SilverError) { @@ -131,11 +133,12 @@ public static NEither reifyChecked(final TypeRep resultType, final NAST ast) { /** * Implementation of the reify operation for an arbitrary type. * + * @param rules origins context of the invocation of reify * @param resultType The type of tree to be constructed. * @param ast The AST to reify. * @return The constructed object. */ - public static Object reify(final TypeRep resultType, final NAST ast) { + public static Object reify(final ConsCell rules, final TypeRep resultType, final NAST ast) { if (ast instanceof PnonterminalAST) { // Unpack production name final String prodName = ((StringCatter)ast.getChild(0)).toString(); @@ -161,6 +164,7 @@ public int compareTo(AnnotationEntry other) { return name.compareTo(other.name); } } + final List annotationASTList = new ArrayList<>(); for (NNamedASTs current = (NNamedASTs)ast.getChild(2); !(current instanceof PnilNamedAST); current = (NNamedASTs)current.getChild(1)) { NNamedAST item = (NNamedAST)current.getChild(0); @@ -173,26 +177,14 @@ public int compareTo(AnnotationEntry other) { annotationNames[i] = annotationASTList.get(i).name; annotationASTs[i] = annotationASTList.get(i).ast; } - - // Invoke the reify function for the appropriate production class - final String[] path = prodName.split(":"); - path[path.length - 1] = "P" + path[path.length - 1]; - final String className = String.join(".", path); - try { - Method prodReify = - ((Class)Class.forName(className)).getMethod("reify", TypeRep.class, NAST[].class, String[].class, NAST[].class); - return prodReify.invoke(null, resultType, childASTs, annotationNames, annotationASTs); - } catch (ClassNotFoundException e) { + + RTTIManager.Prodleton pton = RTTIManager.getProdleton(prodName); + if (pton==null) { throw new SilverError("Undefined production " + prodName); - } catch (NoSuchMethodException | IllegalAccessException e) { - throw new SilverInternalError("Error invoking reify for class " + className, e); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof SilverException) { - throw (SilverException)e.getTargetException(); - } else { - throw new SilverInternalError("Error invoking reify for class " + className, e.getTargetException()); - } } + + return pton.reify(ast, rules, resultType, childASTs, annotationNames, annotationASTs); + } else if (ast instanceof PterminalAST) { // Unpack components final String terminalName = ((StringCatter)ast.getChild(0)).toString(); @@ -203,32 +195,20 @@ public int compareTo(AnnotationEntry other) { if (!TypeRep.unify(resultType, new BaseTypeRep(terminalName))) { throw new SilverError("reify is constructing " + resultType.toString() + ", but found terminal " + terminalName + " AST."); } - - // Invoke the reify function for the appropriate terminal class - final String[] path = terminalName.split(":"); - path[path.length - 1] = "T" + path[path.length - 1]; - final String className = String.join(".", path); - try { - Constructor terminalConstructor = - ((Class)Class.forName(className)).getConstructor(StringCatter.class, NLocation.class); - return terminalConstructor.newInstance(lexeme, location); - } catch (ClassNotFoundException e) { + + RTTIManager.Terminalton tton = RTTIManager.getTerminalton(terminalName); + if (tton==null) { throw new SilverError("Undefined terminal " + terminalName); - } catch (NoSuchMethodException | InstantiationException | IllegalAccessException e) { - throw new SilverInternalError("Error constructing class " + className, e); - } catch (InvocationTargetException e) { - if (e.getTargetException() instanceof SilverException) { - throw (SilverException)e.getTargetException(); - } else { - throw new SilverInternalError("Error constructing class " + className, e.getTargetException()); - } } + + return tton.construct(lexeme, location); + } else if (ast instanceof PlistAST) { final TypeRep paramType = new VarTypeRep(); - if (!TypeRep.unify(resultType, new ListTypeRep(paramType))) { + if (!TypeRep.unify(resultType, new AppTypeRep(new BaseTypeRep("[]"), paramType))) { throw new SilverError("reify is constructing " + resultType.toString() + ", but found list AST."); } - return reifyList(paramType, (NASTs)ast.getChild(0)); + return reifyList(rules, paramType, (NASTs)ast.getChild(0)); } else { Object givenObject = ast.getChild(0); @@ -255,17 +235,17 @@ public int compareTo(AnnotationEntry other) { } } // Recursive helper to walk the ASTs tree and build a list - private static ConsCell reifyList(final TypeRep resultParamType, final NASTs asts) { + private static ConsCell reifyList(final ConsCell rules, final TypeRep resultParamType, final NASTs asts) { if (asts instanceof PconsAST) { Object head; try { - head = reify(resultParamType, (NAST)asts.getChild(0)); + head = reify(rules, resultParamType, (NAST)asts.getChild(0)); } catch (SilverException e) { throw new ConsReifyTraceException(true, e); } ConsCell tail; try { - tail = reifyList(resultParamType, (NASTs)asts.getChild(1)); + tail = reifyList(rules, resultParamType, (NASTs)asts.getChild(1)); } catch (SilverException e) { throw new ConsReifyTraceException(false, e); } @@ -285,27 +265,39 @@ private static ConsCell reifyList(final TypeRep resultParamType, final NASTs ast * @param namedArgs A list of Pair> named arguments or holes for partial application. * @return An Either object containing either an error message or the reflected result of calling the function. */ - public static NEither applyAST(final NAST fn, final ConsCell args, final ConsCell namedArgs) { + public static NEither applyAST(final OriginContext ctx, final NAST fn, final ConsCell args, final ConsCell namedArgs) { // Unpack function if (!(fn instanceof PanyAST) || !(fn.getChild(0) instanceof NodeFactory)) { return new Pleft(new StringCatter("Expected a function AST")); } NodeFactory givenFn = (NodeFactory)(fn.getChild(0)); - FunctionTypeRep fnType = givenFn.getType(); + + // Unpack the function type + List typeArgs = new LinkedList<>(); + TypeRep a = givenFn.getType(); + for (; a instanceof AppTypeRep; a = ((AppTypeRep)a).cons) { + typeArgs.add(0, ((AppTypeRep)a).arg); + } + FunctionTypeRep fnType = (FunctionTypeRep)a; + List params = typeArgs.subList(0, fnType.params); + List namedParamTypes = typeArgs.subList(fnType.params, fnType.params + fnType.namedParams.length); + TypeRep resultType = typeArgs.get(fnType.params + fnType.namedParams.length); + + final ConsCell rules = ctx.rulesAsSilverList(); // Unpack args final List argIndexList = new ArrayList<>(5); final List argList = new ArrayList<>(5); int i = 0; for (ConsCell current = args; !current.nil(); current = current.tail()) { - if (i >= fnType.params.length) { - return new Pleft(new StringCatter("Expected only " + fnType.params.length + " arguments, but got " + args.length())); + if (i >= fnType.params) { + return new Pleft(new StringCatter("Expected only " + fnType.params + " arguments, but got " + args.length())); } final NMaybe item = (NMaybe)current.head(); if (item instanceof Pjust) { argIndexList.add(i); try { - argList.add(reify(fnType.params[i], (NAST)item.getChild(0))); + argList.add(reify(rules, params.get(i), (NAST)item.getChild(0))); } catch (SilverException e) { Throwable rootCause = SilverException.getRootCause(e); if (rootCause instanceof SilverError) { @@ -317,19 +309,19 @@ public static NEither applyAST(final NAST fn, final ConsCell args, final ConsCel } i++; } - if (i < fnType.params.length) { - return new Pleft(new StringCatter("Expected " + fnType.params.length + " arguments, but got only " + i)); + if (i < fnType.params) { + return new Pleft(new StringCatter("Expected " + fnType.params + " arguments, but got only " + i)); } // Unpack named args final List convertedIndexList = new ArrayList<>(); final List suppliedIndexList = new ArrayList<>(); final List namedArgList = new ArrayList<>(); - final Object[] reorderedNamedArgs = new Object[fnType.namedParamNames.length]; + final Object[] reorderedNamedArgs = new Object[fnType.namedParams.length]; for (ConsCell current = namedArgs; !current.nil(); current = current.tail()) { final NPair entry = (NPair)current.head(); final String name = entry.getChild(0).toString(); - int index = Arrays.asList(fnType.namedParamNames).indexOf(name); + int index = Arrays.asList(fnType.namedParams).indexOf(name); if (index == -1) { return new Pleft(new StringCatter("Unexpected named argument " + name)); } @@ -337,7 +329,7 @@ public static NEither applyAST(final NAST fn, final ConsCell args, final ConsCel if (item instanceof Pjust) { Object o; try { - o = reify(fnType.namedParamTypes[index], (NAST)item.getChild(0)); + o = reify(rules, namedParamTypes.get(index), (NAST)item.getChild(0)); } catch (SilverException e) { Throwable rootCause = SilverException.getRootCause(e); if (rootCause instanceof SilverError) { @@ -356,7 +348,7 @@ public static NEither applyAST(final NAST fn, final ConsCell args, final ConsCel } Object result; - if (argList.size() < fnType.params.length || namedArgList.size() < fnType.namedParamNames.length) { + if (argList.size() < fnType.params || namedArgList.size() < fnType.namedParams.length) { // Apply partial result = givenFn .invokePartial(argIndexList.stream().mapToInt(n -> n).toArray(), argList.toArray()) @@ -366,8 +358,318 @@ public static NEither applyAST(final NAST fn, final ConsCell args, final ConsCel namedArgList.toArray()); } else { // Apply regular - result = givenFn.invoke(argList.toArray(), reorderedNamedArgs); + result = givenFn.invoke(ctx, argList.toArray(), reorderedNamedArgs); + } + return new Pright(reflect(rules, result)); + } + + + //////////////////////////////////////////////////////////////////////////////////////////////// + // This is the machinery for nativeSerialize/nativeDeserialize. It is a bespoke binary + // format written/read via java's DataOutputStream/DataInputStream machinery. + // + // This is an OPAQUE serialization format, and must not be modified. Type-safety + // of nativeDeserialize (silver type-safety) depends on the stored value being + // well-formed (i.e., produced by nativeSerialize from a well-typed silver value.) + // + // We use the writeUTF/readUTF helpers from java to write and read strings. This internally + // is length+data, and allows us to treat strings as units. + // + // Files start with a header to make sure we don't try to interpret garbage. Then there + // is a lookup table of production names, which are indexed into by serialized production + // values. This means that names are only looked up as strings once in the prodleton dictionary. + // This table also contains opaque string representations of the types of the productions, which are + // compared to the string type reps of productions in the deserializing silver (stored on + // prodletons.) This prevents type confusion if the definition of a serialized nonterminal is + // changed. + // + // Serialized values start with a one-byte type tag, then some body. Primitives are stored + // directly. Lists have a special-case serialization (length+items). Terminals are stored + // with their full name, looked up at deserialization. [If we started serializing lots of CSTs + // we could do the same lookup table thing we do with productions to speed this up. We could also + // make that faster by special-casing storing the Location value (currently it is stored as a + // 'normal' nonterminal value.) But we don't really ever serialize terminals so it dosen't matter + // much.] Productions store the index of the entry in the production table, and then the children + // and annotations. Deserialization machinery must interrogate the prodleton to know how many + // children and annos to read (we are guaranteed it has not changed by the type rep check when + // loading the prod table.) Nonterminals are then constructed via constructDirect with NO + // typechecking on erased values (so it is important that this is an opaque format, since then + // the type-safety is guaranteed by it's having been produced from a valid silver value.) + // Annotations are in whatever arbitrary order they are in in the translation, and the opaque + // type identifier prevents this from violating type safety. If we ever use more than `location`, + // revisit this. + // + // One final reify is invoked to make sure that the types match for polymorphic serialized types. + // No typedata is stored, so this is reifying the expected type with the getType of the deserialized + // type. + // + // Decorated nodes are not supported. Foreign types are not supported. + // + // This is a more fragile format w/r/t changes in the silver program saving/loading it than the + // textual one (e.g. reordering annotations, changing children names, will cause a deserialization + // failure) and should only be used for caching. But it is MUCH faster and about 50% more + // space-efficient than the textual one. + // + // File: SVB\0<\n><1b version (0)> + // item: <0> - String + // <1><4b integer> - Integer + // <2> - false + // <3> - true + // <4><4b float> - float + // <5> - production (children, annos = items) + // <6> - terminal (location = item) + // <7><2b length> - list (data = items) + // <8><4b length> - long list for dawn :) -- separate for backwards compat because it was easy + // + // index array: <2b nt count> + // ntrec: + // + // strings, ints, etc are writeUTF/writeInt/writeShort format (so integers are big-endian), see + // https://docs.oracle.com/javase/7/docs/api/java/io/DataOutputStream.html#writeUTF(java.lang.String) + // for details on writeUTF (basically a 2b length then UTF-8). + // + // @author louisg + + private static class NativeSerializationException extends IOException { + public NativeSerializationException(String x) { super(x); } + } + + // Serialize x into a bytearray + // x::a -> Either + public static NEither nativeSerialize(Object x) { + try{ + ByteArrayOutputStream arr = new ByteArrayOutputStream(10_000_000); + DataOutputStream o = new DataOutputStream(arr); + + o.writeBytes("SVB\0\n\0"); // Header + + ArrayList> prodset = new ArrayList>(); + + nSerGetProdSet(prodset, x); // Compute set of productions + + if (prodset.size() > 1<<15) throw new NativeSerializationException("Too many productions for native serialize"); + o.writeShort(prodset.size()); // Write lookup table size + + for (RTTIManager.Prodleton p : prodset) { // Write lookup table. For each prod: + o.writeUTF(p.getName()); // fully qualified silver prod name + o.writeUTF(p.getTypeUnparse()); // opaque typerep + } + + nSerItem(o, prodset, x); // Serialize the value + + return new Pright(arr.toByteArray()); + } catch (NativeSerializationException e) { + return new Pleft(new StringCatter("Native serialize failed: " + e.toString())); + } catch (Exception e) { + String m = "Native serialize failed: Unknown error: " + e.toString(); + System.err.println(m); + return new Pleft(new StringCatter(m)); + } + } + + // Recursively compute the set of productions used in the object, adding their prodletons once to `s`. + // This forms the production lookup table. + public static void nSerGetProdSet(ArrayList> s, Object x) { + if(x instanceof Node) { + Node n = (Node)x; + if (s.indexOf(n.getProdleton()) == -1) s.add(n.getProdleton()); + + for (int i = 0; i < n.getNumberOfChildren(); i++) { + nSerGetProdSet(s, n.getChild(i)); + } + + String[] annotationNames = n.getAnnoNames(); + + for (int i = 0; i < annotationNames.length; i++) { + String name = annotationNames[i]; + nSerGetProdSet(s, n.getAnno(name)); + } + } else if (x instanceof Terminal) { + nSerGetProdSet(s, ((Terminal)x).location); + } else if (x instanceof ConsCell) { + ConsCell c = (ConsCell) x; + while (c != ConsCell.nil) { + nSerGetProdSet(s, c.head()); + c = c.tail(); + } + } + } + + + // Serialize a single value `x` into `o`, using `s` as production lookup table + public static void nSerItem(DataOutputStream o, ArrayList> s, Object x) throws IOException { + if(x instanceof Node) { + Node n = (Node)x; + + o.writeByte(5); // type tag + o.writeShort(s.indexOf(n.getProdleton())); // index of this production in prod lookup table + + String[] annotationNames = n.getAnnoNames(); + + for (int i = 0; i < n.getNumberOfChildren(); i++) { + nSerItem(o, s, n.getChild(i)); // serialize all children + } + + for (int i = 0; i < annotationNames.length; i++) { + String name = annotationNames[i]; + nSerItem(o, s, n.getAnno(name)); // serialize all annos + } + } else if(x instanceof Terminal) { + Terminal t = (Terminal)x; + + o.writeByte(6); // type tag + o.writeUTF(t.getTerminalton().getName()); // store name of terminal + o.writeUTF(t.lexeme.toString()); // store lexeme + nSerItem(o, s, t.location); // store location (which is a NT, so recurse to store it as an item) + + } else if(x instanceof ConsCell) { + ConsCell c = (ConsCell)x; + + int listLen = c.length(); + + if (listLen > 1<<30 || listLen < 0) throw new NativeSerializationException("List too long for native serialize"); + + if (listLen > 1<<15) { + o.writeByte(8); // type tag for 32-bit-count list + o.writeInt(listLen); // store length of list + } else { + o.writeByte(7); // type tag for 16-bit-count (original) list + o.writeShort(listLen); // store length of list + } + + while (c!=ConsCell.nil) { + nSerItem(o, s, c.head()); // store items of list ("forward") + c = (ConsCell)c.tail(); + } + } else if(x instanceof StringCatter) { + o.writeByte(0); // type tag + o.writeUTF(((StringCatter)x).toString()); // string value + } else if(x instanceof Integer) { + o.writeByte(1); // type tag + o.writeInt((int)x); // integer value + } else if(x instanceof Float) { + o.writeByte(4); // type tag + o.writeFloat((float)x); // float value + } else if(x instanceof Boolean) { + if ((boolean)x) o.writeByte(3); // type tag/value for true + else o.writeByte(2); // type tag/value for false + } else if(x instanceof DecoratedNode) { + throw new NativeSerializationException("Cannot serialize DecoratedNodes (prod " + x.toString() + ")"); + } else { + throw new NativeSerializationException("Unserializable type encountered: " + x.toString()); + } + } + + // Deserialize `arr` into a silver object + // RuntimeTypable x => ByteArray -> Either + public static NEither nativeDeserialize(final TypeRep expected, final byte[] arr) { + try{ + ByteArrayInputStream ins = new ByteArrayInputStream(arr); + DataInputStream i = new DataInputStream(ins); + + byte header[] = "SVB\0\n\0".getBytes("ASCII"); + byte[] buf = new byte[6]; + i.readFully(buf, 0, 6); // Read header + + if (!Arrays.equals(header, buf)) throw new NativeSerializationException("Not a SVB serialization"); + + int prodCount = i.readShort(); // length of prod lookup table + + ArrayList> lookup = new ArrayList>(prodCount); + for (int c = 0; c < prodCount; c++) { + String name = i.readUTF(); // Read name of prod + String opaqueTypeIdentifier = i.readUTF(); // Read opaque type identifier + + RTTIManager.Prodleton pton = RTTIManager.getProdleton(name); + if (pton == null) throw new NativeSerializationException("Unknown production: " + name); + if (!pton.getTypeUnparse().equals(opaqueTypeIdentifier)) + throw new NativeSerializationException("Production " + name + " changed type since serialization (from `" + opaqueTypeIdentifier + "` to `" + pton.getTypeUnparse() + "`)"); + + lookup.add(pton); + } + + Object v = nDeserItem(lookup, i); // Deserialize the stored item + + if (!TypeRep.unify(expected, getType(v))) { // Run a unification check to ensure silver type-safety + return new Pleft(new StringCatter("nativeDeserialize is constructing " + expected.toString() + ", but found " + getType(v).toString())); + } + + return new Pright(v); + } catch (NativeSerializationException e) { + return new Pleft(new StringCatter("Native deserialize failed: " + e.toString())); + } catch (IOException e) { + String m = "Native deserialize failed: Unknown Error: " + e.toString(); + System.err.println(m); + return new Pleft(new StringCatter(m)); + } + } + + // Deserialize a single item from stream `i` using prod lookup table `s` + public static Object nDeserItem(ArrayList> s, DataInputStream i) throws IOException { + int typeId = i.readByte(); // read type-tag + + if (typeId == 5) { // production + + int orderedIndex = i.readShort(); // read index and retrieve prodleton from lookup + RTTIManager.Prodleton pton = s.get(orderedIndex); + + int childCount = pton.getChildCount(); // get number of children to load + Object children[] = new Object[childCount]; + + for (int n = 0; n < childCount; n++) { + children[n] = nDeserItem(s, i); // ...and load them + } + + int annoCount = pton.getAnnoCount(); // get number of annos to load + Object annos[] = new Object[annoCount]; + + for (int n = 0; n < annoCount; n++) { + annos[n] = nDeserItem(s, i); // ...and load them + } + + return pton.constructDirect(children, annos); // construct value + } else if (typeId == 6) { // terminal + String name = i.readUTF(); // read the terminal name + RTTIManager.Terminalton tton = RTTIManager.getTerminalton(name); + + if (tton == null) + throw new NativeSerializationException("Can't find terminal " + name); + + String lexeme = i.readUTF(); + Object location = nDeserItem(s, i); // deserialize the location as an item + + return tton.construct(new StringCatter(lexeme), (NLocation)location); + } else if (typeId == 7 || typeId == 8) { // list + int length; + + if (typeId == 7) length = i.readShort(); // 16-bit-count (original) list + else length = i.readInt(); // 32-bit-count list + + Object values[] = new Object[length]; + + for (int c=0; c=0; c--) { + l = new ConsCell(values[c], l); // traverse "backwards" to construct cons-list + } + + return l; + } else if (typeId == 0) { // string primitive + return new StringCatter(i.readUTF()); + } else if (typeId == 1) { // int primitive + return i.readInt(); + } else if (typeId == 4) { // float primitive + return i.readFloat(); + } else if (typeId == 3) { // bool primitive + return true; + } else if (typeId == 2) { // bool primitive + return false; + } else { + throw new NativeSerializationException("Unknown type id (" + Integer.toString(typeId) + ")"); } - return new Pright(reflect(result)); } } diff --git a/runtime/java/src/common/SilverCopperParser.java b/runtime/java/src/common/SilverCopperParser.java new file mode 100644 index 000000000..161019ac4 --- /dev/null +++ b/runtime/java/src/common/SilverCopperParser.java @@ -0,0 +1,23 @@ +package common; + +import java.util.List; +import edu.umn.cs.melt.copper.runtime.engines.CopperParser; +import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; + +/** + * Interface for Copper parser classes generated from Silver, + * having a getTokens method for accessing the parsed terminals. + * + * @author krame505 + */ +public interface SilverCopperParser extends CopperParser { + /** + * Get the list of terminals that were parsed in the most recent invocation + * of this parser. + * + * @return The parsed terminals, or null if this parser has not been run. + */ + List getTokens(); + + void setTabStop(int tabStop); +} diff --git a/runtime/java/src/common/Terminal.java b/runtime/java/src/common/Terminal.java index 750b7f2bf..369491ec2 100644 --- a/runtime/java/src/common/Terminal.java +++ b/runtime/java/src/common/Terminal.java @@ -1,9 +1,9 @@ package common; import edu.umn.cs.melt.copper.runtime.engines.semantics.VirtualLocation; -import core.NLocation; -import core.Ploc; -import core.Alocation; +import silver.core.NLocation; +import silver.core.Ploc; +import silver.core.Alocation; /** * The terminal representation object, containing a lexeme and a location. @@ -22,8 +22,7 @@ public Terminal(final String lexeme, final VirtualLocation vl, final int index, final int line = vl.getLine(); final int column = vl.getColumn(); vl.defaultUpdate(lexeme); - this.location = new Ploc( - new StringCatter(vl.getFileName()), + this.location = new Ploc(new StringCatter(vl.getFileName()), line, column, vl.getLine(), @@ -47,19 +46,25 @@ private Object getFromLoc(int syn) { return d.synthesized(syn); } public Integer getLine() { - return (Integer)getFromLoc(core.Init.core_line__ON__core_Location); + return (Integer)getFromLoc(silver.core.Init.silver_core_line__ON__silver_core_Location); + } + public Integer getEndLine() { + return (Integer)getFromLoc(silver.core.Init.silver_core_endLine__ON__silver_core_Location); } public Integer getColumn() { - return (Integer)getFromLoc(core.Init.core_column__ON__core_Location); + return (Integer)getFromLoc(silver.core.Init.silver_core_column__ON__silver_core_Location); + } + public Integer getEndColumn() { + return (Integer)getFromLoc(silver.core.Init.silver_core_endColumn__ON__silver_core_Location); } public StringCatter getFilename() { - return (StringCatter)getFromLoc(core.Init.core_filename__ON__core_Location); + return (StringCatter)getFromLoc(silver.core.Init.silver_core_filename__ON__silver_core_Location); } public Integer getStartOffset() { - return (Integer)getFromLoc(core.Init.core_index__ON__core_Location); + return (Integer)getFromLoc(silver.core.Init.silver_core_index__ON__silver_core_Location); } public Integer getEndOffset() { - return (Integer)getFromLoc(core.Init.core_endIndex__ON__core_Location); + return (Integer)getFromLoc(silver.core.Init.silver_core_endIndex__ON__silver_core_Location); } // This is a utility that I put here because why not. Perhaps it should be moved? @@ -67,14 +72,13 @@ public static NLocation span(final NLocation a, final NLocation b) { final DecoratedNode x = a.decorate(TopNode.singleton, (Lazy[])null); final DecoratedNode y = b.decorate(TopNode.singleton, (Lazy[])null); - return new Ploc( - x.synthesized(core.Init.core_filename__ON__core_Location), - x.synthesized(core.Init.core_line__ON__core_Location), - x.synthesized(core.Init.core_column__ON__core_Location), - y.synthesized(core.Init.core_endLine__ON__core_Location), - y.synthesized(core.Init.core_endColumn__ON__core_Location), - x.synthesized(core.Init.core_index__ON__core_Location), - y.synthesized(core.Init.core_endIndex__ON__core_Location)); + return new Ploc(x.synthesized(silver.core.Init.silver_core_filename__ON__silver_core_Location), + x.synthesized(silver.core.Init.silver_core_line__ON__silver_core_Location), + x.synthesized(silver.core.Init.silver_core_column__ON__silver_core_Location), + y.synthesized(silver.core.Init.silver_core_endLine__ON__silver_core_Location), + y.synthesized(silver.core.Init.silver_core_endColumn__ON__silver_core_Location), + x.synthesized(silver.core.Init.silver_core_index__ON__silver_core_Location), + y.synthesized(silver.core.Init.silver_core_endIndex__ON__silver_core_Location)); } // Ditto public static NLocation createSpan(final Object[] children, VirtualLocation l, int index) { @@ -90,10 +94,15 @@ public static NLocation extractLocation(Object o) { if(o instanceof Terminal) { return ((Terminal)o).location; } else if(o instanceof Alocation) { - return (NLocation) ((Alocation)o).getAnno_core_location(); + return (NLocation) ((Alocation)o).getAnno_silver_core_location(); + } else if(o instanceof Tracked) { + silver.core.NOriginInfo oi = ((Tracked)o).getOrigin(); + if (oi!=null && oi instanceof silver.core.PparsedOriginInfo) { + return ((silver.core.PparsedOriginInfo)oi).getChild_source(); + } } // TODO: a better error, maybe? Eh, it should never happen. - throw new RuntimeException("Attempting to extract location from locationless object"); + throw new RuntimeException("Attempting to extract location from locationless object: "+o.toString()); } /** @@ -114,4 +123,6 @@ public static NLocation extractLocation(Object o) { public final BaseTypeRep getType() { return new BaseTypeRep(getName()); } + + public abstract RTTIManager.Terminalton getTerminalton(); } diff --git a/runtime/java/src/common/Thunk.java b/runtime/java/src/common/Thunk.java index 84038a49d..ffcc6402a 100644 --- a/runtime/java/src/common/Thunk.java +++ b/runtime/java/src/common/Thunk.java @@ -22,6 +22,7 @@ public Thunk(final Evaluable e) { o = e; } + @SuppressWarnings("unchecked") public T eval() { if(o instanceof Evaluable) { o = ((Evaluable)o).eval(); @@ -40,6 +41,7 @@ public static Thunk fromLazy(Lazy l, DecoratedNode ctx) { * @param t Either a DecoratedNode or a Thunk * @return Either a Node or a Thunk */ + @SuppressWarnings("unchecked") public static Object transformUndecorate(final Object t) { // DecoratedNode if(t instanceof DecoratedNode) diff --git a/runtime/java/src/common/TopNode.java b/runtime/java/src/common/TopNode.java index 22e1caad3..453c12633 100644 --- a/runtime/java/src/common/TopNode.java +++ b/runtime/java/src/common/TopNode.java @@ -1,63 +1,69 @@ -package common; - -import common.exceptions.SilverInternalError; - -/** - * TopNode is a DecoratedNode that is empty. It is used largely to give better error - * messages than "null pointer exception" if there is a compiler bug. - * - *

It's used as the parent of the very first thing, as the context for evaluating - * global values, and is the parent of all function nodes. - * - * @author tedinski, bodin - * @see DecoratedNode - */ -public class TopNode extends DecoratedNode{ // TODO: this should become a Node! - - public static final TopNode singleton = new TopNode(); - - private TopNode() { - super(0,0,0,0,null,null,null,null); - } - - @Override - public Object inherited(final int attribute) { - throw new SilverInternalError("No inherited attributes given to TopNode."); - } - - @Override - protected Object inheritedForwarded(final int attribute) { - throw new SilverInternalError("TopNode does not provided inherited attributes."); - } - - @Override - public Lazy synthesized(final int attribute) { - throw new SilverInternalError("No synthesized attributes defined on TopNode."); - } - - @Override - public Object childAsIs(final int s) { - throw new SilverInternalError("No Children defined on TopNode."); - } - - @Override - public DecoratedNode childDecorated(final int s) { - throw new SilverInternalError("No Children defined on TopNode."); - } - - @Override - public Object localAsIs(final int attribute) { - throw new SilverInternalError("No local attributes defined on TopNode."); - } - - @Override - public DecoratedNode localDecorated(final int attribute) { - throw new SilverInternalError("No local attributes defined on TopNode."); - } - - @Override - public DecoratedNode forward() { - throw new SilverInternalError("TopNode does not forward."); - } - -} +package common; + +import common.exceptions.SilverInternalError; + +/** + * TopNode is a DecoratedNode that is empty. It is used largely to give better error + * messages than "null pointer exception" if there is a compiler bug. + * + *

It's used as the parent of the very first thing, as the context for evaluating + * global values, and is the parent of all function nodes. + * + * @author tedinski, bodin + * @see DecoratedNode + */ +public class TopNode extends DecoratedNode{ // TODO: this should become a Node! + + public static final TopNode singleton = new TopNode(); + + private TopNode() { + super(0,0,0,0,null,null,null,null); + this.originCtx = OriginContext.GLOBAL_CONTEXT; + } + + @Override + public final DecoratedNode decorate(final DecoratedNode parent, final Lazy[] inhs) { + throw new SilverInternalError("TopNode cannot be decorated."); + } + + @Override + public T inherited(final int attribute) { + throw new SilverInternalError("No inherited attributes given to TopNode."); + } + + @Override + protected Object inheritedForwarded(final int attribute) { + throw new SilverInternalError("TopNode does not provided inherited attributes."); + } + + @Override + public T synthesized(final int attribute) { + throw new SilverInternalError("No synthesized attributes defined on TopNode."); + } + + @Override + public T childAsIs(final int s) { + throw new SilverInternalError("No Children defined on TopNode."); + } + + @Override + public DecoratedNode childDecorated(final int s) { + throw new SilverInternalError("No Children defined on TopNode."); + } + + @Override + public T localAsIs(final int attribute) { + throw new SilverInternalError("No local attributes defined on TopNode."); + } + + @Override + public DecoratedNode localDecorated(final int attribute) { + throw new SilverInternalError("No local attributes defined on TopNode."); + } + + @Override + public DecoratedNode forward() { + throw new SilverInternalError("TopNode does not forward."); + } + +} diff --git a/runtime/java/src/common/Tracked.java b/runtime/java/src/common/Tracked.java new file mode 100644 index 000000000..5803142ee --- /dev/null +++ b/runtime/java/src/common/Tracked.java @@ -0,0 +1,15 @@ +package common; + +import silver.core.NOriginInfo; + +public interface Tracked { + public abstract NOriginInfo getOrigin(); + + public abstract Object duplicate(Object redex, Object rule); + + public abstract Object duplicate(OriginContext oc); + + public abstract Object duplicateForForwarding(Object redex, String note); + + public abstract Object copy(Object redex, Object rule); +} \ No newline at end of file diff --git a/runtime/java/src/common/TypeRep.java b/runtime/java/src/common/TypeRep.java index 6c0f23010..aec2c7dff 100644 --- a/runtime/java/src/common/TypeRep.java +++ b/runtime/java/src/common/TypeRep.java @@ -37,6 +37,11 @@ public static boolean unify(final TypeRep t1, final TypeRep t2) { return t1.unifyPartial(t2.getSubstitution()) || t2.unifyPartial(t1.getSubstitution()); } + /** + * @return The base name of the type. + */ + public abstract String typeName(); + @Override public abstract String toString(); } diff --git a/runtime/java/src/common/Typed.java b/runtime/java/src/common/Typed.java index 498827333..fa5590b96 100644 --- a/runtime/java/src/common/Typed.java +++ b/runtime/java/src/common/Typed.java @@ -1,18 +1,18 @@ -package common; - -/** - * Interface implemented by all objects with a Silver type (recommended for foreign types as well.) - * Only objects implementing this interface may be part of a reified tree. - * - * @author krame505 - */ -public interface Typed { - /** - * Construct the type representation of the object. - * Note that this returns the freshened type, containing all new type variables, with - * skolems replaced by flexible type vars. - * - * @return The type of the object - */ - public TypeRep getType(); -} +package common; + +/** + * Interface implemented by all objects with a Silver type (recommended for foreign types as well.) + * Only objects implementing this interface may be part of a reified tree. + * + * @author krame505 + */ +public interface Typed { + /** + * Construct the type representation of the object. + * Note that this returns the freshened type, containing all new type variables, with + * skolems replaced by flexible type vars. + * + * @return The type of the object + */ + public TypeRep getType(); +} diff --git a/runtime/java/src/common/Util.java b/runtime/java/src/common/Util.java index 804a98692..1381c119a 100644 --- a/runtime/java/src/common/Util.java +++ b/runtime/java/src/common/Util.java @@ -1,28 +1,25 @@ package common; import java.io.*; -import java.lang.reflect.*; import java.util.*; import java.util.stream.Collectors; import java.net.URI; import common.exceptions.*; import common.javainterop.ConsCellCollection; - -import core.NLocation; -import core.NParseError; -import core.NParseResult; -import core.NTerminalDescriptor; -import core.Ploc; -import core.PparseFailed; -import core.PsyntaxError; -import core.PterminalDescriptor; -import core.PunknownParseError; +import silver.core.NLocation; +import silver.core.NParseError; +import silver.core.NParseResult; +import silver.core.NTerminalDescriptor; +import silver.core.Ploc; +import silver.core.PparseFailed; +import silver.core.PsyntaxError; +import silver.core.PterminalDescriptor; +import silver.core.PunknownParseError; import edu.umn.cs.melt.copper.runtime.engines.CopperParser; import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; import edu.umn.cs.melt.copper.runtime.logging.CopperSyntaxError; - /** * Many places in Silver's translation are bits of code that need factoring out, somehow. * @@ -32,18 +29,69 @@ * @author tedinski, bodin, krame505 */ public final class Util { + private static String forceInit; + private static ArrayList freeThisToPrintErrors; + + public static void init() { + // forceInit = "foo" + "bar" + "baz"; + // freeThisToPrintErrors = new ArrayList(); + // for (int i=0; i<1_000_000; i++) { + // freeThisToPrintErrors.add(i); + // } + } + + public static void stackProbe(int count) { + long a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z; + long aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll, mm, nn, oo, pp, qq, rr, ss, tt, uu, vv, ww, xx, yy, zz; + if (count!=0) stackProbe(count-1); + } + + public static void stackProbe() { + // stackProbe(1_000); + } + + + /** + * There are some places in the translation where we need to perform unchecked casts, + * known to be safe via Silver's static type system. + * Java doesn't have a nice way of suppressing warnings on individual subexpressions, + * so this function exists as a proxy. + * + * @param o An object + * @return o + */ + @SuppressWarnings("unchecked") + public static T uncheckedCast(final Object o) { + return (T) o; + } + /** - * Ensures that a (potential) closure is evaluated. + * Ensures that a (potential) thunk is evaluated. * * Use by writing (v = Util.demand(v)), never just invoke it! * - * @param c Either a value, or a Closure - * @return The value, either directly, or evaluating the Closure + * @param c Either a value, or a Thunk + * @return The value, either directly, or evaluating the Thunk */ - public static Object demand(Object c) { + @SuppressWarnings("unchecked") + public static T demand(final Object c) { if(c instanceof Thunk) - return ((Thunk)c).eval(); - return c; + return ((Thunk)c).eval(); + return (T)c; + } + + /** + * Demand a (potential) thunk in an array of thunks, caching the resulting value. + * This is factored out for the translation of demanding lambda arguments, to avoid unchecked casts. + * + * @param cs An array that are either values or Thunks + * @param i An index in the array + * @return The value, either directly, or evaluating the Thunk at the index + */ + public static T demandIndex(final Object[] cs, final int i) { + T result = demand(cs[i]); + cs[i] = result; + return result; } /** @@ -70,11 +118,11 @@ public static Object error(Object o) { throw new SilverError(o.toString()); } - public static core.NMaybe safetoInt(String s) { + public static silver.core.NMaybe safetoInt(String s) { try { - return new core.Pjust( Integer.valueOf(s) ); + return new silver.core.Pjust(Integer.valueOf(s) ); } catch(NumberFormatException e) { - return new core.Pnothing(); + return new silver.core.Pnothing(); } } @@ -140,6 +188,8 @@ public static int genInt() { } public static void printStackCauses(Throwable e) { + freeThisToPrintErrors = null; + System.err.println("\nAn error occured. Silver stack trace follows. (To see full traces including java elements, SILVERTRACE=1)\n"); if(! "1".equals(System.getenv("SILVERTRACE"))) { @@ -301,7 +351,7 @@ private static void hackyhackyUnparseObject(Object o, StringBuilder sb) { } else if(o instanceof ConsCell) { hackyhackyUnparseList((ConsCell)o, sb); } else { - sb.append(""); + sb.append(""); } } private static void hackyhackyUnparseNode(Node n, StringBuilder sb) { @@ -336,25 +386,23 @@ private static void hackyhackyUnparseList(ConsCell c, StringBuilder sb) { * @param file The filename to report to the parser (filling in location information) * @return A silver ParseResult node. */ - public static NParseResult callCopperParser(CopperParser parser, Object string, Object file) { + public static NParseResult callCopperParser(SilverCopperParser parser, Object string, Object file) { String javaString = ((StringCatter)demand(string)).toString(); String javaFile = ((StringCatter)demand(file)).toString(); try { ROOT tree = parser.parse(new StringReader(javaString), javaFile); Object terminals = getTerminals(parser); - return new core.PparseSucceeded(tree, terminals); + return new silver.core.PparseSucceeded(tree, terminals); } catch(CopperSyntaxError e) { // To create a space, we increment the ending columns and indexes by 1. - NLocation loc = new Ploc( - new StringCatter(e.getVirtualFileName()), + NLocation loc = new Ploc(new StringCatter(e.getVirtualFileName()), e.getVirtualLine(), e.getVirtualColumn(), e.getVirtualLine(), e.getVirtualColumn() + 1, (int)(e.getRealCharIndex()), (int)(e.getRealCharIndex()) + 1); - NParseError err = new PsyntaxError( - new common.StringCatter(e.getMessage()), + NParseError err = new PsyntaxError(new common.StringCatter(e.getMessage()), loc, convertStrings(e.getExpectedTerminalsDisplay().iterator()), convertStrings(e.getMatchedTerminalsDisplay().iterator())); @@ -372,29 +420,22 @@ public static NParseResult callCopperParser(CopperParser Object getTerminals(CopperParser parser) { - Class parserClass = parser.getClass(); - try { - Method getTokens = parserClass.getMethod("getTokens"); - List tokens = (List) getTokens.invoke(parser); - return new Thunk(() -> { - List tds = tokens - .stream() - .map(Util::terminalToTerminalDescriptor) - .collect(Collectors.toList()); - return ConsCellCollection.fromList(tds); - }); - } catch(Throwable t) { - throw new TraceException("Failed to reflect to getTokens()", t); - } + private static Object getTerminals(SilverCopperParser parser) { + List tokens = (List) parser.getTokens(); + return new Thunk(() -> { + List tds = tokens + .stream() + .map(Util::terminalToTerminalDescriptor) + .collect(Collectors.toList()); + return ConsCellCollection.fromList(tds); + }); } /** - * Converts a common.Terminal to a Silver core:TerminalDescriptor. + * Converts a common.Terminal to a Silver silver:core:TerminalDescriptor. */ private static NTerminalDescriptor terminalToTerminalDescriptor(Terminal t) { - return new PterminalDescriptor( - t.lexeme, + return new PterminalDescriptor(t.lexeme, convertStrings(Arrays.stream(t.getLexerClasses()).iterator()), new StringCatter(t.getName()), Terminal.extractLocation(t)); diff --git a/runtime/java/src/common/VarTypeRep.java b/runtime/java/src/common/VarTypeRep.java index 8f2db059a..96f27d4a4 100644 --- a/runtime/java/src/common/VarTypeRep.java +++ b/runtime/java/src/common/VarTypeRep.java @@ -51,6 +51,15 @@ protected final boolean unifyPartial(final TypeRep other) { return true; } } + + @Override + public final String typeName() { + if (this.substitution != null) { + return substitution.typeName(); + } else { + return "a" + id; + } + } @Override public final String toString() { diff --git a/runtime/java/src/common/javainterop/ConsCellCollection.java b/runtime/java/src/common/javainterop/ConsCellCollection.java index e0f1b95c4..edc61fbc7 100644 --- a/runtime/java/src/common/javainterop/ConsCellCollection.java +++ b/runtime/java/src/common/javainterop/ConsCellCollection.java @@ -6,6 +6,7 @@ import java.util.List; import common.ConsCell; +import common.StringCatter; /** * Converts Silver lists into Java iterators. @@ -57,6 +58,10 @@ public static ConsCell fromList(List l) { return ret; } + public static ConsCell fromStringList(List l) { + return fromIterator(l.stream().map(StringCatter::new).iterator()); + } + public static class ConsCellIterator implements Iterator { private ConsCell elem; @@ -71,6 +76,7 @@ public boolean hasNext() { @Override public T next() { + @SuppressWarnings("unchecked") T fst = (T) elem.head(); elem = elem.tail(); return fst; diff --git a/runtime/java/src/common/javainterop/SilverComparator.java b/runtime/java/src/common/javainterop/SilverComparator.java index 521f83aab..1aef8f7f0 100644 --- a/runtime/java/src/common/javainterop/SilverComparator.java +++ b/runtime/java/src/common/javainterop/SilverComparator.java @@ -1,6 +1,7 @@ package common.javainterop; import java.util.Comparator; +import common.OriginContext; import common.NodeFactory; @@ -23,7 +24,7 @@ public SilverComparator(NodeFactory cmp) { @Override public int compare(T arg0, T arg1) { - return cmpFunction.invoke(new Object[] { arg0, arg1 }, null); + return cmpFunction.invoke(OriginContext.FFI_CONTEXT, new Object[] { arg0, arg1 }, null); } } diff --git a/runtime/java/src/common/rawlib/RawGraph.java b/runtime/java/src/common/rawlib/RawGraph.java index fbd1fa858..e0b602d7e 100644 --- a/runtime/java/src/common/rawlib/RawGraph.java +++ b/runtime/java/src/common/rawlib/RawGraph.java @@ -19,13 +19,14 @@ public static TreeMap> empty(NodeFactory cmp) { } // add :: (Graph ::= [Pair] Graph) + @SuppressWarnings("unchecked") public static TreeMap> add(ConsCell l, TreeMap> g) { if(l.nil()) return g; // Note that this clone only the tree map... it's up to us to clone the sets in the values. TreeMap> ret = (TreeMap>)g.clone(); - for(core.NPair elem : new ConsCellCollection(l)) { - assert(elem instanceof core.Ppair); // document as an assert why not + for(silver.core.NPair elem : new ConsCellCollection(l)) { + assert(elem instanceof silver.core.Ppair); // document as an assert why not final Object src = elem.getChild(0); final Object dst = elem.getChild(1); @@ -56,8 +57,8 @@ public static TreeSet edgesFrom(Object key, TreeMap Graph) - public static boolean contains(core.NPair p, TreeMap> g) { - assert(p instanceof core.Ppair); // document as an assert why not + public static boolean contains(silver.core.NPair p, TreeMap> g) { + assert(p instanceof silver.core.Ppair); // document as an assert why not final TreeSet set = g.get(p.getChild(0)); if(set == null) return false; @@ -70,13 +71,14 @@ public static ConsCell toList(TreeMap> g) { for(Entry> e : g.entrySet()) { final Object key = e.getKey(); for(Object value : e.getValue()) { - ret = new ConsCell(new core.Ppair(key, value), ret); + ret = new ConsCell(new silver.core.Ppair(key, value), ret); } } return ret; } // transitiveClosure :: (Graph ::= Graph) + @SuppressWarnings("unchecked") public static TreeMap> transitiveClosure(TreeMap> g) { final TreeMap> ret = (TreeMap>)g.clone(); @@ -112,6 +114,7 @@ public static TreeMap> transitiveClosure(TreeMap ::= [Pair] Graph) + @SuppressWarnings("unchecked") public static TreeMap> repairClosure( ConsCell l, TreeMap> g) { @@ -127,8 +130,8 @@ public static TreeMap> repairClosure( // Cloning the crap out of graphs way too often is a smaller price to pay than the additional // calls to comparison functions. So, uh, to-do someday: make silver fast. Heh. - for(core.NPair elem : new ConsCellCollection(l)) { - assert(elem instanceof core.Ppair); // document as an assert why not + for(silver.core.NPair elem : new ConsCellCollection(l)) { + assert(elem instanceof silver.core.Ppair); // document as an assert why not final Object src = elem.getChild(0); final Object dst = elem.getChild(1); diff --git a/runtime/java/src/common/rawlib/RawProcessHandle.java b/runtime/java/src/common/rawlib/RawProcessHandle.java new file mode 100644 index 000000000..3ff92f671 --- /dev/null +++ b/runtime/java/src/common/rawlib/RawProcessHandle.java @@ -0,0 +1,276 @@ +package common.rawlib; + +import java.io.BufferedReader; +import java.io.OutputStream; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.util.List; +import java.util.ArrayList; + +import common.IOToken; +import common.ConsCell; +import common.javainterop.ConsCellCollection; +import common.StringCatter; +import silver.core.NIOVal; + +/** + * This class represents the `ProcessHandle` type in Silver for + * working with subprocesses. + * + *

Interactions with processes in Silver are handled by the `IOToken` class. + * None of the functions here should be available directly in Silver. + * + * @author RandomActsOfGrammar + */ +public class RawProcessHandle { + + //The underlying process with which we are communicating + private Process proc; + //Used for reading stdout from proc + private BufferedReader our_stdout = null; + //Used for reading stderr from proc + private BufferedReader our_stderr = null; + + + public RawProcessHandle(Process p) { + proc = p; + } + + public RawProcessHandle(List cmd_args) { + ProcessBuilder pb = new ProcessBuilder(cmd_args); + pb.redirectOutput(ProcessBuilder.Redirect.PIPE); + pb.redirectInput(ProcessBuilder.Redirect.PIPE); + pb.redirectError(ProcessBuilder.Redirect.PIPE); + try { + proc = pb.start(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /*Check if stdout has been initialized and initialize it*/ + private void checkInitializeStdout() { + if (our_stdout == null) { + InputStream instream = proc.getInputStream(); + InputStreamReader r = new InputStreamReader(instream); + our_stdout = new BufferedReader(r); + } + } + + /*Check if stderr has been initialized and initialize it*/ + private void checkInitializeStderr() { + if (our_stderr == null) { + InputStream instream = proc.getErrorStream(); + InputStreamReader r = new InputStreamReader(instream); + our_stderr = new BufferedReader(r); + } + } + + + /*Read everything available from a given reader*/ + private String readAll(BufferedReader reader) { + String output = ""; + char last_char; + try { + while (reader.ready()) { + last_char = (char) reader.read(); + output = output + last_char; + } + return output; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*Read everything from a given reader until it ends with ending*/ + private String readUntil(BufferedReader reader, String ending) { + String output = ""; + char last_char; + try { + while (!output.endsWith(ending)) { + last_char = (char) reader.read(); + output = output + last_char; + } + return output; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /*Read a line from stdout of the process*/ + private String readLineStdout() { + try { + checkInitializeStdout(); + return our_stdout.readLine(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*Read everything available from the process (empty string if nothing is available)*/ + private String readAllStdout() { + try { + checkInitializeStdout(); + return readAll(our_stdout); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*Read everything from stdout until the read string ends with ending*/ + private String readUntilStdout(String ending) { + try { + checkInitializeStdout(); + return readUntil(our_stdout, ending); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /*Read a line from stderr of the process*/ + private String readLineStderr() { + try { + checkInitializeStderr(); + return our_stderr.readLine(); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*Read everything available in stderr from the process (empty string if nothing is available)*/ + private String readAllStderr() { + try { + checkInitializeStderr(); + return readAll(our_stderr); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + /*Read everything from stderr until the read string ends with ending*/ + private String readUntilStderr(String ending) { + try { + checkInitializeStderr(); + return readUntil(our_stderr, ending); + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /*Write a message to stdin of the process*/ + private void writeString(String msg) { + OutputStream ostream = proc.getOutputStream(); + try { + ostream.write(msg.getBytes()); + ostream.flush(); + return; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /*Wait for the process to exit*/ + private void waitOnEnd() { + try { + proc.waitFor(); + return; + } + catch (Exception e) { + throw new RuntimeException(e); + } + } + + + /* + These are the public functions which use the Silver types and + call the private functions that actually take care of the + details. + */ + + /** + *

IOVal ::= cmd::String args::[String] io::IO
+ */ + public static NIOVal spawnProcess(StringCatter cmd, ConsCell args, IOToken io) { + List full_cmd = new ArrayList(); + full_cmd.add(cmd.toString()); + //Convert all the args to regular Strings instead of StringCatter + for (StringCatter arg : new ConsCellCollection(args)) { + full_cmd.add(arg.toString()); + } + return io.wrap(new RawProcessHandle(full_cmd)); + } + + /** + *
IO ::= p::ProcessHandle msg::String io::IO
+ */ + public IOToken sendToProcess(StringCatter msg, IOToken io) { + this.writeString(msg.toString()); + return io; + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readLineFromProcess(IOToken io) { + return io.wrap(new StringCatter(this.readLineStdout())); + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readAllFromProcess(IOToken io) { + return io.wrap(new StringCatter(this.readAllStdout())); + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readUntilFromProcess(IOToken io, StringCatter s) { + return io.wrap(new StringCatter(this.readUntilStdout(s.toString()))); + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readErrLineFromProcess(IOToken io) { + return io.wrap(new StringCatter(this.readLineStderr())); + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readErrAllFromProcess(IOToken io) { + return io.wrap(new StringCatter(this.readAllStderr())); + } + + /** + *
IOVal ::= p::ProcessHandle io::IO
+ */ + public NIOVal readErrUntilFromProcess(IOToken io, StringCatter s) { + return io.wrap(new StringCatter(this.readUntilStdout(s.toString()))); + } + + /** + *
IO ::= p::ProcessHandle io::IO
+ */ + public IOToken waitForProcess(IOToken io) { + this.waitOnEnd(); + return io; + } +} + diff --git a/runtime/java/src/common/rawlib/RawTreeMap.java b/runtime/java/src/common/rawlib/RawTreeMap.java index 435da20ca..c4a743bd9 100644 --- a/runtime/java/src/common/rawlib/RawTreeMap.java +++ b/runtime/java/src/common/rawlib/RawTreeMap.java @@ -14,19 +14,27 @@ public static TreeMap empty(NodeFactory cmp) { public static TreeMap addList(ConsCell l, TreeMap t) { if(l.nil()) return t; + @SuppressWarnings("unchecked") TreeMap ret = (TreeMap)t.clone(); - for(core.NPair elem : new ConsCellCollection(l)) { - assert(elem instanceof core.Ppair); // document as an assert why not + for(silver.core.NPair elem : new ConsCellCollection(l)) { + assert(elem instanceof silver.core.Ppair); // document as an assert why not ConsCell existing = lookup(elem.getChild(0), ret); ret.put(elem.getChild(0), new ConsCell(elem.getChild(1), existing)); } return ret; } + public static ConsCell keys(TreeMap t) { + ConsCell ret = ConsCell.nil; + for(Object key : t.descendingKeySet()) { + ret = new ConsCell(key, ret); + } + return ret; + } public static ConsCell toList(TreeMap t) { ConsCell ret = ConsCell.nil; for(Object key : t.descendingKeySet()) { for(Object value : new ConsCellCollection(t.get(key))) { - ret = new ConsCell(new core.Ppair(key, value), ret); + ret = new ConsCell(new silver.core.Ppair(key, value), ret); } } return ret; @@ -36,6 +44,7 @@ public static ConsCell lookup(Object k, TreeMap t) { return r == null ? ConsCell.nil : (ConsCell) r; } public static TreeMap update(Object k, ConsCell v, TreeMap t) { + @SuppressWarnings("unchecked") TreeMap ret = (TreeMap) t.clone(); ret.put(k, v); return ret; diff --git a/runtime/java/src/common/rawlib/RawTreeSet.java b/runtime/java/src/common/rawlib/RawTreeSet.java index 89a8874ff..f6c6ce844 100644 --- a/runtime/java/src/common/rawlib/RawTreeSet.java +++ b/runtime/java/src/common/rawlib/RawTreeSet.java @@ -12,6 +12,7 @@ public static TreeSet empty(NodeFactory cmp) { return new TreeSet(new SilverComparator(cmp)); } public static TreeSet addList(ConsCell l, TreeSet t) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet) t.clone(); ret.addAll(new ConsCellCollection(l)); return ret; @@ -20,16 +21,19 @@ public static ConsCell toList(TreeSet t) { return ConsCellCollection.fromReverseIterator(t.descendingIterator()); } public static TreeSet union(TreeSet l, TreeSet r) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet) l.clone(); ret.addAll(r); return ret; } public static TreeSet intersect(TreeSet l, TreeSet r) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet) l.clone(); ret.retainAll(r); return ret; } public static TreeSet difference(TreeSet l, TreeSet r) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet) l.clone(); ret.removeAll(r); return ret; @@ -53,11 +57,12 @@ public static int size(TreeSet s) { return s.size(); } public static TreeSet filter(NodeFactory cmp, TreeSet s) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet)s.clone(); Iterator x = ret.iterator(); while(x.hasNext()) { - if(!cmp.invoke(new Object[]{x.next()}, null)) { + if(!cmp.invoke(OriginContext.FFI_CONTEXT, new Object[]{x.next()}, null)) { x.remove(); } } @@ -65,6 +70,7 @@ public static TreeSet filter(NodeFactory cmp, TreeSet s return ret; } public static TreeSet removeAll(ConsCell rm, TreeSet s) { + @SuppressWarnings("unchecked") TreeSet ret = (TreeSet)s.clone(); ret.removeAll(new ConsCellCollection(rm)); return ret; diff --git a/runtime/java/src/common/rawlib/RawXML.java b/runtime/java/src/common/rawlib/RawXML.java index cd988707a..b3771d4a3 100644 --- a/runtime/java/src/common/rawlib/RawXML.java +++ b/runtime/java/src/common/rawlib/RawXML.java @@ -24,10 +24,10 @@ import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import lib.xml.ast.NXMLAttribute; -import lib.xml.ast.NXMLDocumentType; -import lib.xml.ast.NXMLNode; -import lib.xml.ast.NXMLNodeList; +import silver.xml.ast.NXMLAttribute; +import silver.xml.ast.NXMLDocumentType; +import silver.xml.ast.NXMLNode; +import silver.xml.ast.NXMLNodeList; import org.w3c.dom.Document; import org.w3c.dom.DocumentType; @@ -44,7 +44,7 @@ import common.exceptions.SilverInternalError; import common.exceptions.TraceException; import common.javainterop.ConsCellCollection; -import core.NPair; +import silver.core.NPair; public final class RawXML { @@ -77,9 +77,9 @@ private static final void ensureParserSetup() { /** * @param fn The filename of an XML file to convert to a Silver AST (Silver type String) * @return The Silver AST of the XML file, wrapped in a ParseResult structure. - * (Silver type core:ParseResult<lib:xml:ast:XMLDocument>) + * (Silver type silver:core:ParseResult<silver:xml:ast:XMLDocument>) */ - public static final core.NParseResult parseXMLFileN(final common.StringCatter fn) { + public static final silver.core.NParseResult parseXMLFileN(final common.StringCatter fn) { ensureParserSetup(); @@ -90,19 +90,19 @@ public static final core.NParseResult parseXMLFileN(final common.StringCatter fn throw new TraceException("IO error while parsing xml file " + fn.toString(), e); } catch (SAXException e) { // Return the failure data structure, with the parse error. - return new core.PparseFailed(new common.StringCatter(e.toString()), null); + return new silver.core.PparseFailed(new common.StringCatter(e.toString()), null); } - return new core.PparseSucceeded(documentF2N(d), null); + return new silver.core.PparseSucceeded(documentF2N(d), null); // fmap documentF2n . parseXMLFileF -- OH WAIT } /** * @param fn The filename of an XML file (Silver type String) * @return A parse result containing a direct reference to a Document. - * (Silver type core:ParseResult<lib:xml:foreigntypes:XML_Document>) + * (Silver type silver:core:ParseResult<silver:xml:foreigntypes:XML_Document>) */ - public static final core.NParseResult parseXMLFileF(final common.StringCatter fn) { + public static final silver.core.NParseResult parseXMLFileF(final common.StringCatter fn) { ensureParserSetup(); @@ -113,42 +113,42 @@ public static final core.NParseResult parseXMLFileF(final common.StringCatter fn throw new TraceException("IO error while parsing xml file " + fn.toString(), e); } catch (SAXException e) { // Return the failure data structure, with the parse error. - return new core.PparseFailed(new common.StringCatter(e.toString()), null); + return new silver.core.PparseFailed(new common.StringCatter(e.toString()), null); } - return new core.PparseSucceeded(d, null); + return new silver.core.PparseSucceeded(d, null); } /** * Convert a document of type Foreign DOM to Silver XML AST. * - * @param d A DOM Document (Silver type lib:xml:foreigntypes:XML_Document) - * @return An equivalent Silver AST type. (Silver type lib:xml:ast:XMLDocument) + * @param d A DOM Document (Silver type silver:xml:foreigntypes:XML_Document) + * @return An equivalent Silver AST type. (Silver type silver:xml:ast:XMLDocument) */ - public static final lib.xml.ast.NXMLDocument documentF2N(org.w3c.dom.Document d) { + public static final silver.xml.ast.NXMLDocument documentF2N(org.w3c.dom.Document d) { final DocumentType dt = d.getDoctype(); final NodeList nl = d.getChildNodes(); - lib.xml.ast.NXMLDocumentType sdt; + silver.xml.ast.NXMLDocumentType sdt; if(dt != null) { - sdt = new lib.xml.ast.PxmlDocumentType( + sdt = new silver.xml.ast.PxmlDocumentType( new common.StringCatter(dt.getName()), nodeListF2N(dt.getChildNodes())); } else { - sdt = new lib.xml.ast.PxmlNoDocumentType(); + sdt = new silver.xml.ast.PxmlNoDocumentType(); } - return new lib.xml.ast.PxmlDocument(sdt, nodeListF2N(nl)); + return new silver.xml.ast.PxmlDocument(sdt, nodeListF2N(nl)); } /** * Convert a document of type Silver XML AST to a Foreign DOM type * - * @param docAst Silver AST Document (Silver type lib:xml:ast:XMLDocument) - * @return An equivalent DOM document (Silver type lib:xml:foreigntypes:XML_Document) + * @param docAst Silver AST Document (Silver type silver:xml:ast:XMLDocument) + * @return An equivalent DOM document (Silver type silver:xml:foreigntypes:XML_Document) */ - public static final Document documentN2F(lib.xml.ast.NXMLDocument docAst) { + public static final Document documentN2F(silver.xml.ast.NXMLDocument docAst) { ensureParserSetup(); Document docContext = parser.newDocument(); @@ -161,7 +161,7 @@ public static final Document documentN2F(lib.xml.ast.NXMLDocument docAst) { /** * Produces a text document from a DOM tree * - * @param document DOM tree to unparse (Silver type lib:xml:foreigntypes:XML_Document) + * @param document DOM tree to unparse (Silver type silver:xml:foreigntypes:XML_Document) * @return String output (Silver type String) */ public static final StringCatter documentF2String(Document document) { @@ -190,7 +190,7 @@ public static final StringCatter documentF2String(Document document) { * @param document The Node or document to query * @param querystring The XPath query to execute on this document * @param namespace null, or a [(String, String)] namespace resolver info - * @return The foreign DOM response (Silver type lib:xml:foreigntypes:XML_NodeList) + * @return The foreign DOM response (Silver type silver:xml:foreigntypes:XML_NodeList) */ public static final NodeList xpathQueryNodeSet(Object document, String querystring, common.ConsCell namespace) { try { @@ -233,12 +233,12 @@ public static final StringCatter xpathQueryString(Object document, String querys /** * Convert a DOM NodeList to a silver one * - * @param nl a DOM NodeList (Silver type lib:xml:foreigntypes:XML_NodeList) - * @return a silver ast NodeList (Silver type lib:xml:ast:XMLNodeList) + * @param nl a DOM NodeList (Silver type silver:xml:foreigntypes:XML_NodeList) + * @return a silver ast NodeList (Silver type silver:xml:ast:XMLNodeList) */ - public static final lib.xml.ast.NXMLNodeList nodeListF2N(final NodeList nl) { + public static final silver.xml.ast.NXMLNodeList nodeListF2N(final NodeList nl) { - lib.xml.ast.NXMLNodeList l = new lib.xml.ast.PxmlNodeListNil(); + silver.xml.ast.NXMLNodeList l = new silver.xml.ast.PxmlNodeListNil(); for(int i = nl.getLength() - 1; i >= 0; i -- ) { final Node n = nl.item(i); @@ -250,19 +250,19 @@ public static final lib.xml.ast.NXMLNodeList nodeListF2N(final NodeList nl) { common.StringCatter val = new common.StringCatter(n.getNodeValue()); // Add this element to the list - l = new lib.xml.ast.PxmlNodeListCons( new lib.xml.ast.PxmlNodeText(val), l); + l = new silver.xml.ast.PxmlNodeListCons( new silver.xml.ast.PxmlNodeText(val), l); break; case Node.ELEMENT_NODE: case Node.ENTITY_NODE: // treating the same as an element for now! // Convert the name common.StringCatter name = new common.StringCatter(n.getNodeName()); // Convert the attributes - common.ConsCell/*lib.xml.ast.NXMLAttribute*/ attrs = attributesF2N(n.getAttributes()); + common.ConsCell/*silver.xml.ast.NXMLAttribute*/ attrs = attributesF2N(n.getAttributes()); // Convert the children - lib.xml.ast.NXMLNodeList children = nodeListF2N(n.getChildNodes()); + silver.xml.ast.NXMLNodeList children = nodeListF2N(n.getChildNodes()); // Create the Node - l = new lib.xml.ast.PxmlNodeListCons( new lib.xml.ast.PxmlNodeElement(name, attrs, children), l); + l = new silver.xml.ast.PxmlNodeListCons( new silver.xml.ast.PxmlNodeElement(name, attrs, children), l); break; case Node.COMMENT_NODE: case Node.DOCUMENT_TYPE_NODE: // for now: wtf? @@ -278,8 +278,8 @@ public static final lib.xml.ast.NXMLNodeList nodeListF2N(final NodeList nl) { /** * Convert a DOM Node List to a silver list of DOM nodes! * - * @param nl a DOM Node list (Silver type lib:xml:foreigntypes:XML_NodeList) - * @return a list of DOM Nodes (Silver type [lib:xml:foreigntypes:XML_Node]) + * @param nl a DOM Node list (Silver type silver:xml:foreigntypes:XML_NodeList) + * @return a list of DOM Nodes (Silver type [silver:xml:foreigntypes:XML_Node]) */ public static final common.ConsCell nodeListF2NPartial(final NodeList nl) { @@ -296,47 +296,47 @@ public static final common.ConsCell nodeListF2NPartial(final NodeList nl) { * Convert a DOM NamedNodeMap (attributes) to a list of silver attributes. * * @param al DOM Attribute list - * @return silver ast attribute list (Silver type [lib:xml:ast:XMLAttribute]) + * @return silver ast attribute list (Silver type [silver:xml:ast:XMLAttribute]) */ private static final common.ConsCell attributesF2N(final NamedNodeMap al) { - common.ConsCell/*lib.xml.ast.NXMLAttribute*/ l = common.ConsCell.nil; + common.ConsCell/*silver.xml.ast.NXMLAttribute*/ l = common.ConsCell.nil; for(int i = 0; i < al.getLength(); i++ ) { final Node n = al.item(i); final common.StringCatter key = new common.StringCatter(n.getNodeName()); final common.StringCatter value = new common.StringCatter(n.getNodeValue()); - l = new common.ConsCell( new lib.xml.ast.PxmlAttribute(key, value), l); + l = new common.ConsCell( new silver.xml.ast.PxmlAttribute(key, value), l); } return l; } - private static final void deconvertXmlAstDocument(lib.xml.ast.NXMLDocument docAst, Document docContext) { - lib.xml.ast.NXMLDocumentType dt = (NXMLDocumentType) docAst.getChild(0); - lib.xml.ast.NXMLNodeList nl = (NXMLNodeList) docAst.getChild(1); + private static final void deconvertXmlAstDocument(silver.xml.ast.NXMLDocument docAst, Document docContext) { + silver.xml.ast.NXMLDocumentType dt = (NXMLDocumentType) docAst.getChild(0); + silver.xml.ast.NXMLNodeList nl = (NXMLNodeList) docAst.getChild(1); mutateDocumentType(dt, docContext); mutateXmlAstNodeList(nl, docContext, docContext); } - private static final void mutateDocumentType(lib.xml.ast.NXMLDocumentType docTypeAst, Document docContext) { - if(docTypeAst instanceof lib.xml.ast.PxmlNoDocumentType) { + private static final void mutateDocumentType(silver.xml.ast.NXMLDocumentType docTypeAst, Document docContext) { + if(docTypeAst instanceof silver.xml.ast.PxmlNoDocumentType) { // do nothing! - } else if(docTypeAst instanceof lib.xml.ast.PxmlDocumentType) { + } else if(docTypeAst instanceof silver.xml.ast.PxmlDocumentType) { // TODO: oh, bother. how? } else { throw new SilverInternalError("Unknown type in XML AST during unparse of doctype..."); } } - private static final void mutateXmlAstNodeList(lib.xml.ast.NXMLNodeList docNLAst, Node nodeContext, Document docContext) { + private static final void mutateXmlAstNodeList(silver.xml.ast.NXMLNodeList docNLAst, Node nodeContext, Document docContext) { NXMLNodeList current = docNLAst; - while(current instanceof lib.xml.ast.PxmlNodeListCons) { + while(current instanceof silver.xml.ast.PxmlNodeListCons) { Node n = deconvertXmlAstNode((NXMLNode)current.getChild(0), docContext); nodeContext.appendChild(n); current = (NXMLNodeList) current.getChild(1); } } - private static final Node deconvertXmlAstNode(lib.xml.ast.NXMLNode docNodeAst, Document docContext) { - if(docNodeAst instanceof lib.xml.ast.PxmlNodeElement) { + private static final Node deconvertXmlAstNode(silver.xml.ast.NXMLNode docNodeAst, Document docContext) { + if(docNodeAst instanceof silver.xml.ast.PxmlNodeElement) { StringCatter sc = (StringCatter) docNodeAst.getChild(0); common.ConsCell at = (ConsCell) docNodeAst.getChild(1); NXMLNodeList nl = (NXMLNodeList) docNodeAst.getChild(2); @@ -346,7 +346,7 @@ private static final Node deconvertXmlAstNode(lib.xml.ast.NXMLNode docNodeAst, D mutateXmlAstNodeList(nl, n, docContext); return n; } - if(docNodeAst instanceof lib.xml.ast.PxmlNodeText) { + if(docNodeAst instanceof silver.xml.ast.PxmlNodeText) { StringCatter sc = (StringCatter) docNodeAst.getChild(0); Text t = docContext.createTextNode(sc.toString()); @@ -355,7 +355,7 @@ private static final Node deconvertXmlAstNode(lib.xml.ast.NXMLNode docNodeAst, D throw new SilverInternalError("Unknown type in XML AST during unparse of node..."); } - private static final void mutateAttributes(common.ConsCell/*lib.xml.ast.NXMLAttribute*/ docAttrsAst, Element elem, Document docContext) { + private static final void mutateAttributes(common.ConsCell/*silver.xml.ast.NXMLAttribute*/ docAttrsAst, Element elem, Document docContext) { common.ConsCell current = docAttrsAst; while(!current.nil()) { diff --git a/runtime/java/src/monto/BrokerRequest.java b/runtime/java/src/monto/BrokerRequest.java deleted file mode 100644 index af1a1d4a8..000000000 --- a/runtime/java/src/monto/BrokerRequest.java +++ /dev/null @@ -1,13 +0,0 @@ -package monto; - -import java.util.List; - -public class BrokerRequest { - public final ProductIdentifier request; - public final List products; - - public BrokerRequest(ProductIdentifier request, List products) { - this.request = request; - this.products = products; - } -} diff --git a/runtime/java/src/monto/FFI.java b/runtime/java/src/monto/FFI.java deleted file mode 100644 index f78996364..000000000 --- a/runtime/java/src/monto/FFI.java +++ /dev/null @@ -1,149 +0,0 @@ -package monto; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import common.ConsCell; -import common.DecoratedNode; -import common.NodeFactory; -import common.StringCatter; -import core.NMaybe; -import core.NPair; -import core.Pjust; -import core.Pnothing; -import core.Ppair; -import java.util.ArrayList; -import java.util.List; -import java.util.function.Function; -import silver.json.NJson; -import silver.json.PjsonArray; -import silver.json.PjsonBoolean; -import silver.json.PjsonFloat; -import silver.json.PjsonNull; -import silver.json.PjsonObject; -import silver.json.PjsonString; -import silver.support.monto.Init; -import silver.support.monto.NService; -import silver.support.monto.negotiation.NProtocolVersion; -import silver.support.monto.negotiation.NServiceBrokerNegotiation; -import silver.support.monto.negotiation.NSoftwareVersion; -import silver.support.monto.negotiation.PprotocolVersion; -import silver.support.monto.negotiation.PserviceBrokerNegotiation; -import silver.support.monto.negotiation.PsoftwareVersion; -import silver.support.monto.products.NMetaItem; -import silver.support.monto.products.PmetaItem; -import silver.support.monto.products.Pproduct; -import silver.support.monto.products.PproductIdentifier; -import silver.support.monto.products.PproductValue; - -/** - * FFI Helpers. - */ -public class FFI { - public static Pair onRequest(NService service, ProductIdentifier ident, List depList) { - DecoratedNode dn = service.decorate(); - NodeFactory onRequest = (NodeFactory) dn.synthesized(Init.silver_support_monto_onRequest__ON__silver_support_monto_Service); - return PairFromNPair(onRequest.invoke(new Object[] { - new PproductIdentifier( - new StringCatter(ident.name), - new StringCatter(ident.language), - new StringCatter(ident.path)), - ConsCellFromList(FFI::PproductFromProduct, depList), - }, new Object[] {})); - } - - public static Pair doNegotiation(NService service, ServiceBrokerNegotiation sbn) { - DecoratedNode dn = service.decorate(); - NodeFactory doNegotiation = (NodeFactory) dn.synthesized(Init.silver_support_monto_doNegotiation__ON__silver_support_monto_Service); - NServiceBrokerNegotiation sbn2 = NServiceBrokerNegotiationFromServiceBrokerNegotiation(sbn); - return PairFromNPair(doNegotiation.invoke(new Object[] {sbn2}, new Object[] {})); - } - - private static ConsCell ConsCellFromList(Function convert, List list) { - ConsCell cons = ConsCell.nil; - for(int i = list.size() - 1; i >= 0; i--) - cons = new ConsCell(convert.apply(list.remove(i)), cons); - return cons; - } - private static Pair PairFromNPair(NPair pair) { - DecoratedNode dn = pair.decorate(); - return new Pair( - (T) dn.synthesized(core.Init.core_fst__ON__core_Pair), - (U) dn.synthesized(core.Init.core_snd__ON__core_Pair)); - } - private static Pproduct PproductFromProduct(Product product) { - NJson json = NJsonFromJsonElement(product.contents); - PproductValue value = new PproductValue(new StringCatter(product.name), json); - List metaList = product.meta; - if(metaList == null) - metaList = new ArrayList<>(); - ConsCell metas = ConsCellFromList(FFI::NMetaItemFromMetaItem, metaList); - return new Pproduct(value, metas, - new StringCatter(product.language), - new StringCatter(product.path)); - } - private static NJson NJsonFromJsonElement(JsonElement el) { - if(el.isJsonNull()) { - return new PjsonNull(); - } else if(el.isJsonArray()) { - JsonArray arr = el.getAsJsonArray(); - List list = new ArrayList(); - arr.forEach(list::add); - ConsCell cc = ConsCellFromList(FFI::NJsonFromJsonElement, list); - return new PjsonArray(cc); - } else if(el.isJsonObject()) { - JsonObject obj = el.getAsJsonObject(); - List list = new ArrayList(); - obj.entrySet().iterator().forEachRemaining(e -> { - String k = e.getKey(); - JsonElement v = e.getValue(); - list.add(new Ppair(new StringCatter(k), NJsonFromJsonElement(v))); - }); - ConsCell cc = ConsCellFromList(x -> x, list); - return new PjsonObject(cc); - } else { - JsonPrimitive prim = el.getAsJsonPrimitive(); - if(prim.isBoolean()) { - return new PjsonBoolean(prim.getAsBoolean()); - } else if(prim.isNumber()) { - return new PjsonFloat(prim.getAsNumber().doubleValue()); - } else if(prim.isString()) { - return new PjsonString(new StringCatter(prim.getAsString())); - } else { - throw new RuntimeException("TODO NJsonFromJsonElement(" + prim + ")"); - } - } - } - private static NMetaItem NMetaItemFromMetaItem(MetaItem meta) { - return new PmetaItem(new StringCatter(meta.service), - new StringCatter(meta.type), - new StringCatter(meta.value)); - } - private static NProtocolVersion NProtocolVersionFromProtocolVersion(ProtocolVersion v) { - return new PprotocolVersion(v.major, v.minor, v.patch); - } - private static NSoftwareVersion NSoftwareVersionFromSoftwareVersion(SoftwareVersion v) { - return new PsoftwareVersion(new StringCatter(v.id), - MaybeFromNullable(StringCatter::new, v.name), - MaybeFromNullable(StringCatter::new, v.vendor), - MaybeFromNullable(x -> x, v.major), - MaybeFromNullable(x -> x, v.minor), - MaybeFromNullable(x -> x, v.patch)); - } - private static NServiceBrokerNegotiation NServiceBrokerNegotiationFromServiceBrokerNegotiation(ServiceBrokerNegotiation sbn) { - List extensions = sbn.extensions; - if(extensions == null) - extensions = new ArrayList<>(); - return new PserviceBrokerNegotiation( - NProtocolVersionFromProtocolVersion(sbn.monto), - NSoftwareVersionFromSoftwareVersion(sbn.broker), - ConsCellFromList(StringCatter::new, extensions)); - } - private static NMaybe MaybeFromNullable(Function f, T t) { - if(t == null) - return new Pnothing(); - else - return new Pjust(f.apply(t)); - } -} diff --git a/runtime/java/src/monto/MetaItem.java b/runtime/java/src/monto/MetaItem.java deleted file mode 100644 index c29f3ebcc..000000000 --- a/runtime/java/src/monto/MetaItem.java +++ /dev/null @@ -1,13 +0,0 @@ -package monto; - -public class MetaItem { - public final String type; - public final String value; - public final String service; - - public MetaItem(String type, String value, String service) { - this.type = type; - this.value = value; - this.service = service; - } -} diff --git a/runtime/java/src/monto/Pair.java b/runtime/java/src/monto/Pair.java deleted file mode 100644 index 00f4429c1..000000000 --- a/runtime/java/src/monto/Pair.java +++ /dev/null @@ -1,14 +0,0 @@ -package monto; - -/** - * A type for a pair. Why is this not a builtin? - */ -public class Pair { - public final A first; - public final B second; - - public Pair(A first, B second) { - this.first = first; - this.second = second; - } -} diff --git a/runtime/java/src/monto/Product.java b/runtime/java/src/monto/Product.java deleted file mode 100644 index aafd6a60a..000000000 --- a/runtime/java/src/monto/Product.java +++ /dev/null @@ -1,20 +0,0 @@ -package monto; - -import com.google.gson.JsonElement; -import java.util.List; - -public class Product { - public final JsonElement contents; - public final String language; - public final String name; - public final List meta; - public final String path; - - public Product(String language, String name, String path, JsonElement contents, List meta) { - this.language = language; - this.name = name; - this.path = path; - this.contents = contents; - this.meta = meta; - } -} diff --git a/runtime/java/src/monto/ProductDescriptor.java b/runtime/java/src/monto/ProductDescriptor.java deleted file mode 100644 index 6a9981bc4..000000000 --- a/runtime/java/src/monto/ProductDescriptor.java +++ /dev/null @@ -1,11 +0,0 @@ -package monto; - -public class ProductDescriptor { - public final String language; - public final String name; - - public ProductDescriptor(String language, String name) { - this.language = language; - this.name = name; - } -} diff --git a/runtime/java/src/monto/ProductIdentifier.java b/runtime/java/src/monto/ProductIdentifier.java deleted file mode 100644 index 1ab53ef5d..000000000 --- a/runtime/java/src/monto/ProductIdentifier.java +++ /dev/null @@ -1,13 +0,0 @@ -package monto; - -public class ProductIdentifier { - public final String language; - public final String name; - public final String path; - - public ProductIdentifier(String language, String name, String path) { - this.language = language; - this.name = name; - this.path = path; - } -} diff --git a/runtime/java/src/monto/ProtocolVersion.java b/runtime/java/src/monto/ProtocolVersion.java deleted file mode 100644 index ec4d5e7ba..000000000 --- a/runtime/java/src/monto/ProtocolVersion.java +++ /dev/null @@ -1,13 +0,0 @@ -package monto; - -public class ProtocolVersion { - public final int major; - public final int minor; - public final int patch; - - public ProtocolVersion(int major, int minor, int patch) { - this.major = major; - this.minor = minor; - this.patch = patch; - } -} diff --git a/runtime/java/src/monto/Server.java b/runtime/java/src/monto/Server.java deleted file mode 100644 index 7394cf352..000000000 --- a/runtime/java/src/monto/Server.java +++ /dev/null @@ -1,98 +0,0 @@ -package monto; - -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.net.InetSocketAddress; - -import silver.support.monto.NService; - -import com.google.gson.Gson; -import com.sun.net.httpserver.HttpExchange; -import com.sun.net.httpserver.HttpHandler; -import com.sun.net.httpserver.HttpServer; -import common.StringCatter; - -/** - * The HTTP server for a Monto3 service. - */ -public class Server { - private static final Gson gson = new Gson(); - - private HttpServer server; - private NService service; - - public Server(NService service, int port) throws IOException { - this.server = HttpServer.create(new InetSocketAddress(port), 0); - this.server.createContext("/monto/version", new LoggingHandler(new NegotiationHandler())); - this.server.createContext("/monto/service", new LoggingHandler(new RequestHandler())); - this.service = service; - } - - public static int run(NService service, int port) { - try { - new Server(service, port).start(); - } catch(IOException ioex) { - throw new RuntimeException(ioex); - } - while(true) { - try { - Thread.sleep(Long.MAX_VALUE); - } catch(InterruptedException iex) {} - } - } - - public void start() { - this.server.start(); - } - - private class LoggingHandler implements HttpHandler { - private HttpHandler inner; - public LoggingHandler(HttpHandler inner) { - this.inner = inner; - } - public void handle(HttpExchange t) throws IOException { - System.out.println("Starting to handle " + t.getRequestURI() + "..."); - long startTime = System.nanoTime(); - try { - inner.handle(t); - } catch(Exception ex) { - ex.printStackTrace(); - throw ex; - } - long requestTimeTaken = (System.nanoTime() - startTime) / 1000000; - System.out.println("Handled " + t.getRequestURI() + " in " + - requestTimeTaken + "ms."); - } - } - - private class NegotiationHandler implements HttpHandler { - public void handle(HttpExchange t) throws IOException { - InputStreamReader isr = new InputStreamReader(t.getRequestBody()); - ServiceBrokerNegotiation sbn = gson.fromJson(isr, ServiceBrokerNegotiation.class); - Pair p = FFI.doNegotiation(service, sbn); - int responseCode = 200; - if(!p.second) responseCode = 409; - String response = p.first.toString(); - t.sendResponseHeaders(responseCode, response.length()); - OutputStream out = t.getResponseBody(); - out.write(response.getBytes()); - out.close(); - t.close(); - } - } - - private class RequestHandler implements HttpHandler { - public void handle(HttpExchange t) throws IOException { - InputStreamReader isr = new InputStreamReader(t.getRequestBody()); - BrokerRequest br = gson.fromJson(isr, BrokerRequest.class); - Pair p = FFI.onRequest(service, br.request, br.products); - String res = p.first.toString(); - t.sendResponseHeaders(p.second, res.length()); - OutputStream out = t.getResponseBody(); - out.write(res.getBytes()); - out.close(); - t.close(); - } - } -} diff --git a/runtime/java/src/monto/ServiceBrokerNegotiation.java b/runtime/java/src/monto/ServiceBrokerNegotiation.java deleted file mode 100644 index 996ef21db..000000000 --- a/runtime/java/src/monto/ServiceBrokerNegotiation.java +++ /dev/null @@ -1,15 +0,0 @@ -package monto; - -import java.util.List; - -public class ServiceBrokerNegotiation { - public final ProtocolVersion monto; - public final SoftwareVersion broker; - public final List extensions; - - public ServiceBrokerNegotiation(ProtocolVersion monto, SoftwareVersion broker, List extensions) { - this.monto = monto; - this.broker = broker; - this.extensions = extensions; - } -} diff --git a/runtime/java/src/monto/SoftwareVersion.java b/runtime/java/src/monto/SoftwareVersion.java deleted file mode 100644 index 703574730..000000000 --- a/runtime/java/src/monto/SoftwareVersion.java +++ /dev/null @@ -1,19 +0,0 @@ -package monto; - -public class SoftwareVersion { - public final String id; - public final String name; - public final String vendor; - public final Integer major; - public final Integer minor; - public final Integer patch; - - public SoftwareVersion(String id, String name, String vendor, Integer major, Integer minor, Integer patch) { - this.id = id; - this.name = name; - this.vendor = vendor; - this.major = major; - this.minor = minor; - this.patch = patch; - } -} diff --git a/runtime/lsp4j/.gitignore b/runtime/lsp4j/.gitignore new file mode 100644 index 000000000..b83d22266 --- /dev/null +++ b/runtime/lsp4j/.gitignore @@ -0,0 +1 @@ +/target/ diff --git a/runtime/lsp4j/README.md b/runtime/lsp4j/README.md new file mode 100644 index 000000000..086c037a8 --- /dev/null +++ b/runtime/lsp4j/README.md @@ -0,0 +1,6 @@ +# lsp4j utils +This library contains utilities for interoperation between lsp4j and Silver/Copper, +e.g. encoding semantic tokens, converting Silver Message values into lsp4j Diagnostics, +loading bundled standard library resource files, etc. +It is currently used by the [LSP implementation for Silver](../../language-server/) itself, +but is intended for reuse in LSP implementations in other Silver-specified languages. \ No newline at end of file diff --git a/runtime/lsp4j/pom.xml b/runtime/lsp4j/pom.xml new file mode 100644 index 000000000..1cbdce6b8 --- /dev/null +++ b/runtime/lsp4j/pom.xml @@ -0,0 +1,50 @@ + + 4.0.0 + edu.umn.cs.melt + lsp4j-util + 0.1.0-SNAPSHOT + jar + + + + org.eclipse.lsp4j + org.eclipse.lsp4j + 0.14.0 + + + + edu.umn.cs.melt + silver + 0.4.4-SNAPSHOT + + + edu.umn.cs.melt + silver-runtime + 0.4.4-SNAPSHOT + + + edu.umn.cs.melt + copper-runtime + 1.0.1-SNAPSHOT + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.1 + + 11 + 11 + + + + + diff --git a/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperParserNodeFactory.java b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperParserNodeFactory.java new file mode 100644 index 000000000..f68d14445 --- /dev/null +++ b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperParserNodeFactory.java @@ -0,0 +1,42 @@ +package edu.umn.cs.melt.lsp4jutil; + +import java.util.function.Supplier; + +import common.AppTypeRep; +import common.BaseTypeRep; +import common.FunctionTypeRep; +import common.NodeFactory; +import common.OriginContext; +import common.SilverCopperParser; +import common.exceptions.TraceException; +import common.Util; +import silver.core.NParseResult; + +/** +* A wrapper to treat a Silver-generated Copper parser as a Silver function. +* +* @author krame505 +* @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens +*/ +public class CopperParserNodeFactory extends NodeFactory { + private Supplier> parserFactory; + + public CopperParserNodeFactory(Supplier> parserFactory) { + this.parserFactory = parserFactory; + } + + @Override + public AppTypeRep getType() { + return new AppTypeRep(new AppTypeRep(new AppTypeRep(new FunctionTypeRep(2, new String[] {}), new BaseTypeRep("String")), new BaseTypeRep("String")), new AppTypeRep(new BaseTypeRep("silver:core:ParseResult"), new BaseTypeRep("silver:compiler:definition:core:Root"))); + } + + @Override + public NParseResult invoke(OriginContext originCtx, Object[] args, Object[] namedArgs) { + try { + return Util.callCopperParser(parserFactory.get(), args[0], args[1]); + } catch(Throwable t) { + throw new TraceException("Error while invoking Copper parser", t); + } + } + +} diff --git a/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperSemanticTokenEncoder.java b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperSemanticTokenEncoder.java new file mode 100644 index 000000000..4177365b2 --- /dev/null +++ b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/CopperSemanticTokenEncoder.java @@ -0,0 +1,157 @@ +package edu.umn.cs.melt.lsp4jutil; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import common.SilverCopperParser; +import common.Terminal; +import edu.umn.cs.melt.copper.runtime.logging.CopperParserException; + +/** + * A utility for invoking a Silver-generated Copper parser to obtain semantic + * tokens, encoded as specified in the LSP specification. + * + * Semantic tokens are specified for a grammar by defining the lexer classes + * in silver:langutil:lsp on the terminals of the grammar. Additional semantic + * tokens that do not directly correspond to a terminal in the grammar can be + * included with the `insert semantic token` parser action. + * This class provides a utility to run the generated parser, extract the list + * of parsed terminals, and encode them as a stream of integers. Terminals with + * any of the specified "token type" lexer classes are returned as semantic + * tokens in the stream, with their position, length, token type and modifiers + * encoded as integers according to the language server protocol specificiation. + * + * @author krame505 + * @see https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens + */ +public class CopperSemanticTokenEncoder { + // The constructor for the Copper parser + private final Supplier> parserFactory; + + // The legend mapping semantic token types and modifier names to their encodings + private final Map tokenTypes; + private final Map tokenModifiers; + + /** + * Construct a CopperSemanticTokenEncoder from a parser constructor and encoding legend. + * We pass a parser factory here because Copper parsers are non-reentrant, + * so to be thread safe we need to construct a new parser object for every call to parseTokens. + * + * @param parserFactory The constructor for a Copper parser + * @param tokenTypes Semantic token types that will be used + * @param tokenModifiers Semantic token modifiers that will be used + */ + public CopperSemanticTokenEncoder(Supplier> parserFactory, List tokenTypes, List tokenModifiers) { + this.parserFactory = parserFactory; + this.tokenTypes = IntStream.range(0, tokenTypes.size()).boxed() + .collect(Collectors.toMap(tokenTypes::get, i -> i)); + this.tokenModifiers = IntStream.range(0, tokenModifiers.size()).boxed() + .collect(Collectors.toMap(tokenModifiers::get, i -> i)); + } + + /** + * Parse a string and encode the result as semantic tokens. + * + * @param content A string to parse + * @return The encoded tokens + */ + public List parseTokens(String content) { + SilverCopperParser parser = parserFactory.get(); + + // By default, Copper treats tabs as padding to multiples of 8 chars in the + // reported column numbers, while we want the absolute character index. + parser.setTabStop(1); + + try { + parser.parse(content); + } catch (CopperParserException e) { + // Ignore parse errors + } catch (IOException e) { + e.printStackTrace(); + } + return encodeTokens(parser.getTokens()); + } + + // A map of previously seen terminal names to 2-element arrays containing + // {encoded type, encoded modifier}, or null if the terminal does not have + // a corresponding semantic token. + private final Map tokenEncodings = new HashMap<>(); + + /** + * Encode a list of parsed terminals as semantic tokens. + * @param tokens The terminals to encode + * @return The encoded tokens + */ + private List encodeTokens(List tokens) { + List result = new ArrayList<>(); + int prevLine = 1; + int prevStartChar = 0; + for (Terminal t : tokens) { + int type = -1; + int modifiers = 0; + if (tokenEncodings.containsKey(t.getName())) { + // We've seen this type of terminal before, look up its type and modifier encoding + int[] encoding = tokenEncodings.get(t.getName()); + if (encoding != null) { + type = encoding[0]; + modifiers = encoding[1]; + } + } else { + // We've haven't this type of terminal before, look for lsp lexer classes + for (String c : t.getLexerClasses()) { + if (c.startsWith("silver:langutil:lsp:")) { + String baseName = c.split("silver:langutil:lsp:")[1].split("_")[0]; + String id = baseName.substring(0, 1).toLowerCase() + baseName.substring(1); + if (tokenTypes.containsKey(id)) { + type = tokenTypes.get(id); + } else if (tokenModifiers.containsKey(id)) { + modifiers |= 1 << tokenModifiers.get(id); + } + } + } + // Cache the encoding (or lack thereof) that we computed + tokenEncodings.put(t.getName(), type != -1? new int[]{type, modifiers} : null); + } + if (type != -1) { + // This terminal has an encoding, add it to the result. + // For each line in the parsed terminal, we emit a semantic token. + String[] lines = t.lexeme.toString().split("\n", -1); + assert lines.length == t.getEndLine() - t.getLine() + 1; + for (int line = t.getLine(); line <= t.getEndLine(); line++) { + // Determine the length of this line of the token + int length = lines.length > 1? + lines[line - t.getLine()].length() : + t.getEndOffset() - t.getStartOffset(); + + // Don't report tokens for empty lines + if (length == 0) continue; + + // Compute delta start line and delta start char + int column = line == t.getLine()? t.getColumn() : 0; + int deltaLine = line - prevLine; + if (deltaLine != 0) { + prevStartChar = 0; + } + int deltaStartChar = column - prevStartChar; + prevLine = line; + prevStartChar = column; + + // Add the encoded token info to the result + result.add(deltaLine); + result.add(deltaStartChar); + result.add(length); + result.add(type); + result.add(modifiers); + //System.err.println(t.getName() + ": " + line + " " + column + " " + (lines.length > 1? lines[line - t.getLine()] : "")); + } + } + } + return result; + } +} diff --git a/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/Util.java b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/Util.java new file mode 100644 index 000000000..dfc1e8035 --- /dev/null +++ b/runtime/lsp4j/src/main/java/edu/umn/cs/melt/lsp4jutil/Util.java @@ -0,0 +1,196 @@ +package edu.umn.cs.melt.lsp4jutil; + +import java.io.IOException; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.net.MalformedURLException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.StandardCopyOption; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +import org.eclipse.lsp4j.Diagnostic; +import org.eclipse.lsp4j.DiagnosticSeverity; +import org.eclipse.lsp4j.Position; +import org.eclipse.lsp4j.Range; + +import common.ConsCell; +import common.DecoratedNode; +import common.SilverCopperParser; +import common.javainterop.ConsCellCollection; +import silver.core.NLocation; +import silver.langutil.NMessage; + +/** + * Generic utilities for LSP server implementations for languages implemented in Silver. + * + * @author krame505 + */ +public class Util { + public static String locationToFile(final NLocation loc) { + DecoratedNode decLoc = loc.decorate(); + return decLoc.synthesized(silver.core.Init.silver_core_filename__ON__silver_core_Location).toString(); + } + + public static Range locationToRange(final NLocation loc) { + DecoratedNode decLoc = loc.decorate(); + return new Range( + new Position( + (int)decLoc.synthesized(silver.core.Init.silver_core_line__ON__silver_core_Location) - 1, + decLoc.synthesized(silver.core.Init.silver_core_column__ON__silver_core_Location)), + new Position( + (int)decLoc.synthesized(silver.core.Init.silver_core_endLine__ON__silver_core_Location) - 1, + decLoc.synthesized(silver.core.Init.silver_core_endColumn__ON__silver_core_Location)) + ); + } + + public static Diagnostic messageToDiagnostic(final NMessage m, final String uri) { + Diagnostic d = new Diagnostic(); + + DecoratedNode decM = m.decorate(); + NLocation loc = decM.synthesized(silver.langutil.Init.silver_langutil_where__ON__silver_langutil_Message); + d.setMessage(decM.synthesized(silver.langutil.Init.silver_langutil_message__ON__silver_langutil_Message).toString()); + + if (uri.endsWith(locationToFile(loc))) { + d.setRange(locationToRange(loc)); + } else { + // The error isn't in the file for which we are generating diagnostics. + // Produce a dummy diagnostic at the beginning of the file containing the actual location in the message. + d.setMessage(decM.synthesized(silver.langutil.Init.silver_langutil_output__ON__silver_langutil_Message).toString()); + d.setRange(new Range(new Position(0, 0), new Position(0, 0))); + } + + switch ((int)decM.synthesized(silver.langutil.Init.silver_langutil_severity__ON__silver_langutil_Message)) { + case 0: + d.setSeverity(DiagnosticSeverity.Information); + break; + case 1: + d.setSeverity(DiagnosticSeverity.Warning); + break; + case 2: + d.setSeverity(DiagnosticSeverity.Error); + break; + } + + return d; + } + + public static List messagesToDiagnostics(final ConsCell ms, final String uri) { + return new ConsCellCollection(ms).stream() + .map((m) -> messageToDiagnostic(m, uri)) + .collect(Collectors.toList()); + } + + /** + * Copy a resource file or directory from a jar to a path. + * + * @param clazz A class from the jar containing the resource + * @param source The resource path within the jar + * @param target The path to which the resource should be copied + * @throws URISyntaxException + * @throws IOException + */ + public static void copyFromJar(Class clazz, String source, final Path target) throws URISyntaxException, IOException { + URI resource = clazz.getResource("").toURI(); + FileSystem fileSystem = FileSystems.newFileSystem(resource, Map.of()); + final Path jarPath = fileSystem.getPath(source); + Files.walkFileTree(jarPath, new SimpleFileVisitor() { + private Path currentTarget; + + @Override + public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException { + currentTarget = target.resolve(jarPath.relativize(dir).toString()); + Files.createDirectories(currentTarget); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.copy(file, target.resolve(jarPath.relativize(file).toString()), StandardCopyOption.REPLACE_EXISTING); + return FileVisitResult.CONTINUE; + } + }); + } + + /** + * Dynamically load a Copper parser from a compiled Silver grammar. + * + * @param The start nonterminal type of the parser + * @param jarPath The path to the jar file to be loaded + * @param name The name of the declared parser, prefixed by its grammar + * @param rootClass The class of the start nonterminal for the parser + * @return A factory object for instantiating the parser + * @throws SecurityException + * @throws ReflectiveOperationException + */ + @SuppressWarnings("unchecked") + public static Supplier> loadCopperParserFactory( + final Path jarPath, final String name, final Class rootClass) + throws SecurityException, ReflectiveOperationException { + // Initialize a class loader for the jar + URLClassLoader loader; + try { + loader = new URLClassLoader(new URL[] {jarPath.toUri().toURL()}, Util.class.getClassLoader()); + } catch (MalformedURLException e) { + throw new RuntimeException(e); + } + + // Load the parser class + String pkg = String.join(".", name.substring(0, Math.max(name.lastIndexOf(":"), 0)).split(":")); + String parserClassName = pkg + ".Parser_" + String.join("_", name.split(":")); + Class parserClass = Class.forName(parserClassName, true, loader); + + // Sanity check: make sure it's actually a Silver-declared Copper parser + if (!SilverCopperParser.class.isAssignableFrom(parserClass)) { + throw new ReflectiveOperationException("Loaded class is not a Silver-generated Copper parser"); + } + + // Hacky way of determining the actual start nonterminal type of the parser, from its superclass generic type args. + Type[] genericParams = ((ParameterizedType)parserClass.getGenericSuperclass()).getActualTypeArguments(); + if (genericParams.length != 2) { + // This should only fail if there is a change to the superclass of Copper-generated parsers. + throw new ReflectiveOperationException("Could not determine parser root type: Parser class should extend SingleDFAEngine"); + } + Class actualRootClass = (Class)genericParams[0]; + + // Check that the start nonterminal is correct + if (!rootClass.isAssignableFrom(actualRootClass)) { + throw new ReflectiveOperationException( + "Loaded parser has the wrong start nonterminal: expected " + rootClass.getName() + ", got " + actualRootClass.getName()); + } + + // Initialize the grammar containing the parser. + // This transitively initializes any dependent grammars, e.g. extensions included in the parser. + Class initClass = Class.forName(pkg + ".Init", true, loader); + initClass.getMethod("initAllStatics").invoke(null); + initClass.getMethod("init").invoke(null); + initClass.getMethod("postInit").invoke(null); + + // Set up the parser factory + Constructor constructor = parserClass.getConstructor(); + return () -> { + try { + // Invoke the constructor. + // This unchecked cast should be safe due to the above sanity checks. + return (SilverCopperParser)constructor.newInstance(); + } catch (InstantiationException | IllegalAccessException | IllegalArgumentException + | InvocationTargetException e) { + throw new RuntimeException("Error instantiating Copper parser", e); + } + }; + } +} diff --git a/sandbox/bools/Bools.sv b/sandbox/bools/Bools.sv index efbd05835..b6bdc0654 100644 --- a/sandbox/bools/Bools.sv +++ b/sandbox/bools/Bools.sv @@ -9,8 +9,8 @@ synthesized attribute condValue :: a; -- Some operators on booleans synthesized attribute not :: Bool; -synthesized attribute and :: Function(Bool ::= Bool); -synthesized attribute or :: Function(Bool ::= Bool); +synthesized attribute and :: (Bool ::= Bool); +synthesized attribute or :: (Bool ::= Bool); -- The introduction forms for booleans diff --git a/self-compile b/self-compile index 9e2b01383..67f90c407 100755 --- a/self-compile +++ b/self-compile @@ -1,17 +1,19 @@ -#!/bin/bash +#!/usr/bin/env bash # Exit on error or undefined variable. Good bash hygiene. set -eu # Defaults overridable by setting an environment variable: -SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx2000M -Xss8M"} +SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx5G -Xss16M"} # These can be fun sometimes: # -XX:+PrintCompilation -verbose:gc -XX:+PrintGCTimeStamps -XX:+PrintGCDetails -XX:-PrintGC -SV_BUILD_TARGET=${SV_BUILD_TARGET:-"silver:composed:Default"} +SV_BUILD_TARGET=${SV_BUILD_TARGET:-"silver:compiler:composed:Default"} + +export ANT_OPTS=-Xss10M mkdir -p build cd build -time java ${SVJVM_FLAGS} -jar ../jars/silver.composed.Default.jar --relative-jar $@ ${SV_BUILD_TARGET} +time java ${SVJVM_FLAGS} -cp ../jars/CopperCompiler.jar -jar ../jars/silver.compiler.composed.Default.jar $@ ${SV_BUILD_TARGET} ant diff --git a/shell.nix b/shell.nix new file mode 100644 index 000000000..e03b6fcf5 --- /dev/null +++ b/shell.nix @@ -0,0 +1,8 @@ +{ pkgs ? import { } }: + +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ ant openjdk_headless wget ]; + shellHook = '' + PATH="$(pwd)/support/bin:$PATH" + ''; +} diff --git a/single-step-rebuild b/single-step-rebuild new file mode 100755 index 000000000..c77a924dc --- /dev/null +++ b/single-step-rebuild @@ -0,0 +1,67 @@ +#!/bin/sh + +set -euo pipefail + +# This script is for applying a simultaneous change to the translation and runtime +# (e.g. renaming something, changing some translation). Let's call the old "ABI" +# (java API) ABI-A and the new one ABI-B. The silver in grammars/silver should be +# updated to produce code using ABI-B, and the runtime in runtime/java should similarly +# be ready to interop with that code. +# +# 1) Start with a working copy of silver and it's runtime (ABI-A) stored in JARS-BAK +# 2) (optionally) apply a patch to silver, the path of which is in $COMPATPATCH +# to make the new ("child") version (which emits code using ABI-B) build with the +# old ("parent") silver +# 3) Deep clean +# 4) Using old silver (ABI-A) build a ONE-JAR version of new silver (which is built +# using ABI-A, but emits code that will use ABI-B) +# 5) (optionally) undo the patch +# 6) Clean out some generated and build silver:core and silver:xml:ast, which are circularly +# required to rebuild the runtime. So now they are built using ABI-B. Ignore javac failure +# 7) Rebuild the runtime for ABI-B +# 8) Install the runtime +# +# After this, jars/silver.*.jar is broken. build/silver.*.jar is a self-contained version of new silver, +# internally using ABI-A with it's bundled ABI-A runtime, but emitting code for ABI-B. +# jars/SilveRuntime.jar is for ABI-B. This means that build/silver.*.jar is ready to compile +# code and emit java code using ABI-B dynamically (i.e. normally) linked to the ABI-B SilverRuntime.jar. +# (So you should be good to run tests now etc.) +# This has the side effect of breaking the old copy of the ABI-A silver (but you can restore it from +# JARS-BAK.) If things work well, just promote your new ABI-B silver to jars/silver.*.jar and run self-compile +# to complete the rebuild cycle, resulting in a silver using ABI-B internally and compiling to ABI-B. + +: ${SVJVM_FLAGS="-Xmx16G -Xss128M"} +: ${COMPATPATCH=""} + +function cleanup () { + if [ -n "$COMPATPATCH" ]; then + echo === APPLY -R $COMPATPATCH === + git apply -R $COMPATPATCH + fi + rm build.xml ||true +} + +trap "echo !!! FAILED !!!; cleanup" EXIT + +echo === RESTORE OLD JARS === +cp JARS-BAK/*.jar jars/ +if [ -n "$COMPATPATCH" ]; then + echo === APPLY $COMPATPATCH === + git apply $COMPATPATCH +fi +echo === DEEP CLEAN === +./deep-clean -delete all +echo === COMPILE WITH OLD SILVER === +mkdir -p build +(cd build; ../support/bin/silver --clean --one-jar silver:compiler:composed:Default) +cleanup +echo === BUILD CHILDRUNTIME === +rm -rf generated/*/silver/core generated/*/silver/xml ||true +(cd build; ../support/bin/silver-custom silver.compiler.composed.Default.jar silver:core ||true) +(cd build; ../support/bin/silver-custom silver.compiler.composed.Default.jar silver:xml:ast ||true) +echo === BUILD JAVA RUNTIME === +(cd runtime/java; ant) +echo === INSTALL NEW RUNTIME === +cp runtime/java/*.jar jars +echo === DONE\? === +trap - EXIT \ No newline at end of file diff --git a/support/bin/debug-silver-jar b/support/bin/debug-silver-jar index e45f66a6f..74c5dbcf5 100755 --- a/support/bin/debug-silver-jar +++ b/support/bin/debug-silver-jar @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu diff --git a/support/bin/install-silver-bin b/support/bin/install-silver-bin index 3f8687b9d..70c064fff 100755 --- a/support/bin/install-silver-bin +++ b/support/bin/install-silver-bin @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu diff --git a/support/bin/silver b/support/bin/silver index 647c5f309..57964d50d 100755 --- a/support/bin/silver +++ b/support/bin/silver @@ -1,11 +1,11 @@ -#!/bin/bash +#!/usr/bin/env bash -case `uname` in +case "$(uname)" in *Darwin*) READLINK=greadlink - if [ ! -f `which greadlink` ]; then + if [ ! -f "$(which greadlink)" ]; then echo "Missing greadlink. Please install coreutils:" - echo -e "\tbrew install coreutils" + printf "\tbrew install coreutils\n" exit 4 fi ;; @@ -15,13 +15,13 @@ case `uname` in esac # ~/bin/silver should be a link to somewhere, find out where! -ME=$(${READLINK} -f "${BASH_SOURCE}") +ME=$(${READLINK} -f "${BASH_SOURCE[0]}") # Set our home export SILVER_HOME=${ME/support\/bin\/silver/} # Find the silver jar -SILVER="$SILVER_HOME/jars/silver.composed.Default.jar" +SILVER="$SILVER_HOME/jars/silver.compiler.composed.Default.jar" if [ ! -f "$SILVER" ]; then echo "Couldn't find the Silver jars to execute, aborting." @@ -29,6 +29,7 @@ if [ ! -f "$SILVER" ]; then fi # Set flags if not overriden in environment -SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx3000M -Xss20M"} +SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx5000M -Xss20M"} +# shellcheck disable=SC2086 java $SVJVM_FLAGS -jar "$SILVER" "$@" && ant diff --git a/support/bin/silver-custom b/support/bin/silver-custom index e2dfc47bc..40de64529 100755 --- a/support/bin/silver-custom +++ b/support/bin/silver-custom @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash case `uname` in *Darwin*) @@ -30,6 +30,6 @@ if [ ! -f "$SILVER" ]; then fi # Set flags if not overriden in environment -SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx3000M -Xss20M"} +SVJVM_FLAGS=${SVJVM_FLAGS:-"-Xmx5000M -Xss20M"} java $SVJVM_FLAGS -jar "$SILVER" "$@" && ant diff --git a/support/emacs/silver-mode/silver-mode.el b/support/emacs/silver-mode/silver-mode.el index 512ba9852..35fc12262 100644 --- a/support/emacs/silver-mode/silver-mode.el +++ b/support/emacs/silver-mode/silver-mode.el @@ -1,6 +1,7 @@ (defvar silver-mode-hook nil) (add-to-list 'auto-mode-alist '("\\.sv\\'" . silver-mode)) +(add-to-list 'auto-mode-alist '("\\.ag\\'" . silver-mode)) (defun silver-comment-dwim (arg) "Comment or uncomment current line or region in a smart way but with silver's -- syntax @@ -14,13 +15,13 @@ For detail, see `comment-dwim'." (list '("[\']\\(\\([\\\\].\\)\\|[^\']\\)*[\']". font-lock-string-face) '("[\/]\\(\\([\\\\].\\)\\|[^\/]\\)*[\/]". font-lock-string-face) - '("\\<\\(production\\|productions\\|occurs\\|on\\|nonterminal\\|attribute\\|propagate\\|if\\|then\\|else\\|option\\|exports\\|close\\|closed\\)\\>" . font-lock-keyword-face) + '("\\<\\(production\\|productions\\|occurs\\|on\\|nonterminal\\|attribute\\|propagate\\|if\\|then\\|else\\|option\\|exports\\|close\\|closed\\|tracked\\)\\>" . font-lock-keyword-face) '("\\<\\(local\\|global\\|terminal\\|concrete\\|abstract\\|default\\|ignore\\|start\\|lexer\\|class\\|classes\\|dominates\\|submits\\|aspect\\|decorate\\|autocopy\\)\\>" . font-lock-keyword-face) - '("\\<\\(import\\|imports\\|grammar\\|association\\|precedence\\|synthesized\\|inherited\\|functor\\|monoid\\|with\\|as\\|include\\)\\>" . font-lock-keyword-face) - '("\\<\\(only\\|hiding\\|using\\|forwards\\|to\\|use\\|syntax\\|forwarding\\|function\\|return\\)\\>" . font-lock-keyword-face) - '("\\<\\(Integer*\\|Boolean\\|String\\|Float\\|Reference\\|Production\\|Decorated\\)\\>" . font-lock-type-face) + '("\\<\\(import\\|imports\\|grammar\\|association\\|precedence\\|synthesized\\|inherited\\|functor\\|monoid\\|strategy\\|with\\|as\\|include\\)\\>" . font-lock-keyword-face) + '("\\<\\(only\\|hiding\\|using\\|forwards\\|to\\|use\\|syntax\\|forwarding\\|function\\|return\\|instance\\|flowtype\\|let\\|do\\)\\>" . font-lock-keyword-face) + '("\\<\\(Integer*\\|Boolean\\|String\\|Float\\|Decorated\\)\\>" . font-lock-type-face) '("\\<\\(reference\\|length\\|substring\\|indexof\\|parser\\|error\\|cast\\|toInt\\|toReal\\|toString\\|new\\)\\>" . font-lock-builtin-face) - '("\\<\\(cons\\|null\\|head\\|tail\\|let\\)\\>" . font-lock-builtin-face) + '("\\<\\(cons\\|null\\|head\\|tail\\)\\>" . font-lock-builtin-face) '("\\<\\(true\\|false\\|left\\|right\\|none\\)\\>" . font-lock-constant-face) '("\\<\\(case\\|rule\\|of\\|end\\)\\>" . font-lock-keyword-face) '("\\" 1 font-lock-function-name-face) @@ -38,7 +39,6 @@ For detail, see `comment-dwim'." '("\\" 1 font-lock-type-face) '("\\<\\<\\(type\\)\\>\\ [A-Za-z_0-9:]+" 1 font-lock-keyword-face) ; '("\\" 1 font-lock-type-face) - '("\\" 1 font-lock-type-face) '("\\" 1 font-lock-type-face) '("\\<\\(reference\\)\\>([A-Za-z_0-9:\\ \n\t\r]+" 1 font-lock-builtin-face t) '("\\") + return NT(x) + + if x[0] in cache: + return cache[x[0]] + else: + r = _translate(x) + cache[x[0]] = r + return r + +class SilverValue: + def has_as_child(self, x): + return False + + def is_origins_impl_value(self): + return False + + def node_color(self): + return None + +class ComplexValue(SilverValue): + pass + +class NT(ComplexValue): + def __init__(self, pexpr): + self.ids = pexpr[0] + self.name = pexpr[1] + self.children = list(map(translate, pexpr[2])) + + self.origin = None + self.redex = None + self.newly_introduced = True + o = translate(pexpr[4]) + self.oi = o + self.comment = o.node_text(False) + if o is None or (isinstance(o, PrimitiveValue) and o.value is None): + return + if o.name=="silver:core:otherOriginInfo" or o.name=="silver:core:parsedOriginInfo": + return + # print() + # print(self.name) + # print(o.children[2].value) + self.origin, self.originlabel = o.children[1],\ + "\n".join(map(lambda x:x.node_text(inclo=False), o.children[2].value)) + if len(o.children)>4: + self.redex, self.redexlabel = o.children[3],\ + "\n".join(map(lambda x:x.node_text(inclo=False), o.children[4].value)) + self.newly_introduced = o.children[-1].value + + def get_real_children(self): + return list(filter(lambda x:not isinstance(x, PrimitiveValue), self.children)) + + def has_as_child(self, x): + return x in self.children or any(map(lambda c:c.has_as_child(x), self.children)) + + def node_text(self, inclo=True): + # r=self.name.split(":")[-1]+"(" + if self.name=="silver:core:originDbgNote": + return ":".join(self.children[0].value[len("silver:core:loc("):].replace("\"","").replace(" ","").split(",")[0:3]) + r=(self.name.split(":",1)[1] if strip_packagename else self.name)+"(" + for c in self.children: + if isinstance(c, PrimitiveValue) or isinstance(c, LocationNT): + r+=c.node_text(inclo=False) + else: + r+="_" + if c!=self.children[-1]: + r += "," + r+=")" + if inclo: + if small_nodes: + if isinstance(self.oi, NT): + r+="\\n"+{ + "silver:core:originAndRedexOriginInfo": "OR", + "silver:core:originOriginInfo": "O", + "silver:core:parsedOriginInfo": "P", + "silver:core:otherOriginInfo":"X"}[self.oi.name] + r+=";"+{ + "silver:core:setInGlobalOIT": "G", + "silver:core:setAtConstructionOIT": "C", + "silver:core:setAtAccessOIT": "A", + "silver:core:setAtForwardingOIT": "F", + "silver:core:setFromParserOIT": "P", + "silver:core:setFromEntryOIT": "E", + "silver:core:setFromReflectionOIT": "R", + "silver:core:setFromReificationOIT": "I", + "silver:core:setAtNewOIT": "N"}[self.oi.children[0].name] + else: + r+="\\n"+"P"+";"+self.oi.node_text(inclo=False) + else: + r+="\\n"+self.comment + return r + + def is_origins_impl_value(self): + return any(x in self.name for x in ["OriginInfo", "loc", "originLink", "Note"]) or self.name.endswith("OIT") + +class LocationNT(NT): + def node_text(self, inclo=True): + return "loc("+":".join(map(lambda x:str(x.value), self.children[:2]))+")" + +class Token(ComplexValue): + def __init__(self, pexpr): + self.ids = pexpr[0] + self.name = pexpr[2] + self.lexeme = pexpr[3] + self.loc = translate(pexpr[4]) + self.origin = None + self.redex = None + self.newly_introduced = True + + def node_text(self, inclo=True): + return self.name.split(":")[-1]+"<"+repr(self.lexeme).replace("\"","\\\"")+">\n"+self.loc.node_text() + + def node_color(self): + return "grey" + +class PrimitiveValue(SilverValue): + def __init__(self, ids, value): + self.ids = ids + self.value = value + self.origin = None + self.redex = None + self.newly_introduced = False + + def node_text(self, inclo=True): + return repr(self.value).replace("\"","\\\"") + +class PrimitiveList(PrimitiveValue): + def has_as_child(self, x): + return x in self.value or any(map(lambda c:c.has_as_child(x), self.value)) + + def node_text(self, inclo=True): + return "["+", ".join(map(lambda x:x.node_text(), self.value))+"]" + +def primitive(x): + return isinstance(x, PrimitiveValue) +def notprimitive(x): + return not isinstance(x, PrimitiveValue) + +while True: + x=input() + if x == "---SVDRAW2 START---": break + print(">", x) + +true, false = True, False +start = eval(input()) +end = eval(input()) +assert input()=="---SVDRAW2 END---" + +print("\n"*3) + +print("evaling pexprs...") +start = translate(start) +end = translate(end) + +os.chdir(os.path.dirname(__file__)) + +fd = open("out.dot", 'w') +w = lambda x: fd.write(x+"\n") +w("digraph{") +w("node [fontname = \"monospace\"];") +w("graph [compound=true ") +# w("rankdir=TB ") +w("splines=spline];") + +print("finding roots...") +# roots = list(filter(lambda x:all(map(lambda o:not o.has_as_child(x), cache.values())) and not x.is_origins_impl_value(), +# cache.values())) +roots = [] +for v in cache.values(): + if not v.is_origins_impl_value(): + for o in cache.values(): + if not o.is_origins_impl_value(): + if o.has_as_child(v): + break + else: + if isinstance(v, ComplexValue) and (allow_c or not v.name.endswith("_c")): + roots.append(v) + +def draw_only_children(node, color=None): + if node is start: color="#bbbbff" + if node is end: color="#bbffbb" + effcolor = node.node_color() or color or "white" + w("n"+str(node.ids)+" [label=\""+node.node_text()+"\" style=filled fillcolor=\""+effcolor+"\"") + if node is start or node is end: + w(" penwidth=3 ") + if node.newly_introduced: + w(" shape=diamond ") + w("];") + if not isinstance(node, NT): return + w("subgraph children"+str(node.ids)+"{") + kids = node.get_real_children() + for child in kids: + if not (allow_c or not node.name.endswith("_c")): continue + draw_only_children(child, color) + w("n"+str(node.ids)+" -> n"+str(child.ids)+" [label="+str(node.children.index(child))+" arrowhead=none];") + if child!=kids[-1]: + next = kids[kids.index(child)+1] + w("n"+str(node.ids)+" -> n"+str(child.ids)+" [style=invisible arrowhead=none];") + w("}") + +print("drawing object graph...") + +for thing in filter(lambda x:isinstance(x, ComplexValue), roots): + w("subgraph cluster"+str(thing.ids)+"{") + draw_only_children(thing) + w("}") + +def propogate_redex(node, redex_haver, root=True): + if do_partial_redex_prop: w("n"+str(node.ids)+" -> n"+str(redex_haver.ids)+" [style=dotted penwidth=6 arrowsize=0.25 ];") + w("n"+str(node.ids)+" -> n"+str(redex_haver.redex.ids)+" [style=dotted, label=\""+(redex_haver.redexlabel if root or allow_multiredex else "")+"\"];") + if isinstance(node, NT): + for x in node.get_real_children(): + if x.redex is None or allow_multiredex: + if do_partial_redex_prop: propogate_redex(x, redex_haver, False) + +print("adding origin information...") + +for thing in cache.values(): + if thing.origin: + if not (allow_c or not thing.origin.name.endswith("_c")) or thing.is_origins_impl_value(): continue + w("n"+str(thing.ids)+" -> n"+str(thing.origin.ids)+" [style=dashed, label=\""+thing.originlabel+"\"];") + + if thing.redex: + if not (allow_c or not thing.origin.name.endswith("_c")): continue + propogate_redex(thing, thing) + + +w("}") + +fd.close() + +print("rendering...") + +os.system("dot -Tsvg -o out.svg out.dot") \ No newline at end of file diff --git a/support/profile/run.sh b/support/profile/run.sh index bd113a7eb..f415c7472 100755 --- a/support/profile/run.sh +++ b/support/profile/run.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash if [ ! -d jars ]; then echo "Run from silver root as './support/profile/run.sh'" @@ -8,14 +8,14 @@ fi mkdir -p build cd build -java -Xss8M -Xmx3000M -Xrunhprof:heap=sites,cpu=samples -jar ../jars/silver.composed.Default.jar --clean silver:composed:Default +java -Xss8M -Xmx3000M -Xrunhprof:heap=sites,cpu=samples -jar ../jars/silver.compiler.composed.Default.jar --clean silver:compiler:composed:Default # Heap profile: -#java -Xss8M -Xmx3000M -Xrunhprof:heap=sites,depth=2 -jar ../jars/silver.composed.Default.jar --clean silver:composed:Default +#java -Xss8M -Xmx3000M -Xrunhprof:heap=sites,depth=2 -jar ../jars/silver.compiler.composed.Default.jar --clean silver:compiler:composed:Default # Deep CPU stack profile: -#java -Xss8M -Xmx3000M -Xrunhprof:cpu=samples,depth=12 -jar ../jars/silver.composed.Default.jar --clean silver:composed:Default +#java -Xss8M -Xmx3000M -Xrunhprof:cpu=samples,depth=12 -jar ../jars/silver.compiler.composed.Default.jar --clean silver:compiler:composed:Default # Kinda useless CPU profile -#java -Xss8M -Xmx3000M -Xrunhprof:cpu=samples,depth=1,interval=1 -jar ../jars/silver.composed.Default.jar --clean silver:composed:Default +#java -Xss8M -Xmx3000M -Xrunhprof:cpu=samples,depth=1,interval=1 -jar ../jars/silver.compiler.composed.Default.jar --clean silver:compiler:composed:Default diff --git a/support/scripts/sv-edit-erroring-files b/support/scripts/sv-edit-erroring-files new file mode 100755 index 000000000..ed78625f2 --- /dev/null +++ b/support/scripts/sv-edit-erroring-files @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +set -eu + +# try calling ./support/scripts/sv-edit-erroring-files tmux split-window -v nvim + +cd "$(realpath "$(dirname "$(realpath "${BASH_SOURCE[0]}")")"/../..)" + +./self-compile | tee /dev/stderr | grep -o '^ \[.*\]$' | sed 's/ \[\(.*\)\]/\1/' | xargs "${@:-$EDITOR}" diff --git a/support/scripts/sv-grep b/support/scripts/sv-grep index 1a51a7427..e658696f3 100755 --- a/support/scripts/sv-grep +++ b/support/scripts/sv-grep @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # A simple utility to grep all .sv files below the currect directory. diff --git a/support/scripts/sv-make-flowtype-decl b/support/scripts/sv-make-flowtype-decl index 8a0468a05..b3a1f3973 100755 --- a/support/scripts/sv-make-flowtype-decl +++ b/support/scripts/sv-make-flowtype-decl @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # A simple utility to generate a skeleton flowtype declaration for an attribute. diff --git a/support/scripts/sv-name-grep b/support/scripts/sv-name-grep index a8d74b116..8e21946db 100755 --- a/support/scripts/sv-name-grep +++ b/support/scripts/sv-name-grep @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # A simple utility to grep all .sv files below the currect directory # for *WHOLE WORDS* only. diff --git a/support/scripts/sv-name-replace b/support/scripts/sv-name-replace index e4f2583d2..0b2a33622 100755 --- a/support/scripts/sv-name-replace +++ b/support/scripts/sv-name-replace @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Quick and dirty *whole-word* find and replace in .sv files only. diff --git a/support/scripts/sv-replace b/support/scripts/sv-replace index dffc31c53..dc55a5b46 100755 --- a/support/scripts/sv-replace +++ b/support/scripts/sv-replace @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # Quick and dirty search and replace in .sv files only. diff --git a/support/vim/ftdetect/sv.vim b/support/vim/ftdetect/sv.vim index 05f1fde37..6b50d88b1 100644 --- a/support/vim/ftdetect/sv.vim +++ b/support/vim/ftdetect/sv.vim @@ -1 +1,2 @@ au BufRead,BufNewFile *.sv set filetype=sv +au BufRead,BufNewFile *.ag set filetype=sv diff --git a/support/vim/install-neovim.sh b/support/vim/install-neovim.sh index 14b264599..4f0088bb2 100755 --- a/support/vim/install-neovim.sh +++ b/support/vim/install-neovim.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu diff --git a/support/vim/install.sh b/support/vim/install.sh index ac6d7185c..7340ffad7 100755 --- a/support/vim/install.sh +++ b/support/vim/install.sh @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash # stop on error set -eu diff --git a/support/vim/syntax/sv.vim b/support/vim/syntax/sv.vim index e135cf324..13f1b9f34 100644 --- a/support/vim/syntax/sv.vim +++ b/support/vim/syntax/sv.vim @@ -10,7 +10,7 @@ endif syn keyword svlangExternals grammar import imports exports build hiding as only option -syn keyword svlangDeclarations parser attribute annotation function local closed nonterminal type occurs on production terminal marking foreign layout disambiguate action global productions lexer class default propagate +syn keyword svlangDeclarations parser attribute annotation function local closed nonterminal type occurs on production terminal marking foreign layout disambiguate action global productions lexer class default propagate tracked syn keyword svlangPrimitiveTypes IO Boolean Decorated Float Function Integer Production String @@ -22,9 +22,7 @@ syn keyword svlangFlowOther forwarding forwards to return pluck syn keyword svlangFlow case rule of let in end decorate with prefix else forward if new then -syn keyword svlangFunction print toString toInt toFloat length reference substring indexOf error cast left right partitionEithers hackUnparse print readLineStdin exit mkdir system writeFile appendFile fileTime isFile isDirectory readFile cwd envVar listContents deleteFile deleteTree copyFile touchFile error unsafeIO genInt genRand unsafeTrace dirNameInFilePath fileNameInFilePath splitFileNameAndExtension map foldr foldl foldr1 foldl1 filter partition containsBy nubBy removeBy removeAllBy last drop take dropWhile takeWhile takeUntil positionOf positionOfHelper repeat zipWith reverse sortBy groupBy intersperse unionBy intersectBy unionsBy nil cons append null listLength head tail locationLte fromMaybe orElse consMaybe catMaybes fst snd lookupBy lookupAllBy unzipPairs parseTreeOrDieWithoutStackTrace implode explode indexOf lastIndexOf substring startsWith endsWith substitute replicate isDigit isAlpha isSpace isLower isUpper toIntSafe compareString stringConcat stringEq stringLte runIO evalIO unsafeEvalIO bindList returnList bindMaybe returnMaybe runState evalState ioval - -"syn keyword svlangIde temp_imp_ide_dcl temp_imp_ide_font font color italic bold +syn keyword svlangFunction print toString toInteger toFloat length reference substring indexOf error cast left right partitionEithers hackUnparse print readLineStdin exit mkdir system writeFile appendFile fileTime isFile isDirectory readFile cwd envVar listContents deleteFile deleteTree copyFile touchFile error unsafeIO genInt genRand unsafeTrace dirNameInFilePath fileNameInFilePath splitFileNameAndExtension map foldr foldl foldr1 foldl1 filter partition containsBy nubBy removeBy removeAllBy last drop take dropWhile takeWhile takeUntil positionOf positionOfHelper repeat zipWith reverse sortBy groupBy intersperse unionBy intersectBy unionsBy nil cons append null listLength head tail locationLte fromMaybe orElse consMaybe catMaybes fst snd lookupBy lookupAllBy unzipPairs parseTreeOrDieWithoutStackTrace implode explode indexOf lastIndexOf substring startsWith endsWith substitute replicate isDigit isAlpha isSpace isLower isUpper toIntSafe compareString stringConcat stringEq stringLte runIO evalIO unsafeEvalIO bindList returnList bindMaybe returnMaybe runState evalState ioval "syn keyword svlangIdeInner contained builder postbuilder exporter folder property string required string wizard new file stub generator product name version option resource diff --git a/support/vs-code/silverlsp/.eslintrc.json b/support/vs-code/silverlsp/.eslintrc.json new file mode 100644 index 000000000..f9b22b793 --- /dev/null +++ b/support/vs-code/silverlsp/.eslintrc.json @@ -0,0 +1,24 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint" + ], + "rules": { + "@typescript-eslint/naming-convention": "warn", + "@typescript-eslint/semi": "warn", + "curly": "warn", + "eqeqeq": "warn", + "no-throw-literal": "warn", + "semi": "off" + }, + "ignorePatterns": [ + "out", + "dist", + "**/*.d.ts" + ] +} diff --git a/support/vs-code/silverlsp/.gitignore b/support/vs-code/silverlsp/.gitignore new file mode 100644 index 000000000..1581edf21 --- /dev/null +++ b/support/vs-code/silverlsp/.gitignore @@ -0,0 +1,3 @@ +node_modules +out +*.vsix \ No newline at end of file diff --git a/support/vs-code/silverlsp/.vscode/extensions.json b/support/vs-code/silverlsp/.vscode/extensions.json new file mode 100644 index 000000000..3ac9aeb61 --- /dev/null +++ b/support/vs-code/silverlsp/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "dbaeumer.vscode-eslint" + ] +} diff --git a/support/vs-code/silverlsp/.vscode/launch.json b/support/vs-code/silverlsp/.vscode/launch.json new file mode 100644 index 000000000..670d6e66c --- /dev/null +++ b/support/vs-code/silverlsp/.vscode/launch.json @@ -0,0 +1,34 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + }, + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/suite/index" + ], + "outFiles": [ + "${workspaceFolder}/out/test/**/*.js" + ], + "preLaunchTask": "${defaultBuildTask}" + } + ] +} diff --git a/support/vs-code/silverlsp/.vscode/settings.json b/support/vs-code/silverlsp/.vscode/settings.json new file mode 100644 index 000000000..30bf8c2d3 --- /dev/null +++ b/support/vs-code/silverlsp/.vscode/settings.json @@ -0,0 +1,11 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false // set this to true to hide the "out" folder with the compiled JS files + }, + "search.exclude": { + "out": true // set this to false to include "out" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} \ No newline at end of file diff --git a/support/vs-code/silverlsp/.vscode/tasks.json b/support/vs-code/silverlsp/.vscode/tasks.json new file mode 100644 index 000000000..3b17e53b6 --- /dev/null +++ b/support/vs-code/silverlsp/.vscode/tasks.json @@ -0,0 +1,20 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "type": "npm", + "script": "watch", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} diff --git a/support/vs-code/silverlsp/.vscodeignore b/support/vs-code/silverlsp/.vscodeignore new file mode 100644 index 000000000..389996760 --- /dev/null +++ b/support/vs-code/silverlsp/.vscodeignore @@ -0,0 +1,10 @@ +.vscode/** +.vscode-test/** +src/** +.gitignore +.yarnrc +vsc-extension-quickstart.md +**/tsconfig.json +**/.eslintrc.json +**/*.map +**/*.ts diff --git a/support/vs-code/silverlsp/CHANGELOG.md b/support/vs-code/silverlsp/CHANGELOG.md new file mode 100644 index 000000000..e0b752498 --- /dev/null +++ b/support/vs-code/silverlsp/CHANGELOG.md @@ -0,0 +1,16 @@ +# Change Log + +All notable changes to the "silverlsp" extension will be documented in this file. + +Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file. + +## [Unreleased] + +## [0.0.2] +- Add support for specifying a jar containing an alternate Silver parser +- Fix issue with semantic tokens for lines contianing tabs +- Internal refactoring to move reusable lsp4j utilities +- Fixes in the flow analysis and build process corresponding to changes in the forthcoming 0.4.5 release of Silver + +# [0.0.1] +- Initial release \ No newline at end of file diff --git a/support/vs-code/silverlsp/LICENSE.txt b/support/vs-code/silverlsp/LICENSE.txt new file mode 100644 index 000000000..0a041280b --- /dev/null +++ b/support/vs-code/silverlsp/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/support/vs-code/silverlsp/README.md b/support/vs-code/silverlsp/README.md new file mode 100644 index 000000000..8cd9a11e1 --- /dev/null +++ b/support/vs-code/silverlsp/README.md @@ -0,0 +1,27 @@ +# silverlsp README + +This extension provides language server protocol-based editor features for [Silver](https://melt.cs.umn.edu/silver). + +## Features + +* Semantic token-based syntax highlighting, powered by [Copper](https://melt.cs.umn.edu/copper) +* Diagnostic error reporting +* View / jump to declaration +* Clean rebuild command + +## Requirements + +Running this extension requires Java 11. Issues have been noted with newer versions of Java. + +## Extension Settings + +This extension contributes the following settings: + +* `silver.enableMWDA`: Enable the modular well-definedness analysis +* `silver.jvmArgs`: JVM flags to use when launching the language server, the heap size may need to be increased for large projects +* `silver.compilerJar`: Path to a jar file containing an alternate version of the Silver compiler +* `silver.parserName`: Full name of the Silver parser to use, must be set if compiler jar is specified + +## Known Issues + +The use of Copper for semantic tokens means that in case of a syntax error, highlighting is not shown for the rest of the file after the syntax error. diff --git a/support/vs-code/silverlsp/launcher/launcher.jar b/support/vs-code/silverlsp/launcher/launcher.jar new file mode 120000 index 000000000..70bd772b8 --- /dev/null +++ b/support/vs-code/silverlsp/launcher/launcher.jar @@ -0,0 +1 @@ +../../../../language-server/launcher/target/launcher.jar \ No newline at end of file diff --git a/support/vs-code/silverlsp/package-lock.json b/support/vs-code/silverlsp/package-lock.json new file mode 100644 index 000000000..d5a20b0b0 --- /dev/null +++ b/support/vs-code/silverlsp/package-lock.json @@ -0,0 +1,4384 @@ +{ + "name": "silverlsp", + "version": "0.0.2", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "silverlsp", + "version": "0.0.2", + "license": "LGPL-3.0-or-later", + "dependencies": { + "vscode-languageclient": "8.0.1" + }, + "devDependencies": { + "@types/glob": "^7.2.0", + "@types/mocha": "^9.1.1", + "@types/node": "14.x", + "@types/vscode": "^1.67.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "@vscode/test-electron": "^2.1.3", + "eslint": "^8.14.0", + "glob": "^8.0.1", + "mocha": "^9.2.2", + "typescript": "^4.6.4" + }, + "engines": { + "vscode": "^1.67.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", + "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", + "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "node_modules/@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "14.18.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.31.tgz", + "integrity": "sha512-vQAnaReSQkEDa8uwAyQby8bYGKu84R/deEc6mg5T8fX6gzCn8QW6rziSgsti1fNvsrswKUKPnVTi7uoB+u62Mw==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.72.0.tgz", + "integrity": "sha512-WvHluhUo+lQvE3I4wUagRpnkHuysB4qSyOQUyIAS9n9PYMJjepzTUD8Jyks0YeXoPD0UGctjqp2u84/b3v6Ydw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz", + "integrity": "sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/type-utils": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.39.0.tgz", + "integrity": "sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz", + "integrity": "sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz", + "integrity": "sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.39.0.tgz", + "integrity": "sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "node_modules/@vscode/test-electron": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.1.5.tgz", + "integrity": "sha512-O/ioqFpV+RvKbRykX2ItYPnbcZ4Hk5V0rY4uhQjQTLhGL9WZUvS7exzuYQCCI+ilSqJpctvxq2llTfGXf9UnnA==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" + } + }, + "node_modules/acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "dependencies": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "node_modules/buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true, + "engines": { + "node": ">=0.2.0" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "dependencies": { + "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "dependencies": { + "readable-stream": "^2.0.2" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.3.2", + "@humanwhocodes/config-array": "^0.10.5", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true, + "engines": { + "node": ">=4.x" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "bin": { + "he": "bin/he" + } + }, + "node_modules/http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "dependencies": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "dependencies": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/glob/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "dependencies": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/vscode-jsonrpc": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz", + "integrity": "sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/vscode-languageclient": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz", + "integrity": "sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==", + "dependencies": { + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.1" + }, + "engines": { + "vscode": "^1.67.0" + } + }, + "node_modules/vscode-languageserver-protocol": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz", + "integrity": "sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==", + "dependencies": { + "vscode-jsonrpc": "8.0.1", + "vscode-languageserver-types": "3.17.1" + } + }, + "node_modules/vscode-languageserver-types": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz", + "integrity": "sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + }, + "dependencies": { + "@eslint/eslintrc": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.3.2.tgz", + "integrity": "sha512-AXYd23w1S/bv3fTs3Lz0vjiYemS08jWkI3hYyS9I1ry+0f+Yjs1wm+sU0BS8qDOPrBIkp4qHYC16I8uVtpLajQ==", + "dev": true, + "requires": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.15.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + } + }, + "@humanwhocodes/config-array": { + "version": "0.10.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz", + "integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/gitignore-to-minimatch": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", + "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", + "dev": true + }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + } + }, + "@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true + }, + "@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "requires": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + } + }, + "@tootallnate/once": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", + "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", + "dev": true + }, + "@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "requires": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true + }, + "@types/mocha": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", + "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", + "dev": true + }, + "@types/node": { + "version": "14.18.31", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.31.tgz", + "integrity": "sha512-vQAnaReSQkEDa8uwAyQby8bYGKu84R/deEc6mg5T8fX6gzCn8QW6rziSgsti1fNvsrswKUKPnVTi7uoB+u62Mw==", + "dev": true + }, + "@types/vscode": { + "version": "1.72.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.72.0.tgz", + "integrity": "sha512-WvHluhUo+lQvE3I4wUagRpnkHuysB4qSyOQUyIAS9n9PYMJjepzTUD8Jyks0YeXoPD0UGctjqp2u84/b3v6Ydw==", + "dev": true + }, + "@typescript-eslint/eslint-plugin": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.39.0.tgz", + "integrity": "sha512-xVfKOkBm5iWMNGKQ2fwX5GVgBuHmZBO1tCRwXmY5oAIsPscfwm2UADDuNB8ZVYCtpQvJK4xpjrK7jEhcJ0zY9A==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/type-utils": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/parser": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.39.0.tgz", + "integrity": "sha512-PhxLjrZnHShe431sBAGHaNe6BDdxAASDySgsBCGxcBecVCi8NQWxQZMcizNA4g0pN51bBAn/FUfkWG3SDVcGlA==", + "dev": true, + "requires": { + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "debug": "^4.3.4" + } + }, + "@typescript-eslint/scope-manager": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.39.0.tgz", + "integrity": "sha512-/I13vAqmG3dyqMVSZPjsbuNQlYS082Y7OMkwhCfLXYsmlI0ca4nkL7wJ/4gjX70LD4P8Hnw1JywUVVAwepURBw==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.39.0.tgz", + "integrity": "sha512-KJHJkOothljQWzR3t/GunL0TPKY+fGJtnpl+pX+sJ0YiKTz3q2Zr87SGTmFqsCMFrLt5E0+o+S6eQY0FAXj9uA==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "5.39.0", + "@typescript-eslint/utils": "5.39.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/types": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.39.0.tgz", + "integrity": "sha512-gQMZrnfEBFXK38hYqt8Lkwt8f4U6yq+2H5VDSgP/qiTzC8Nw8JO3OuSUOQ2qW37S/dlwdkHDntkZM6SQhKyPhw==", + "dev": true + }, + "@typescript-eslint/typescript-estree": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.39.0.tgz", + "integrity": "sha512-qLFQP0f398sdnogJoLtd43pUgB18Q50QSA+BTE5h3sUxySzbWDpTSdgt4UyxNSozY/oDK2ta6HVAzvGgq8JYnA==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/visitor-keys": "5.39.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + } + }, + "@typescript-eslint/utils": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.39.0.tgz", + "integrity": "sha512-+DnY5jkpOpgj+EBtYPyHRjXampJfC0yUZZzfzLuUWVZvCuKqSdJVC8UhdWipIw7VKNTfwfAPiOWzYkAwuIhiAg==", + "dev": true, + "requires": { + "@types/json-schema": "^7.0.9", + "@typescript-eslint/scope-manager": "5.39.0", + "@typescript-eslint/types": "5.39.0", + "@typescript-eslint/typescript-estree": "5.39.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0" + } + }, + "@typescript-eslint/visitor-keys": { + "version": "5.39.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.39.0.tgz", + "integrity": "sha512-yyE3RPwOG+XJBLrhvsxAidUgybJVQ/hG8BhiJo0k8JSAYfk/CshVcxf0HwP4Jt7WZZ6vLmxdo1p6EyN3tzFTkg==", + "dev": true, + "requires": { + "@typescript-eslint/types": "5.39.0", + "eslint-visitor-keys": "^3.3.0" + } + }, + "@ungap/promise-all-settled": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", + "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", + "dev": true + }, + "@vscode/test-electron": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.1.5.tgz", + "integrity": "sha512-O/ioqFpV+RvKbRykX2ItYPnbcZ4Hk5V0rY4uhQjQTLhGL9WZUvS7exzuYQCCI+ilSqJpctvxq2llTfGXf9UnnA==", + "dev": true, + "requires": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + } + }, + "acorn": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", + "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "dev": true + }, + "acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "requires": {} + }, + "agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "requires": { + "debug": "4" + } + }, + "ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "dev": true + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true + }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "anymatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", + "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "dev": true, + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "big-integer": { + "version": "1.6.51", + "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz", + "integrity": "sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==", + "dev": true + }, + "binary": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", + "integrity": "sha512-D4H1y5KYwpJgK8wk1Cue5LLPgmwHKYSChkbspQg5JtVuR5ulGckxfR62H3AE9UDkdMC8yyXlqYihuz3Aqg2XZg==", + "dev": true, + "requires": { + "buffers": "~0.1.1", + "chainsaw": "~0.1.0" + } + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true + }, + "bluebird": { + "version": "3.4.7", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", + "integrity": "sha512-iD3898SR7sWVRHbiQv+sHUtHnMvC1o3nW5rAcqnq3uOn07DSAppZYUkIGslDz6gXC7HfunPe7YVBgoEJASPcHA==", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "requires": { + "fill-range": "^7.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true + }, + "buffer-indexof-polyfill": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", + "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", + "dev": true + }, + "buffers": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", + "integrity": "sha512-9q/rDEGSb/Qsvv2qvzIzdluL5k7AaJOTrw23z9reQthrbF7is4CtlT0DXyO1oei2DCp4uojjzQ7igaSHp1kAEQ==", + "dev": true + }, + "callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true + }, + "chainsaw": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", + "integrity": "sha512-75kWfWt6MEKNC8xYXIdRpDehRYY/tNSgwKaJq+dbbDcxORuVrrQ+SEHoWsniVn9XPYfP4gmdWIeDk/4YNp1rNQ==", + "dev": true, + "requires": { + "traverse": ">=0.3.0 <0.4" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, + "cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "requires": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true + }, + "deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "dev": true + }, + "dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "requires": { + "path-type": "^4.0.0" + } + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "duplexer2": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", + "integrity": "sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==", + "dev": true, + "requires": { + "readable-stream": "^2.0.2" + } + }, + "emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true + }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, + "eslint": { + "version": "8.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", + "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "dev": true, + "requires": { + "@eslint/eslintrc": "^1.3.2", + "@humanwhocodes/config-array": "^0.10.5", + "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@humanwhocodes/module-importer": "^1.0.1", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.1", + "globals": "^13.15.0", + "globby": "^11.1.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "dependencies": { + "eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "requires": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^2.0.0" + }, + "dependencies": { + "eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true + } + } + }, + "eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true + }, + "espree": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", + "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "dev": true, + "requires": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + } + }, + "esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "requires": { + "estraverse": "^5.1.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "requires": { + "estraverse": "^5.2.0" + }, + "dependencies": { + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + } + } + }, + "estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "requires": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } + } + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "fastq": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "dev": true, + "requires": { + "reusify": "^1.0.4" + } + }, + "file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "requires": { + "flat-cache": "^3.0.4" + } + }, + "fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true + }, + "flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "requires": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + } + }, + "flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "optional": true + }, + "fstream": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", + "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "dev": true, + "requires": { + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + } + } + }, + "get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true + }, + "glob": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", + "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", + "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, + "glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "requires": { + "is-glob": "^4.0.3" + } + }, + "globals": { + "version": "13.17.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz", + "integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==", + "dev": true, + "requires": { + "type-fest": "^0.20.2" + } + }, + "globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "requires": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", + "dev": true + }, + "grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", + "dev": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true + }, + "http-proxy-agent": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", + "dev": true, + "requires": { + "@tootallnate/once": "1", + "agent-base": "6", + "debug": "4" + } + }, + "https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "dev": true + }, + "import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true + }, + "is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true + }, + "is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "js-sdsl": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.1.5.tgz", + "integrity": "sha512-08bOAKweV2NUC1wqTtf3qZlnpOX/R2DU9ikpjOHs0H+ibQv3zpncVQg6um4uYtRtrwIX8M4Nh3ytK4HGlYAq7Q==", + "dev": true + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + } + }, + "listenercount": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", + "integrity": "sha512-3mk/Zag0+IJxeDrxSgaDPy4zZ3w05PRZeJNnlWhzFz5OkX49J4krc+A8X2d2M69vGMBEX0uyl8M+W+8gH+kBqQ==", + "dev": true + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + } + }, + "lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "requires": { + "yallist": "^4.0.0" + } + }, + "merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true + }, + "micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "requires": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + } + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", + "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", + "dev": true + }, + "mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "requires": { + "minimist": "^1.2.6" + } + }, + "mocha": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-9.2.2.tgz", + "integrity": "sha512-L6XC3EdwT6YrIk0yXpavvLkn8h+EU+Y5UcCHKECyMbdUIxyMuZj4bX4U9e1nvnvUUvQVsV2VHQr5zLdcUkhW/g==", + "dev": true, + "requires": { + "@ungap/promise-all-settled": "1.1.2", + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.3", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "4.2.1", + "ms": "2.1.3", + "nanoid": "3.3.1", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "which": "2.0.2", + "workerpool": "6.2.0", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "dependencies": { + "debug": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.3.tgz", + "integrity": "sha512-/zxw5+vh1Tfv+4Qn7a5nsbcJKPaSvCDhojn6FEl9vupwK2VCSDtEiEtqr8DFtzYFOdz63LBkxec7DYuc2jon6Q==", + "dev": true, + "requires": { + "ms": "2.1.2" + }, + "dependencies": { + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + } + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "dependencies": { + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + } + } + }, + "minimatch": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-4.2.1.tgz", + "integrity": "sha512-9Uq1ChtSZO+Mxa/CL1eGizn2vRn3MlLgzhT0Iz8zaY8NdvxvB0d5QdPFmCKf7JKA9Lerx5vRrnwO03jsSfGG9g==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "nanoid": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.1.tgz", + "integrity": "sha512-n6Vs/3KGyxPQd6uO0eH4Bv0ojGSUvuLlIHtC3Y0kEO23YRge8H9x1GCzLn28YX0H66pMkxuaeESFq4tKISKwdw==", + "dev": true + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "requires": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" + } + }, + "parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true + }, + "path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true + }, + "path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true + }, + "prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "requires": { + "picomatch": "^2.2.1" + } + }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true + }, + "reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true + }, + "rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "requires": { + "glob": "^7.1.3" + }, + "dependencies": { + "glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + } + } + }, + "run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "requires": { + "lru-cache": "^6.0.0" + } + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true + }, + "shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "requires": { + "shebang-regex": "^3.0.0" + } + }, + "shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true + }, + "slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + } + } + }, + "string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "requires": { + "is-number": "^7.0.0" + } + }, + "traverse": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", + "integrity": "sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==", + "dev": true + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "requires": { + "prelude-ls": "^1.2.1" + } + }, + "type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true + }, + "typescript": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", + "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "dev": true + }, + "unzipper": { + "version": "0.10.11", + "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", + "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", + "dev": true, + "requires": { + "big-integer": "^1.6.17", + "binary": "~0.3.0", + "bluebird": "~3.4.1", + "buffer-indexof-polyfill": "~1.0.0", + "duplexer2": "~0.1.4", + "fstream": "^1.0.12", + "graceful-fs": "^4.2.2", + "listenercount": "~1.0.1", + "readable-stream": "~2.3.6", + "setimmediate": "~1.0.4" + } + }, + "uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "vscode-jsonrpc": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.1.tgz", + "integrity": "sha512-N/WKvghIajmEvXpatSzvTvOIz61ZSmOSa4BRA4pTLi+1+jozquQKP/MkaylP9iB68k73Oua1feLQvH3xQuigiQ==" + }, + "vscode-languageclient": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.1.tgz", + "integrity": "sha512-9XoE+HJfaWvu7Y75H3VmLo5WLCtsbxEgEhrLPqwt7eyoR49lUIyyrjb98Yfa50JCMqF2cePJAEVI6oe2o1sIhw==", + "requires": { + "minimatch": "^3.0.4", + "semver": "^7.3.5", + "vscode-languageserver-protocol": "3.17.1" + } + }, + "vscode-languageserver-protocol": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.1.tgz", + "integrity": "sha512-BNlAYgQoYwlSgDLJhSG+DeA8G1JyECqRzM2YO6tMmMji3Ad9Mw6AW7vnZMti90qlAKb0LqAlJfSVGEdqMMNzKg==", + "requires": { + "vscode-jsonrpc": "8.0.1", + "vscode-languageserver-types": "3.17.1" + } + }, + "vscode-languageserver-types": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.1.tgz", + "integrity": "sha512-K3HqVRPElLZVVPtMeKlsyL9aK0GxGQpvtAUTfX4k7+iJ4mc1M+JM+zQwkgGy2LzY0f0IAafe8MKqIkJrxfGGjQ==" + }, + "which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true + }, + "workerpool": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.0.tgz", + "integrity": "sha512-Rsk5qQHJ9eowMH28Jwhe8HEbmdYDX4lwoMWshiCXugjtHqMD9ZbiqSDLxcsfdqsETPzVUtX5s1Z5kStiIM6l4A==", + "dev": true + }, + "wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true + }, + "yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "requires": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "dev": true + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true + } + } +} diff --git a/support/vs-code/silverlsp/package.json b/support/vs-code/silverlsp/package.json new file mode 100644 index 000000000..f4c03ebed --- /dev/null +++ b/support/vs-code/silverlsp/package.json @@ -0,0 +1,104 @@ +{ + "name": "silverlsp", + "displayName": "Silver LSP", + "description": "Plugin for Silver Language Server Protocol support", + "version": "0.0.2", + "engines": { + "vscode": "^1.67.0" + }, + "repository": { + "type": "git", + "url": "https://github.com/melt-umn/silver.git" + }, + "publisher": "MELT", + "icon": "silver.png", + "categories": [ + "Programming Languages" + ], + "tags": [ + "attribute grammar", "parsing", "compiler", "metaprogramming", "extensible language" + ], + "license": "LGPL-3.0-or-later", + "activationEvents": [ + "onLanguage:silver" + ], + "main": "./out/extension.js", + "contributes": { + "languages": [ + { + "id": "silver", + "aliases": [ + "Silver", + "ag", + "sv" + ], + "extensions": [ + ".ag", + ".sv" + ], + "configuration": "./language-configuration.json" + } + ], + "commands": [ + { + "command": "silver.clean", + "title": "Silver: Clean Silver language server workspace" + } + ], + "configuration": [ + { + "title": "Silver", + "properties": { + "silver.enableMWDA": { + "type": "boolean", + "default": true, + "description": "Enable the modular well-definedness analysis" + }, + "silver.jvmArgs": { + "type": "string", + "default": "-Xmx6G -Xss20M", + "description": "Language server JVM flags" + }, + "silver.compilerJar": { + "type": "string", + "description": "Path to the jar containing an alternate version of the Silver compiler" + }, + "silver.parserName": { + "type": "string", + "default": "silver:compiler:composed:Default:svParse", + "description": "Full name of the Silver parser to use, must be set if compiler jar is specified" + } + } + } + ] + }, + "configurationDefaults": { + "silver": { + "editor.semanticHighlighting.enabled": true + } + }, + "dependencies": { + "vscode-languageclient": "8.0.1" + }, + "scripts": { + "vscode:prepublish": "npm run compile", + "compile": "tsc -p ./", + "watch": "tsc -watch -p ./", + "pretest": "npm run compile && npm run lint", + "lint": "eslint src --ext ts", + "test": "node ./out/test/runTest.js" + }, + "devDependencies": { + "@types/glob": "^7.2.0", + "@types/mocha": "^9.1.1", + "@types/node": "14.x", + "@types/vscode": "^1.67.0", + "@typescript-eslint/eslint-plugin": "^5.21.0", + "@typescript-eslint/parser": "^5.21.0", + "@vscode/test-electron": "^2.1.3", + "eslint": "^8.14.0", + "glob": "^8.0.1", + "mocha": "^9.2.2", + "typescript": "^4.6.4" + } +} diff --git a/support/vs-code/silverlsp/silver.png b/support/vs-code/silverlsp/silver.png new file mode 100644 index 000000000..41cc4d53b Binary files /dev/null and b/support/vs-code/silverlsp/silver.png differ diff --git a/support/vs-code/silverlsp/src/extension.ts b/support/vs-code/silverlsp/src/extension.ts new file mode 100644 index 000000000..77a1d104b --- /dev/null +++ b/support/vs-code/silverlsp/src/extension.ts @@ -0,0 +1,66 @@ +import * as path from 'path'; +import * as vscode from 'vscode'; + +// Import the language client, language client options and server options from VSCode language client. +import { LanguageClient, LanguageClientOptions, ServerOptions } from 'vscode-languageclient/node'; + +let client: LanguageClient; + +// Name of the launcher class which contains the main. +const main: string = 'StdioLauncher'; + +export function activate(context: vscode.ExtensionContext) { + console.log('Congratulations, your extension "silverlsp" is now active!'); + + // Get the java home from the process environment. + const { JAVA_HOME } = process.env; + + // Get the config settings + const config = vscode.workspace.getConfiguration("silver"); + + console.log(`Using java from JAVA_HOME: ${JAVA_HOME}`); + // If java home is available continue. + if (JAVA_HOME) { + // Java execution path. + let excecutable: string = path.join(JAVA_HOME, 'bin', 'java'); + + // path to the launcher.jar + let classPath = path.join(__dirname, '..', 'launcher', 'launcher.jar'); + let jvmArgs: string = config.get('jvmArgs') || ""; + const args: string[] = [ + '-cp', classPath, + ].concat(jvmArgs.split(' ')); + + // Set the server options + // -- java execution path + // -- argument to be pass when executing the java command + let serverOptions: ServerOptions = { + command: excecutable, + args: [...args, main], + options: {} + }; + + // Options to control the language client + let clientOptions: LanguageClientOptions = { + documentSelector: [{ scheme: 'file', language: 'silver' }], + synchronize: { + configurationSection: 'Silver' + }, + }; + + // Create the language client and start the client. + console.log("Launching language server"); + client = new LanguageClient('silver-langserver', 'Language Support for Silver', serverOptions, clientOptions); + + client.start(); + } +} + +// this method is called when your extension is deactivated +export function deactivate() { + console.log('Your extension "silver" is now deactivated!'); + if (!client) { + return undefined; + } + return client.stop(); +} \ No newline at end of file diff --git a/support/vs-code/silverlsp/src/test/runTest.ts b/support/vs-code/silverlsp/src/test/runTest.ts new file mode 100644 index 000000000..27b3ceb22 --- /dev/null +++ b/support/vs-code/silverlsp/src/test/runTest.ts @@ -0,0 +1,23 @@ +import * as path from 'path'; + +import { runTests } from '@vscode/test-electron'; + +async function main() { + try { + // The folder containing the Extension Manifest package.json + // Passed to `--extensionDevelopmentPath` + const extensionDevelopmentPath = path.resolve(__dirname, '../../'); + + // The path to test runner + // Passed to --extensionTestsPath + const extensionTestsPath = path.resolve(__dirname, './suite/index'); + + // Download VS Code, unzip it and run the integration test + await runTests({ extensionDevelopmentPath, extensionTestsPath }); + } catch (err) { + console.error('Failed to run tests'); + process.exit(1); + } +} + +main(); diff --git a/support/vs-code/silverlsp/src/test/suite/extension.test.ts b/support/vs-code/silverlsp/src/test/suite/extension.test.ts new file mode 100644 index 000000000..4ca0ab419 --- /dev/null +++ b/support/vs-code/silverlsp/src/test/suite/extension.test.ts @@ -0,0 +1,15 @@ +import * as assert from 'assert'; + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +// import * as myExtension from '../../extension'; + +suite('Extension Test Suite', () => { + vscode.window.showInformationMessage('Start all tests.'); + + test('Sample test', () => { + assert.strictEqual(-1, [1, 2, 3].indexOf(5)); + assert.strictEqual(-1, [1, 2, 3].indexOf(0)); + }); +}); diff --git a/support/vs-code/silverlsp/src/test/suite/index.ts b/support/vs-code/silverlsp/src/test/suite/index.ts new file mode 100644 index 000000000..7029e38ed --- /dev/null +++ b/support/vs-code/silverlsp/src/test/suite/index.ts @@ -0,0 +1,38 @@ +import * as path from 'path'; +import * as Mocha from 'mocha'; +import * as glob from 'glob'; + +export function run(): Promise { + // Create the mocha test + const mocha = new Mocha({ + ui: 'tdd', + color: true + }); + + const testsRoot = path.resolve(__dirname, '..'); + + return new Promise((c, e) => { + glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { + if (err) { + return e(err); + } + + // Add files to the test suite + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + e(new Error(`${failures} tests failed.`)); + } else { + c(); + } + }); + } catch (err) { + console.error(err); + e(err); + } + }); + }); +} diff --git a/support/vs-code/silverlsp/target/npmlist.json b/support/vs-code/silverlsp/target/npmlist.json new file mode 100644 index 000000000..e8c2a295d --- /dev/null +++ b/support/vs-code/silverlsp/target/npmlist.json @@ -0,0 +1 @@ +{"version":"0.0.1","name":"silverlsp","dependencies":{"vscode-languageclient":{"version":"5.1.1"}}} \ No newline at end of file diff --git a/support/vs-code/silverlsp/tsconfig.json b/support/vs-code/silverlsp/tsconfig.json new file mode 100644 index 000000000..315af7ec7 --- /dev/null +++ b/support/vs-code/silverlsp/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "module": "commonjs", + "target": "ES2020", + "outDir": "out", + "lib": [ + "ES2020" + ], + "sourceMap": true, + "rootDir": "src", + "strict": true /* enable all strict type-checking options */ + /* Additional Checks */ + // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ + // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ + // "noUnusedParameters": true, /* Report errors on unused parameters. */ + } +} diff --git a/support/vs-code/silverlsp/vsc-extension-quickstart.md b/support/vs-code/silverlsp/vsc-extension-quickstart.md new file mode 100644 index 000000000..b510bff34 --- /dev/null +++ b/support/vs-code/silverlsp/vsc-extension-quickstart.md @@ -0,0 +1,42 @@ +# Welcome to your VS Code Extension + +## What's in the folder + +* This folder contains all of the files necessary for your extension. +* `package.json` - this is the manifest file in which you declare your extension and command. + * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin. +* `src/extension.ts` - this is the main file where you will provide the implementation of your command. + * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`. + * We pass the function containing the implementation of the command as the second parameter to `registerCommand`. + +## Get up and running straight away + +* Press `F5` to open a new window with your extension loaded. +* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`. +* Set breakpoints in your code inside `src/extension.ts` to debug your extension. +* Find output from your extension in the debug console. + +## Make changes + +* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`. +* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes. + + +## Explore the API + +* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`. + +## Run tests + +* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Extension Tests`. +* Press `F5` to run the tests in a new window with your extension loaded. +* See the output of the test result in the debug console. +* Make changes to `src/test/suite/extension.test.ts` or create new test files inside the `test/suite` folder. + * The provided test runner will only consider files matching the name pattern `**.test.ts`. + * You can create folders inside the `test` folder to structure your tests any way you want. + +## Go further + + * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension). + * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VSCode extension marketplace. + * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration). diff --git a/test/copper_features/Main.sv b/test/copper_features/Main.sv index c2e5b37f1..356ee3714 100644 --- a/test/copper_features/Main.sv +++ b/test/copper_features/Main.sv @@ -1,13 +1,13 @@ grammar copper_features; imports silver:testing ; -imports lib:extcore ; import copper_features:test_layout; import copper_features:mdatests; import copper_features:token_pushing; import copper_features:disambiguation_class; import copper_features:lexer_class; +import copper_features:semantic_tokens; mainTestSuite copper_tests ; diff --git a/test/copper_features/ParserAttributes.sv b/test/copper_features/ParserAttributes.sv index e594914eb..c0c0e99db 100644 --- a/test/copper_features/ParserAttributes.sv +++ b/test/copper_features/ParserAttributes.sv @@ -46,7 +46,7 @@ action { } disambiguate KnownTerm, UnknownTerm { - pluck if containsBy(stringEq, lexeme, knownlist) then KnownTerm else UnknownTerm; + pluck if contains(lexeme, knownlist) then KnownTerm else UnknownTerm; } diff --git a/test/copper_features/disambiguation_class/DisambiguationClasses.sv b/test/copper_features/disambiguation_class/DisambiguationClasses.sv index 46f432519..102a1a0d2 100644 --- a/test/copper_features/disambiguation_class/DisambiguationClasses.sv +++ b/test/copper_features/disambiguation_class/DisambiguationClasses.sv @@ -1,5 +1,4 @@ imports silver:testing ; -imports lib:extcore ; imports copper_features; -- Test disambiguation classes. diff --git a/test/copper_features/lexer_class/LexerClass.sv b/test/copper_features/lexer_class/LexerClass.sv index 214518613..89819a1f5 100644 --- a/test/copper_features/lexer_class/LexerClass.sv +++ b/test/copper_features/lexer_class/LexerClass.sv @@ -1,7 +1,6 @@ grammar copper_features:lexer_class; imports silver:testing ; -imports lib:extcore ; imports copper_features; lexer class Identifier; @@ -55,12 +54,15 @@ parser parse :: Root { equalityTest ( parse("bar", "").parseSuccess, true, Boolean, copper_tests ); equalityTest ( parse("bar", "").parseTree.result, "Id", String, copper_tests ); +equalityTest ( parse("bar", "").parseTerminals, [terminalDescriptor("bar", ["copper_features:lexer_class:Identifier", "copper_features:lexer_class:Identifier2"], "copper_features:lexer_class:Id", loc("", 1, 0, 1, 3, 0, 3))], [TerminalDescriptor], copper_tests ); equalityTest ( parse("foo", "").parseSuccess, true, Boolean, copper_tests ); equalityTest ( parse("foo", "").parseTree.result, "Foo", String, copper_tests ); +equalityTest ( parse("foo", "").parseTerminals, [terminalDescriptor("foo", ["copper_features:lexer_class:Keyword", "copper_features:lexer_class:AKeyword", "copper_features:lexer_class:BKeyword", "copper_features:lexer_class:C3", "copper_features:lexer_class:C1", "copper_features:lexer_class:C2"], "copper_features:lexer_class:Foo", loc("", 1, 0, 1, 3, 0, 3))], [TerminalDescriptor], copper_tests ); equalityTest ( parse("fooxyz", "").parseSuccess, true, Boolean, copper_tests ); equalityTest ( parse("fooxyz", "").parseTree.result, "FooId", String, copper_tests ); +equalityTest ( parse("fooxyz", "").parseTerminals, [terminalDescriptor("fooxyz", ["copper_features:lexer_class:Keyword", "copper_features:lexer_class:AKeyword", "copper_features:lexer_class:CKeyword"], "copper_features:lexer_class:FooId", loc("", 1, 0, 1, 6, 0, 6))], [TerminalDescriptor], copper_tests ); equalityTest ( parse("getRes", "").parseSuccess, true, Boolean, copper_tests ); equalityTest ( parse("getRes", "").parseTree.result, "true", String, copper_tests ); \ No newline at end of file diff --git a/test/copper_features/mdatests/Test.sv b/test/copper_features/mdatests/Test.sv index 0df0d3451..3cd97b6cc 100644 --- a/test/copper_features/mdatests/Test.sv +++ b/test/copper_features/mdatests/Test.sv @@ -1,7 +1,6 @@ grammar copper_features:mdatests; imports silver:testing ; -imports lib:extcore ; imports copper_features; diff --git a/test/copper_features/semantic_tokens/SemanticTokens.sv b/test/copper_features/semantic_tokens/SemanticTokens.sv new file mode 100644 index 000000000..c3a82a484 --- /dev/null +++ b/test/copper_features/semantic_tokens/SemanticTokens.sv @@ -0,0 +1,34 @@ +grammar copper_features:semantic_tokens; + +imports silver:testing ; +imports copper_features; + +lexer class Op; +lexer class Fn; + +terminal Plus_t '+' association=left, lexer classes {Op}; +terminal LParen_t '('; +terminal RParen_t ')'; + +terminal Id_t /[a-zA-Z]+/; +terminal IdFn_t /[a-zA-Z]+/ lexer classes {Fn}; + +ignore terminal Whitespace /[\ \t\r\n]+/; + +nonterminal Expr; +concrete productions top::Expr +| Id_t {} +| i::Id_t '(' ')' {} action { + insert semantic token IdFn_t at i.location; +} +| e1::Expr '+' e2::Expr {} + + +parser parse :: Expr { + copper_features:semantic_tokens; +} + +equalityTest ( flatMap((.lexerClasses), parse("a", "").parseTerminals), [], [String], copper_tests ); +equalityTest ( flatMap((.lexerClasses), parse("a()", "").parseTerminals), ["copper_features:semantic_tokens:Fn"], [String], copper_tests ); +equalityTest ( flatMap((.lexerClasses), parse("a+b", "").parseTerminals), ["copper_features:semantic_tokens:Op"], [String], copper_tests ); +equalityTest ( flatMap((.lexerClasses), parse("a+b()+c", "").parseTerminals), ["copper_features:semantic_tokens:Op", "copper_features:semantic_tokens:Fn", "copper_features:semantic_tokens:Op"], [String], copper_tests ); diff --git a/test/copper_features/silver-compile b/test/copper_features/silver-compile index f04404c77..262412ad1 100755 --- a/test/copper_features/silver-compile +++ b/test/copper_features/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/test/copper_features/test_layout/Layout.sv b/test/copper_features/test_layout/Layout.sv index 229176b45..4f8c791f8 100644 --- a/test/copper_features/test_layout/Layout.sv +++ b/test/copper_features/test_layout/Layout.sv @@ -1,7 +1,6 @@ grammar copper_features:test_layout; imports silver:testing ; -imports lib:extcore ; imports copper_features hiding A; imports copper_features:test_layout:quoting; imports copper_features:test_layout:lookahead; diff --git a/test/copper_features/test_layout/basics/Basics.sv b/test/copper_features/test_layout/basics/Basics.sv index 7ffa49a2d..057407de9 100644 --- a/test/copper_features/test_layout/basics/Basics.sv +++ b/test/copper_features/test_layout/basics/Basics.sv @@ -1,7 +1,6 @@ grammar copper_features:test_layout:basics; imports silver:testing ; -imports lib:extcore ; imports copper_features hiding A; nonterminal BRoot; diff --git a/test/copper_features/test_layout/lookahead/Test.sv b/test/copper_features/test_layout/lookahead/Test.sv index 44857046f..bb7bc5d88 100644 --- a/test/copper_features/test_layout/lookahead/Test.sv +++ b/test/copper_features/test_layout/lookahead/Test.sv @@ -1,7 +1,6 @@ grammar copper_features:test_layout:lookahead; imports silver:testing ; -imports lib:extcore ; imports copper_features; imports copper_features:test_layout:lookahead:host; imports copper_features:test_layout:lookahead:ext; diff --git a/test/copper_features/test_layout/quoting/Quoting.sv b/test/copper_features/test_layout/quoting/Quoting.sv index d36dd1fef..0e13c6552 100644 --- a/test/copper_features/test_layout/quoting/Quoting.sv +++ b/test/copper_features/test_layout/quoting/Quoting.sv @@ -1,7 +1,6 @@ grammar copper_features:test_layout:quoting; imports silver:testing ; -imports lib:extcore ; imports copper_features hiding A; -- This grammar mimics embedding one host language as an extension to another, diff --git a/test/copper_features/token_pushing/TokenPush.sv b/test/copper_features/token_pushing/TokenPush.sv index 6508cd935..2e3a2a191 100644 --- a/test/copper_features/token_pushing/TokenPush.sv +++ b/test/copper_features/token_pushing/TokenPush.sv @@ -1,7 +1,6 @@ grammar copper_features:token_pushing; imports silver:testing ; -imports lib:extcore ; imports copper_features; nonterminal PushTokenRoot; diff --git a/test/csterrors/silver-compile b/test/csterrors/silver-compile index 0f122eb22..d2b9fc688 100755 --- a/test/csterrors/silver-compile +++ b/test/csterrors/silver-compile @@ -1,33 +1,48 @@ -#!/bin/bash +#!/usr/bin/env bash export SILVER_HOME=../.. silver() { "$SILVER_HOME/support/bin/silver" "$@"; } # Always clean build -ARGS="$@ --clean" +ARGS="$@ --clean --dont-translate" count=0 +fail=0 +pass=0 # Check to ensure all these builds yield the shown string (as an error) - -silver $ARGS rhs | grep "B_t" > /dev/null || ((count++)) -silver $ARGS disamb | grep "B_t" > /dev/null || ((count++)) -silver $ARGS startMissing | grep "nonterm_b:B" > /dev/null || ((count++)) -silver $ARGS prodMod | grep "B_t" > /dev/null || ((count++)) -silver $ARGS mda | grep "nonterm_b:B" > /dev/null || ((count++)) -silver $ARGS missingLHS | grep "nonterm_b:B" > /dev/null || ((count++)) -# terminalLHS doesn't contain a reference to B_t or B -silver $ARGS terminalLHS | grep "terminalLHS:terminalLHS" > /dev/null || ((count++)) -silver $ARGS terminalSubmit | grep "B_t" > /dev/null || ((count++)) -silver $ARGS terminalDominate | grep "B_t" > /dev/null || ((count++)) -silver $ARGS terminalLexer | grep "lexer_b:B" > /dev/null || ((count++)) -silver $ARGS lexerSubmit | grep "B_t" > /dev/null || ((count++)) -silver $ARGS lexerDominate | grep "B_t" > /dev/null || ((count++)) -silver $ARGS missingSeparator | grep "test:nonterm_b:Mangle_t" > /dev/null || ((count++)) -silver $ARGS multipleSeparators | grep "multipleSeparators:A_t, multipleSeparators:B_t" > /dev/null || ((count++)) - +function cst_test { + silver $ARGS $1 |& grep "$2" > /dev/null + if [[ $? != 0 ]] + then + echo -e "\nFailure: $1 did not produce output $2" + ((fail++)) + else + echo -n "." + ((pass++)) + fi + ((count++)) +} + +cst_test rhs "B_t" +cst_test disamb "B_t" +cst_test startMissing "nonterm_b:B" +cst_test prodMod "B_t" +cst_test mda "nonterm_b:B" +cst_test missingLHS "nonterm_b:B" +cst_test terminalLHS "terminalLHS:A_t" +cst_test terminalSubmit "B_t" +cst_test terminalDominate "B_t" +cst_test terminalSubmitLexer "lexer_b:B" +cst_test lexerSubmit "B_t" +cst_test lexerDominate "B_t" +cst_test missingSeparator "test:nonterm_b:Mangle_t" +cst_test multipleSeparators "multipleSeparators:A_t, multipleSeparators:B_t" + +echo "" +echo "$pass out of $count tests passed" rm -f build.xml -test $count == 0 +exit $fail diff --git a/test/csterrors/terminalLexer/Artifact.sv b/test/csterrors/terminalLexer/Artifact.sv deleted file mode 100644 index 4ca830d3d..000000000 --- a/test/csterrors/terminalLexer/Artifact.sv +++ /dev/null @@ -1,12 +0,0 @@ -grammar terminalLexer; - -imports test:lexer_b; - -exports host; - -parser extendedParser :: Root { - host; - terminalLexer; -} - -terminal A_t 'A' lexer classes {B}; \ No newline at end of file diff --git a/test/csterrors/terminalSubmitLexer/Artifact.sv b/test/csterrors/terminalSubmitLexer/Artifact.sv new file mode 100644 index 000000000..44f3d02cc --- /dev/null +++ b/test/csterrors/terminalSubmitLexer/Artifact.sv @@ -0,0 +1,12 @@ +grammar terminalSubmitLexer; + +imports test:lexer_b; + +exports host; + +parser extendedParser :: Root { + host; + terminalSubmitLexer; +} + +terminal A_t 'A' submits to {B}; \ No newline at end of file diff --git a/test/flow/FlowSpecs.sv b/test/flow/FlowSpecs.sv new file mode 100644 index 000000000..d8c4accf5 --- /dev/null +++ b/test/flow/FlowSpecs.sv @@ -0,0 +1,25 @@ +grammar flow; + +nonterminal FSExpr with env1, env2; + +production fse +top::FSExpr ::= +{} + +flowtype FSExpr = forward {env1}, decorate {forward, env2}; + +wrongCode "Declaration of global missingRes with type Decorated flow:FSExpr2 with {flow:env1, :env2} has initialization expression with type Decorated flow:FSExpr with {}" { + global missingRes::Decorated FSExpr2 = decorate fse() with {}; +} + +nonterminal FSExpr2 with env1, env2; + +wrongCode "circularity in flow specification for decorate on flow:FSExpr2" { + flowtype FSExpr2 = decorate {decorate}; +} + +{- No way to actually test this, I think, since wrongCode doesn't bubble up flowDefs +wrongCode "circularity in flow specification for forward on flow:FSExpr2" { + flowtype FSExpr2 = decorate {forward}, forward {decorate}; +} +-} diff --git a/test/flow/OccursContext.sv b/test/flow/OccursContext.sv new file mode 100644 index 000000000..dd197b089 --- /dev/null +++ b/test/flow/OccursContext.sv @@ -0,0 +1,246 @@ +grammar flow; + +attribute isEqual, compareTo occurs on Expr; +flowtype isEqual {compareTo} on Expr; +aspect production zero top::Expr ::= +{ propagate isEqual, compareTo; } +aspect production succ top::Expr ::= _ +{ propagate isEqual, compareTo; } +aspect default production +top::Expr ::= +{ top.isEqual = false; } + +function isEqual +attribute compareTo occurs on a, +attribute isEqual {compareTo} occurs on a => +Boolean ::= x::a y::a +{ + x.compareTo = y; + return x.isEqual; +} + +warnCode "Equation has transitive dependency on child x's inherited attribute for flow:env1 but this equation appears to be missing." { +function isEqualBad +attribute compareTo occurs on a, +attribute isEqual {compareTo, env1} occurs on a => +Boolean ::= x::a y::a +{ + x.compareTo = y; + return x.isEqual; +} +} + +global isEqualGlobal :: + attribute compareTo occurs on a, + attribute isEqual {compareTo} occurs on a => + (Boolean ::= a a) = \ x::a y::a -> + decorate x with {compareTo = decorate y with {};}.isEqual; + +warnCode "Decoration requires inherited attribute for flow:env1." { +global isEqualGlobalBad :: + attribute compareTo occurs on a, + attribute isEqual {compareTo, env1} occurs on a => + (Boolean ::= a a) = \ x::a y::a -> + decorate x with {compareTo = decorate y with {};}.isEqual; +} + +function isEqualTooSmall +attribute compareTo occurs on a, +attribute isEqual {} occurs on a => +Boolean ::= x::a y::a +{ + x.compareTo = y; + return x.isEqual; +} + +warnCode "The instance for attribute silver:core:isEqual {} occurs on flow:Expr (arising from the use of isEqualTooSmall) has a flow type exceeding the constraint with dependencies on silver:core:compareTo" { + global isEqualTooSmallErr::Boolean = isEqualTooSmall(zero(), zero()); +} + +warnCode "The instance for attribute silver:core:isEqual {} occurs on Decorated a with {} (arising from the use of isEqualTooSmall) depends on an unbounded set of inherited attributes" { +function isEqualUnboundedError +attribute compareTo occurs on a, +attribute isEqual i occurs on a => +Boolean ::= x::a y::a +{ + return isEqualTooSmall(x, y); +} +} + +class Equal1 a { + isEqual1 :: (Boolean ::= a a); +} + +warnCode "Decoration requires inherited attribute for flow:env1." { +instance attribute compareTo occurs on a, + attribute isEqual {compareTo, env1} occurs on a => + Equal1 a { + isEqual1 = \ x::a y::a -> + decorate x with {compareTo = decorate y with {};}.isEqual; +} +} + +warnCode "Decoration requires inherited attribute for flow:env1." { +class attribute compareTo occurs on a, + attribute isEqual {compareTo, env1} occurs on a => + Equal2 a { + isEqual2 :: (Boolean ::= a a) = \ x::a y::a -> + decorate x with {compareTo = decorate y with {};}.isEqual; +} +} + +synthesized attribute value::Integer occurs on Expr; +flowtype value {env1} on Expr; +aspect value on Expr of +| zero() -> 0 +| succ(e) -> e.value + 1 +end; + +production valueThing1 +attribute env1 occurs on a, +attribute value {env1} occurs on a => +top::Expr ::= x::a +{ + x.env1 = top.env1; + top.value = x.value; +} + +warnCode "Equation has transitive dependency on child x's inherited attribute for flow:env2 but this equation appears to be missing." { +production valueThing1Bad +attribute env1 occurs on a, +attribute value {env1, env2} occurs on a => +top::Expr ::= x::a +{ + x.env1 = top.env1; + top.value = x.value; +} +} + +production valueThing2 +attribute env1 occurs on a, +attribute env2 occurs on a, +attribute value i occurs on a, +i subset {env1, env2} => +top::Expr ::= x::a +{ + x.env1 = top.env1; + x.env2 = top.env1; + top.value = x.value; +} + +warnCode "Access of syn attribute value on x requires missing inherited attributes flow:env2 to be supplied" { +production valueThing2Bad +attribute env1 occurs on a, +attribute env2 occurs on a, +attribute value i occurs on a, +i subset {env1, env2} => +top::Expr ::= x::a +{ + x.env1 = top.env1; + top.value = x.value; +} +} + +warnCode "Access of value from Decorated a with {} requires an unbounded set of inherited attributes" { +production valueThing3Bad +attribute value i occurs on a => +top::Expr ::= x::a +{ + top.value = x.value; +} +} + +production valueThing4 +attribute value i occurs on a, i subset i1 => +top::Expr ::= x::Decorated a with i1 +{ + top.value = x.value; +} + +warnCode "Access of value from reference of type Decorated a with i1 requires one of the following sets of inherited attributes not known to be supplied to this reference: i" { +production valueThing4Bad +attribute value i occurs on a => +top::Expr ::= x::Decorated a with i1 +{ + top.value = x.value; +} +} + +warnCode "warning: Access of value from reference of type Decorated a with i1 requires one of the following sets of inherited attributes not known to be supplied to this reference: i, i2" { +production valueThing4AlsoBad +attribute value i occurs on a, i subset i2 => +top::Expr ::= x::Decorated a with i1 +{ + top.value = x.value; +} +} + +-- Attribute sections +production valueThing5 +attribute env1 occurs on a, +attribute value {env1} occurs on a => +top::Expr ::= x::Decorated a with {env1} +{ + top.value = head(map((.value), [x, x])); +} + +warnCode "Access of value from reference of type Decorated a with {flow:env1} requires inherited attributes not known to be supplied to this reference: flow:env2" { +production valueThing5Bad +attribute env1 occurs on a, +attribute value {env1, env2} occurs on a => +top::Expr ::= x::Decorated a with {env1} +{ + top.value = head(map((.value), [x, x])); +} +} + +-- Type vars as flow types in syn occurs constraints can be satisfied by contexts +production valueThing +attribute value i occurs on a => +top::Expr ::= x::Decorated a with i +{ + top.value = x.value; +} + +function callValueThing +attribute value i occurs on a, i subset i1 => +Expr ::= x::Decorated a with i1 +{ + return valueThing(x); +} + +warnCode "The instance for attribute flow:value i1 occurs on a (arising from the use of valueThing) exceeds the flow type constraint with dependencies on one of the following sets of inherited attributes: i" { +function callValueThingBad +attribute value i occurs on a => +Expr ::= x::Decorated a with i1 +{ + return valueThing(x); +} +} + +-- Specializing the reference set based on the flow type. +function getValuePoly +attribute value i occurs on a => +Integer ::= x::Decorated a with i +{ + return x.value; +} + +function getValueExpr +Integer ::= e::Expr +{ + e.env1 = []; + return getValuePoly(e); +} + +-- value missing a flowtype +nonterminal Expr2 with env1, value; + +wrongCode "Ambiguous type variable a (arising from the use of getValuePoly) prevents the constraint attribute flow:value a occurs on flow:Expr2 from being solved. Note: this ambiguity might be resolved by specifying an explicit flowtype for flow:value on flow:Expr2" { +function getValueExpr2 +Integer ::= e::Expr2 +{ + e.env1 = []; + return getValuePoly(e); +} +} \ No newline at end of file diff --git a/test/flow/PartialDec.sv b/test/flow/PartialDec.sv new file mode 100644 index 000000000..d78c88529 --- /dev/null +++ b/test/flow/PartialDec.sv @@ -0,0 +1,110 @@ +grammar flow; + +synthesized attribute errors1::Boolean; +synthesized attribute errors2::Boolean; + +nonterminal PDExpr with env1, env2, errors1, errors2; + +production overloadThing +top::PDExpr ::= e::PDExpr +{ + e.env1 = top.env1; + forwards to dispatchThing(e); +} + +production dispatchThing +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + -- Accesses on partially decorated children should not give flow errors + top.errors1 = e.errors1; + top.errors2 = !null(e.env1); +} + +function getEnv2FromPartialRef +[String] ::= e::Expr +{ + e.env2 = []; + -- Accesses on partially decorated local should not give flow errors + local e2::PartiallyDecorated Expr with {env2} = e; + return e2.env2; +} + +warnCode "Multiple partially decorated references taken to e in production flow:overloadThing2 (reference has type PartiallyDecorated flow:PDExpr with {flow:env1})." { +production overloadThing2 +top::PDExpr ::= e::PDExpr +{ + local otherRef::PartiallyDecorated PDExpr with {env1} = e; + e.env1 = top.env1; + forwards to dispatchThing(e); +} +} + +warnCode "Attribute env2 with an equation on e is not in the partially decorated reference taken" { +aspect production overloadThing +top::PDExpr ::= e::PDExpr +{ + e.env2 = []; +} +} + +warnCode "Multiple partially decorated references taken to e2 in production flow:overloadThing (reference has type PartiallyDecorated flow:PDExpr with {flow:env1})." { +aspect production overloadThing +top::PDExpr ::= e::PDExpr +{ + local e2::PDExpr = e; + e2.env1 = []; + e2.env2 = []; + local ref1::PartiallyDecorated PDExpr with {env1} = e2; + local ref2::PartiallyDecorated PDExpr with {env1, env2} = e2; +} +} + +warnCode "Partially decorated reference of type PartiallyDecorated flow:PDExpr with {} does not contain all attributes in the reference set of e's type PartiallyDecorated flow:PDExpr with {flow:env1}" { +aspect production dispatchThing +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + local otherRef::PartiallyDecorated PDExpr with {} = e; +} +} + +function thingWithBoundedRefArg +{env1, env2} subset i, i subset {env1} => -- Uninhabited, but shouldn't give an error +PartiallyDecorated PDExpr with {env1} ::= e::PartiallyDecorated PDExpr with i +{ + return e; +} + +warnCode "Cannot take a partially decorated reference to e of type PartiallyDecorated flow:PDExpr with i, as the reference set is not bounded" { +function thingWithUnboundedRefArg +{env1, env2} subset i => PartiallyDecorated PDExpr with {env1} ::= e::PartiallyDecorated PDExpr with i +{ + return e; +} +} + +warnCode "Duplicate equation for env2 on e in production flow:alreadyDec" { +function alreadyDec +() ::= e::PartiallyDecorated PDExpr with {env2} +{ + e.env2 = []; + return (); +} +} + +warnCode "Attribute env1 with an equation on e is not in the partially decorated reference taken" { +function eqnNotInRef +PartiallyDecorated PDExpr with {env2} ::= e::PDExpr +{ + e.env1 = []; + e.env2 = []; + return e; +} +} + +warnCode "Partially decorated reference of type PartiallyDecorated flow:PDExpr with {} does not contain all attributes in the reference set of e's type PartiallyDecorated flow:PDExpr with {flow:env1}" { +aspect production dispatchThing +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + local otherRef::PartiallyDecorated PDExpr with {} = e; +} +} diff --git a/test/flow/References.sv b/test/flow/References.sv new file mode 100644 index 000000000..8bb42d8b4 --- /dev/null +++ b/test/flow/References.sv @@ -0,0 +1,100 @@ +grammar flow; + +imports silver:core hiding zero; + +inherited attribute env1::[String]; +inherited attribute env2::[String]; +nonterminal Expr with env1, env2; +flowtype Expr = decorate {env1}; + +production zero +top::Expr ::= +{} + +production succ +top::Expr ::= e::Expr +{ + e.env1 = top.env1; +} + +function getRef +Decorated Expr ::= x::Expr +{ + x.env1 = []; + return x; +} + +warnCode "Equation has transitive dependency on child x's inherited attribute for flow:env1 but this equation appears to be missing." { + function getRefEnv1Missing + Decorated Expr with {env1} ::= x::Expr + { return x; } +} + +function getRefEnv1 +Decorated Expr with {env1} ::= x::Expr +{ + x.env1 = []; + return x; +} + +warnCode "Cannot take a reference of type Decorated flow:Expr with i, as the reference set is not bounded." { + function getRefUnbounded + Decorated Expr with i ::= x::Expr + { + x.env1 = []; + return x; + } +} + +function getRefBoundedEnv1 +i subset {env1} => Decorated Expr with i ::= x::Expr +{ + x.env1 = []; + return x; +} + +-- This would be flow error, but is caught by type checking first +wrongCode "Expected return type is Decorated flow:Expr with i, but the expression has actual type Decorated flow:Expr with {flow:env1}" { + function getDecRefUnbounded + Decorated Expr with i ::= x::Expr + { + return decorate x with {env1=[];}; + } +} + +-- This would be OK, but is caught by type checking anyway +wrongCode "Expected return type is Decorated flow:Expr with i, but the expression has actual type Decorated flow:Expr with {flow:env1}" { + function getDecRefUnbounded + i subset {env1} => Decorated Expr with i ::= x::Expr + { + return decorate x with {env1=[];}; + } +} + +synthesized attribute getRefWith :: Decorated a with i; +nonterminal RExpr<(i :: InhSet)> with env1, env2, getRefWith i>; +warnCode "Cannot take a reference of type Decorated flow:RExpr with i, as the reference set is not bounded." { + production mkRExprUnbounded + top::RExpr ::= + { + top.getRefWith = top; + } +} + +production mkRExprBounded +i subset {env1} => top::RExpr ::= +{ + top.getRefWith = top; +} + +function getRExprRefWithEnv1 +Decorated RExpr<{env1}> with {env1} ::= +{ + local a::RExpr<{env1}> = mkRExprBounded(); + a.env1 = []; + return a.getRefWith; +} + +warnCode "Duplicate equation for env1" { + global decDuplicate::Decorated Expr with {env1, env2} = decorate zero() with {env1 = []; env2 = []; env1 = ["a"];}; +} diff --git a/test/flow/build.test b/test/flow/build.test new file mode 100644 index 000000000..15937bbb9 --- /dev/null +++ b/test/flow/build.test @@ -0,0 +1 @@ +run: ./silver-compile --clean diff --git a/test/flow/silver-compile b/test/flow/silver-compile new file mode 100755 index 000000000..16dbbd46a --- /dev/null +++ b/test/flow/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../support/bin/silver" "$@"; } + +SRC=.. +GRAMMAR=flow + +silver -I $SRC --warn-all --dont-translate $@ $GRAMMAR + diff --git a/test/implicit_monads/AttrTest.sv b/test/implicit_monads/AttrTest.sv new file mode 100644 index 000000000..521c08fe5 --- /dev/null +++ b/test/implicit_monads/AttrTest.sv @@ -0,0 +1,14 @@ +grammar implicit_monads; + + +implicit synthesized attribute test1::Either; +implicit inherited attribute test2::Either; + + +wrongCode "must have a monadic type" { + implicit synthesized attribute test3::Integer; +} +wrongCode "must have a monadic type" { + implicit inherited attribute test4::Integer; +} + diff --git a/test/implicit_monads/EquationTest.sv b/test/implicit_monads/EquationTest.sv new file mode 100644 index 000000000..bcb760650 --- /dev/null +++ b/test/implicit_monads/EquationTest.sv @@ -0,0 +1,276 @@ +grammar implicit_monads; + + +implicit synthesized attribute i_eq_out::[Integer]; +implicit inherited attribute i_eq_in::[Integer]; + +restricted synthesized attribute r_eq_out::Integer; +restricted inherited attribute r_eq_in::Integer; + +synthesized attribute u_eq_out::Integer; +inherited attribute u_eq_in::Integer; + +implicit synthesized attribute no_fail_out::State; +implicit inherited attribute no_fail_in::State; + +nonterminal EquationTest with + i_eq_out, i_eq_in, r_eq_out, r_eq_in, u_eq_out, u_eq_in, + no_fail_in, no_fail_out; + +nonterminal TurnAround_Eq with + i_eq_out, i_eq_in, r_eq_out, r_eq_in, u_eq_out, u_eq_in, + no_fail_in, no_fail_out; +production turnAround_Eq +top::TurnAround_Eq ::= +{ + top.i_eq_out = top.i_eq_in; + top.r_eq_out = top.r_eq_in; + top.u_eq_out = top.u_eq_in; + top.no_fail_out = top.no_fail_in; +} + + + +--Test empty equations work with failure monads +production test_empty_eq1 +top::EquationTest ::= +{ + implicit top.i_eq_out = ; +} +production test_empty_eq2 +top::EquationTest ::= c::TurnAround_Eq +{ + implicit c.i_eq_in = ; + top.i_eq_out = c.i_eq_out; +} + +equalityTest(test_empty_eq1().i_eq_out, [], + [Integer], implicit_monad_tests); +equalityTest(test_empty_eq2(turnAround_Eq()).i_eq_out, [], + [Integer], implicit_monad_tests); + + + +--Test empty equations only work with failure monads +wrongCode "not an instance of MonadFail" { + production test_empty_eq_no_fail + top::EquationTest ::= + { + implicit top.no_fail_out = ; + } +} +wrongCode "not an instance of MonadFail" { + production test_empty_eq_no_fail + top::EquationTest ::= c::TurnAround_Eq + { + implicit c.no_fail_in = ; + } +} + + + +--test restricted flowing into implicit +production test_restricted_impeq1 +top::EquationTest ::= +{ + top.r_eq_out = 3; + top.i_eq_out = top.r_eq_out; +} +production test_restricted_impeq2 +top::EquationTest ::= +{ + top.i_eq_out = top.r_eq_in; +} +production test_restricted_impeq3 +top::EquationTest ::= c::TurnAround_Eq +{ + top.r_eq_out = 4; + c.i_eq_in = top.r_eq_out; + top.i_eq_out = c.i_eq_out; +} +production test_restricted_impeq4 +top::EquationTest ::= c::TurnAround_Eq +{ + c.i_eq_in = top.r_eq_in; + top.i_eq_out = c.i_eq_out; +} + +equalityTest(test_restricted_impeq1().i_eq_out, [3], + [Integer], implicit_monad_tests); +equalityTest(decorate test_restricted_impeq2() with {r_eq_in = 4;}.i_eq_out, [4], + [Integer], implicit_monad_tests); +equalityTest(test_restricted_impeq3(turnAround_Eq()).i_eq_out, [4], + [Integer], implicit_monad_tests); +equalityTest(decorate test_restricted_impeq4(turnAround_Eq()) with {r_eq_in = 5;}.i_eq_out, [5], + [Integer], implicit_monad_tests); + + + +--test implicit flowing into unrestricted +production test_implicit_unreq1 +top::EquationTest ::= +{ + top.i_eq_out = 3; + top.u_eq_out = head(top.i_eq_out); +} +production test_implicit_unreq2 +top::EquationTest ::= +{ + top.u_eq_out = head(top.i_eq_in); +} +production test_implicit_unreq3 +top::EquationTest ::= c::TurnAround_Eq +{ + top.i_eq_out = 4; + c.u_eq_in = head(top.i_eq_out); + top.u_eq_out = c.u_eq_out; +} +production test_implicit_unreq4 +top::EquationTest ::= c::TurnAround_Eq +{ + c.u_eq_in = head(top.i_eq_in); + top.u_eq_out = c.u_eq_out; +} + +equalityTest(test_implicit_unreq1().u_eq_out, 3, + Integer, implicit_monad_tests); +equalityTest(decorate test_implicit_unreq2() with {i_eq_in = [4];}.u_eq_out, 4, + Integer, implicit_monad_tests); +equalityTest(test_implicit_unreq3(turnAround_Eq()).u_eq_out, 4, + Integer, implicit_monad_tests); +equalityTest(decorate test_implicit_unreq4(turnAround_Eq()) with {i_eq_in = [5];}.u_eq_out, 5, + Integer, implicit_monad_tests); + + + +--test restricted flowing into unrestricted +production test_restricted_unreq1 +top::EquationTest ::= +{ + top.r_eq_out = 3; + top.u_eq_out = top.r_eq_out; +} +production test_restricted_unreq2 +top::EquationTest ::= +{ + top.u_eq_out = top.r_eq_in; +} +production test_restricted_unreq3 +top::EquationTest ::= c::TurnAround_Eq +{ + top.r_eq_out = 4; + c.u_eq_in = top.r_eq_out; + top.u_eq_out = c.u_eq_out; +} +production test_restricted_unreq4 +top::EquationTest ::= c::TurnAround_Eq +{ + c.u_eq_in = top.r_eq_in; + top.u_eq_out = c.u_eq_out; +} + +equalityTest(test_restricted_unreq1().u_eq_out, 3, + Integer, implicit_monad_tests); +equalityTest(decorate test_restricted_unreq2() with {r_eq_in = 4;}.u_eq_out, 4, + Integer, implicit_monad_tests); +equalityTest(test_restricted_unreq3(turnAround_Eq()).u_eq_out, 4, + Integer, implicit_monad_tests); +equalityTest(decorate test_restricted_unreq4(turnAround_Eq()) with {r_eq_in = 5;}.u_eq_out, 5, + Integer, implicit_monad_tests); + + + +--test implicit not flowing into restricted +wrongCode "must be restricted" { + production test_implicit_reseq1 + top::EquationTest ::= + { + top.r_eq_out = top.i_eq_in; + } +} +wrongCode "must be restricted" { + production test_implicit_reseq2 + top::EquationTest ::= c::TurnAround_Eq + { + top.r_eq_out = c.i_eq_out; + } +} +wrongCode "must be restricted" { + production test_implicit_reseq3 + top::EquationTest ::= c::TurnAround_Eq + { + c.r_eq_in = top.i_eq_in; + } +} +wrongCode "must be restricted" { + production test_implicit_reseq4 + top::EquationTest ::= c::TurnAround_Eq + { + c.r_eq_in = top.i_eq_out; + } +} + + + +--test unrestricted not flowing into restricted +wrongCode "must be restricted" { + production test_unrestricted_reseq1 + top::EquationTest ::= + { + top.r_eq_out = top.u_eq_in; + } +} +wrongCode "must be restricted" { + production test_unrestricted_reseq2 + top::EquationTest ::= c::TurnAround_Eq + { + top.r_eq_out = c.u_eq_out; + } +} +wrongCode "must be restricted" { + production test_unrestricted_reseq3 + top::EquationTest ::= c::TurnAround_Eq + { + c.r_eq_in = top.u_eq_in; + } +} +wrongCode "must be restricted" { + production test_unrestricted_reseq4 + top::EquationTest ::= c::TurnAround_Eq + { + c.r_eq_in = top.u_eq_out; + } +} + + + +--test unrestricted not flowing into implicit +wrongCode "must be either implicit or restricted" { + production test_unrestricted_impeq1 + top::EquationTest ::= + { + top.i_eq_out = top.u_eq_in; + } +} +wrongCode "must be either implicit or restricted" { + production test_unrestricted_impeq2 + top::EquationTest ::= c::TurnAround_Eq + { + top.i_eq_out = c.u_eq_out; + } +} +wrongCode "must be either implicit or restricted" { + production test_unrestricted_impeq3 + top::EquationTest ::= c::TurnAround_Eq + { + c.i_eq_in = top.u_eq_in; + } +} +wrongCode "must be either implicit or restricted" { + production test_unrestricted_impeq4 + top::EquationTest ::= c::TurnAround_Eq + { + c.i_eq_in = top.u_eq_out; + } +} + diff --git a/test/implicit_monads/ExprTest.sv b/test/implicit_monads/ExprTest.sv new file mode 100644 index 000000000..aa1e3516a --- /dev/null +++ b/test/implicit_monads/ExprTest.sv @@ -0,0 +1,704 @@ +grammar implicit_monads; + + +{- + This is to test rewriting for all the expressions, not for testing + different monads. Therefore we will use Maybe here for everything, + and test other monads elsewhere. +-} + +restricted synthesized attribute const::String; + +nonterminal Const with const; + +production constC +top::Const ::= +{ top.const = "s"; } + + +function stringID +String ::= s::String +{ return s; } + + +restricted inherited attribute down::String; +restricted synthesized attribute up::String; + +nonterminal TurnAround with down, up; + +production turnAround +top::TurnAround ::= +{ + top.up = top.down; +} + + + +implicit synthesized attribute s_out::Maybe; +implicit synthesized attribute i_out::Maybe; +implicit synthesized attribute b_out::Maybe; +implicit synthesized attribute f_out::Maybe; + +nonterminal ExprTest with s_out, i_out, b_out, f_out; + +production test_application_monad_arg +top::ExprTest ::= arg::Maybe +{ + top.s_out = stringID(arg); +} + +production test_application_monad_fun +top::ExprTest ::= f::Maybe<(String ::= String)> +{ + top.s_out = f("s"); +} + +equalityTest(test_application_monad_arg(just("s")).s_out, just("s"), + Maybe, implicit_monad_tests); +equalityTest(test_application_monad_arg(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_application_monad_fun(just(stringID)).s_out, just("s"), + Maybe, implicit_monad_tests); +equalityTest(test_application_monad_fun(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + + +production test_access_monad_tree +top::ExprTest ::= tree::Maybe +{ + top.s_out = tree.const; +} + +equalityTest(test_access_monad_tree(just(constC())).s_out, just("s"), + Maybe, implicit_monad_tests); +equalityTest(test_access_monad_tree(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_decorate_monad_tree +top::ExprTest ::= tree::Maybe +{ + top.s_out = decorate tree with {down = "s";}.up; +} + +equalityTest(test_decorate_monad_tree(just(turnAround())).s_out, just("s"), + Maybe, implicit_monad_tests); +equalityTest(test_decorate_monad_tree(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_ifelse_monad_cond1 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then "t" else "f"; +} +production test_ifelse_monad_cond2 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then just("t") else "f"; +} +production test_ifelse_monad_cond3 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then "t" else just("f"); +} +production test_ifelse_monad_cond4 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then just("t") else just("f"); +} + +equalityTest(test_ifelse_monad_cond1(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond1(just(false)).s_out, just("f"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond1(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_ifelse_monad_cond2(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond2(just(false)).s_out, just("f"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond2(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_ifelse_monad_cond3(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond3(just(false)).s_out, just("f"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond3(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_ifelse_monad_cond4(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond4(just(false)).s_out, just("f"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_cond4(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + +production test_ifelse_monad_b1 +top::ExprTest ::= cond::Boolean +{ + top.s_out = if cond then just("t") else "f"; +} +production test_ifelse_monad_b2 +top::ExprTest ::= cond::Boolean +{ + top.s_out = if cond then "t" else just("f"); +} + +equalityTest(test_ifelse_monad_b1(true).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_b1(false).s_out, just("f"), + Maybe, implicit_monad_tests); +-- +equalityTest(test_ifelse_monad_b2(true).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifelse_monad_b2(false).s_out, just("f"), + Maybe, implicit_monad_tests); + + + +production test_ifthen_monad_cond1 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then "t" end; +} +production test_ifthen_monad_cond2 +top::ExprTest ::= cond::Maybe +{ + top.s_out = if cond then just("t") end; +} + +equalityTest(test_ifthen_monad_cond1(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_monad_cond1(just(false)).s_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_monad_cond1(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_ifthen_monad_cond2(just(true)).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_monad_cond2(just(false)).s_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_monad_cond2(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + +production test_ifthen_monad_b +top::ExprTest ::= cond::Boolean +{ + top.s_out = if cond then just("t") end; +} + +equalityTest(test_ifthen_monad_b(true).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_monad_b(false).s_out, nothing(), + Maybe, implicit_monad_tests); + +production test_ifthen_no_monad +top::ExprTest ::= cond::Boolean +{ + top.s_out = if cond then "t" end; +} + +equalityTest(test_ifthen_no_monad(true).s_out, just("t"), + Maybe, implicit_monad_tests); +equalityTest(test_ifthen_no_monad(false).s_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_and_1 +top::ExprTest ::= b1::Maybe b2::Boolean +{ + top.b_out = b1 && b2; +} +production test_and_2 +top::ExprTest ::= b1::Boolean b2::Maybe +{ + top.b_out = b1 && b2; +} +production test_and_3 +top::ExprTest ::= b1::Maybe b2::Maybe +{ + top.b_out = b1 && b2; +} + +equalityTest(test_and_1(just(true), true).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_and_1(just(false), true).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_and_1(nothing(), true).b_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_and_2(false, just(true)).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_and_2(true, just(false)).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_and_2(true, nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_and_2(false, nothing()).b_out, just(false), + Maybe, implicit_monad_tests); +-- +equalityTest(test_and_3(just(true), just(true)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_and_3(just(false), just(true)).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_and_3(nothing(), just(true)).b_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_and_3(just(true), nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_and_3(just(false), nothing()).b_out, just(false), + Maybe, implicit_monad_tests); + + + +production test_or_1 +top::ExprTest ::= b1::Maybe b2::Boolean +{ + top.b_out = b1 || b2; +} +production test_or_2 +top::ExprTest ::= b1::Boolean b2::Maybe +{ + top.b_out = b1 || b2; +} +production test_or_3 +top::ExprTest ::= b1::Maybe b2::Maybe +{ + top.b_out = b1 || b2; +} + +equalityTest(test_or_1(just(true), true).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_1(just(false), true).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_1(just(false), false).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_or_1(nothing(), true).b_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_or_2(false, just(true)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_2(true, just(false)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_2(true, nothing()).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_2(false, nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_or_3(just(true), just(true)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_3(just(false), just(true)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_3(nothing(), just(true)).b_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_or_3(just(true), nothing()).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_or_3(just(false), nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_not +top::ExprTest ::= b::Maybe +{ + top.b_out = ! b; +} + +equalityTest(test_not(just(true)).b_out, just(false), + Maybe, implicit_monad_tests); +equalityTest(test_not(just(false)).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_not(nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_plus1 +top::ExprTest ::= i1::Maybe i2::Integer +{ + top.i_out = i1 + i2; +} +production test_plus2 +top::ExprTest ::= i1::Integer i2::Maybe +{ + top.i_out = i1 + i2; +} +production test_plus3 +top::ExprTest ::= i1::Maybe i2::Maybe +{ + top.i_out = i1 + i2; +} + +equalityTest(test_plus1(just(1), 3).i_out, just(4), + Maybe, implicit_monad_tests); +equalityTest(test_plus1(nothing(), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_plus2(1, just(3)).i_out, just(4), + Maybe, implicit_monad_tests); +equalityTest(test_plus2(1, nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_plus3(just(1), just(3)).i_out, just(4), + Maybe, implicit_monad_tests); +equalityTest(test_plus3(nothing(), just(3)).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_plus3(just(1), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_plus3(nothing(), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_minus1 +top::ExprTest ::= i1::Maybe i2::Integer +{ + top.i_out = i1 - i2; +} +production test_minus2 +top::ExprTest ::= i1::Integer i2::Maybe +{ + top.i_out = i1 - i2; +} +production test_minus3 +top::ExprTest ::= i1::Maybe i2::Maybe +{ + top.i_out = i1 - i2; +} + +equalityTest(test_minus1(just(1), 3).i_out, just(-2), + Maybe, implicit_monad_tests); +equalityTest(test_minus1(nothing(), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_minus2(1, just(3)).i_out, just(-2), + Maybe, implicit_monad_tests); +equalityTest(test_minus2(1, nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_minus3(just(1), just(3)).i_out, just(-2), + Maybe, implicit_monad_tests); +equalityTest(test_minus3(nothing(), just(3)).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_minus3(just(1), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_minus3(nothing(), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_multiply1 +top::ExprTest ::= i1::Maybe i2::Integer +{ + top.i_out = i1 * i2; +} +production test_multiply2 +top::ExprTest ::= i1::Integer i2::Maybe +{ + top.i_out = i1 * i2; +} +production test_multiply3 +top::ExprTest ::= i1::Maybe i2::Maybe +{ + top.i_out = i1 * i2; +} + +equalityTest(test_multiply1(just(1), 3).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_multiply1(nothing(), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_multiply2(1, just(3)).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_multiply2(1, nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_multiply3(just(1), just(3)).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_multiply3(nothing(), just(3)).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_multiply3(just(1), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_multiply3(nothing(), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_divide1 +top::ExprTest ::= i1::Maybe i2::Integer +{ + top.i_out = i1 / i2; +} +production test_divide2 +top::ExprTest ::= i1::Integer i2::Maybe +{ + top.i_out = i1 / i2; +} +production test_divide3 +top::ExprTest ::= i1::Maybe i2::Maybe +{ + top.i_out = i1 / i2; +} + +equalityTest(test_divide1(just(10), 3).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_divide1(nothing(), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_divide2(10, just(3)).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_divide2(10, nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_divide3(just(10), just(3)).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_divide3(nothing(), just(3)).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_divide3(just(10), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_divide3(nothing(), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_modulus1 +top::ExprTest ::= i1::Maybe i2::Integer +{ + top.i_out = i1 % i2; +} +production test_modulus2 +top::ExprTest ::= i1::Integer i2::Maybe +{ + top.i_out = i1 % i2; +} +production test_modulus3 +top::ExprTest ::= i1::Maybe i2::Maybe +{ + top.i_out = i1 % i2; +} + +equalityTest(test_modulus1(just(10), 3).i_out, just(1), + Maybe, implicit_monad_tests); +equalityTest(test_modulus1(nothing(), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_modulus2(10, just(3)).i_out, just(1), + Maybe, implicit_monad_tests); +equalityTest(test_modulus2(10, nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_modulus3(just(10), just(3)).i_out, just(1), + Maybe, implicit_monad_tests); +equalityTest(test_modulus3(nothing(), just(3)).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_modulus3(just(10), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_modulus3(nothing(), nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_negate +top::ExprTest ::= i::Maybe +{ + top.i_out = - i; +} + +equalityTest(test_negate(just(10)).i_out, just(-10), + Maybe, implicit_monad_tests); +equalityTest(test_negate(nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_let1 +top::ExprTest ::= i::Maybe +{ + top.i_out = let x::Integer = i in x end; +} +production test_let2 +top::ExprTest ::= +{ + top.i_out = let x::Integer = 3 in x end; +} + +equalityTest(test_let1(just(3)).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_let1(nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); +-- +equalityTest(test_let2().i_out, just(3), + Maybe, implicit_monad_tests); + + + +production test_length +top::ExprTest ::= s::Maybe +{ + top.i_out = length(s); +} + +equalityTest(test_length(just("s")).i_out, just(1), + Maybe, implicit_monad_tests); +equalityTest(test_length(nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_toInteger +top::ExprTest ::= s::Maybe +{ + top.i_out = toInteger(s); +} + +equalityTest(test_toInteger(just("3")).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_toInteger(nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_toBoolean +top::ExprTest ::= s::Maybe +{ + top.b_out = toBoolean(s); +} + +equalityTest(test_toBoolean(just("true")).b_out, just(true), + Maybe, implicit_monad_tests); +equalityTest(test_toBoolean(nothing()).b_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_toFloat +top::ExprTest ::= s::Maybe +{ + top.f_out = toFloat(s); +} + +equalityTest(test_toFloat(just("0.0")).f_out, just(0.0), + Maybe, implicit_monad_tests); +equalityTest(test_toFloat(nothing()).f_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_toString +top::ExprTest ::= s::Maybe +{ + top.s_out = toString(s); +} + +equalityTest(test_toString(just(3)).s_out, just("3"), + Maybe, implicit_monad_tests); +equalityTest(test_toString(nothing()).s_out, nothing(), + Maybe, implicit_monad_tests); + + + +production test_case1 +top::ExprTest ::= s::Maybe +{ + top.i_out = + case s of + | "a" -> 5 + | "b" -> 6 + end; +} + +equalityTest(test_case1(just("a")).i_out, just(5), + Maybe, implicit_monad_tests); +equalityTest(test_case1(just("b")).i_out, just(6), + Maybe, implicit_monad_tests); +equalityTest(test_case1(just("c")).i_out, nothing(), + Maybe, implicit_monad_tests); + +production test_case2 +top::ExprTest ::= s::Maybe i::Integer +{ + top.i_out = + case s, i of + | "a", _ -> 5 + | "b", 5 -> 6 + end; +} + +equalityTest(test_case2(just("a"), 3).i_out, just(5), + Maybe, implicit_monad_tests); +equalityTest(test_case2(just("b"), 5).i_out, just(6), + Maybe, implicit_monad_tests); +equalityTest(test_case2(just("b"), 3).i_out, nothing(), + Maybe, implicit_monad_tests); +equalityTest(test_case2(just("c"), 0).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +--The point here is nesting, which is clearer if we use this syntax rather than (_, _, _) +production test_case_nested +top::ExprTest ::= p::Maybe>> +{ + top.i_out = + case p of + | pair("a", pair(0, 1)) -> 3 + | pair("b", pair(_, 0)) -> 4 + | _ -> 5 + end; +} + +equalityTest(test_case_nested(just(pair("a", pair(0, 1)))).i_out, just(3), + Maybe, implicit_monad_tests); +equalityTest(test_case_nested(just(pair("b", pair(0, 0)))).i_out, just(4), + Maybe, implicit_monad_tests); +equalityTest(test_case_nested(just(pair("a", pair(0, 0)))).i_out, just(5), + Maybe, implicit_monad_tests); +equalityTest(test_case_nested(nothing()).i_out, nothing(), + Maybe, implicit_monad_tests); + + + +implicit synthesized attribute i_out_lst::[Integer]; +attribute i_out_lst occurs on ExprTest; + +production test_case_any1 +top::ExprTest ::= p::Pair +{ + top.i_out_lst = + case_any p of + | pair("a", 0) -> [3] + | pair("b", 1) -> [4] + | _ -> 5 + end; +} +production test_case_any2 +top::ExprTest ::= p::[Pair] +{ + top.i_out_lst = + case_any p of + | pair("a", 0) -> 3 + | pair("b", 1) -> 4 + | _ -> 5 + end; +} + +equalityTest(test_case_any1(pair("a", 0)).i_out_lst, [3, 5], + [Integer], implicit_monad_tests); +equalityTest(test_case_any1(pair("b", 1)).i_out_lst, [4, 5], + [Integer], implicit_monad_tests); +equalityTest(test_case_any1(pair("c", 2)).i_out_lst, [5], + [Integer], implicit_monad_tests); +-- +equalityTest(test_case_any2([pair("a", 0)]).i_out_lst, [3, 5], + [Integer], implicit_monad_tests); +equalityTest(test_case_any2([pair("b", 1)]).i_out_lst, [4, 5], + [Integer], implicit_monad_tests); +equalityTest(test_case_any2([pair("c", 2)]).i_out_lst, [5], + [Integer], implicit_monad_tests); +equalityTest(test_case_any2([]).i_out_lst, [], + [Integer], implicit_monad_tests); + diff --git a/test/implicit_monads/Main.sv b/test/implicit_monads/Main.sv new file mode 100644 index 000000000..2ac435e41 --- /dev/null +++ b/test/implicit_monads/Main.sv @@ -0,0 +1,11 @@ +grammar implicit_monads; + +imports silver:testing; + +mainTestSuite implicit_monad_tests; + +{- + I'm putting this in its own test suite beacuse including it in + silver_features was causing the testing to stack overflow. +-} + diff --git a/test/implicit_monads/MonadTest.sv b/test/implicit_monads/MonadTest.sv new file mode 100644 index 000000000..50454701a --- /dev/null +++ b/test/implicit_monads/MonadTest.sv @@ -0,0 +1,107 @@ +grammar implicit_monads; + + +implicit synthesized attribute i_state_out::State; +implicit inherited attribute i_state_in::State; + +implicit synthesized attribute i_unit_out::Unit; +implicit inherited attribute i_unit_in::Unit; + +nonterminal MonadTest with + i_state_out, i_state_in, i_unit_out, i_unit_in; + +nonterminal TurnAround_M with + i_state_out, i_state_in, i_unit_out, i_unit_in; + +production turnAround_M +top::TurnAround_M ::= +{ + top.i_state_out = top.i_state_in; + top.i_unit_out = top.i_unit_in; +} + + + +--Test case_any only works with alternative monads +wrongCode "not an instance of" { + production test_case_any_no_alt1 + top::MonadTest ::= + { + top.i_state_out = + case_any 3, 4 of + | 3, 4 -> 3 + | 5, 6 -> 6 + | 7, 8 -> 8 + | _, _ -> 9 + end; + } +} +wrongCode "not an instance of" { + production test_case_any_no_alt2 + top::MonadTest ::= c::TurnAround_M + { + c.i_state_in = + case_any 3, 4 of + | 3, 4 -> 3 + | 5, 6 -> 6 + | 7, 8 -> 8 + | _, _ -> 9 + end; + } +} + + + +--Test user-defined monads can be used implicitly +production test_user_monad1 +top::MonadTest ::= +{ + top.i_unit_out = top.i_unit_in + 5; +} +production test_user_monad2 +top::MonadTest ::= c::TurnAround_M +{ + implicit c.i_unit_in = ; + top.i_unit_out = 2 * c.i_unit_out; +} + +--these are just to test that it actually evaluates +equalityTest(decorate test_user_monad1() with {i_unit_in=unitA();}.i_unit_out, unitA(), + Unit, implicit_monad_tests); +equalityTest(test_user_monad2(turnAround_M()).i_unit_out, unitA(), + Unit, implicit_monad_tests); + + + +nonterminal Unit; + +production unitA +top::Unit ::= +{ } + +instance Functor Unit { + map = \ f::(b ::= a) m::Unit -> unitA(); +} + +instance Apply Unit { + ap = \ mf::Unit<(b ::= a)> m::Unit -> unitA(); +} + +instance Applicative Unit { + pure = \ a -> unitA(); +} + +instance Bind Unit { + bind = \ m::Unit fn::(Unit ::= a) -> unitA(); +} + +instance Monad Unit {} + +instance MonadFail Unit { + fail = \ String -> unitA(); +} + +instance Eq Unit { + eq = \ Unit Unit -> true; +} + diff --git a/test/implicit_monads/build.test b/test/implicit_monads/build.test new file mode 100644 index 000000000..15937bbb9 --- /dev/null +++ b/test/implicit_monads/build.test @@ -0,0 +1 @@ +run: ./silver-compile --clean diff --git a/test/implicit_monads/run.test b/test/implicit_monads/run.test new file mode 100644 index 000000000..ce3fb0c94 --- /dev/null +++ b/test/implicit_monads/run.test @@ -0,0 +1 @@ +test suite test.jar diff --git a/test/implicit_monads/silver-compile b/test/implicit_monads/silver-compile new file mode 100755 index 000000000..93270fa44 --- /dev/null +++ b/test/implicit_monads/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../support/bin/silver" "$@"; } + +SRC=.. +GRAMMAR=implicit_monads + +silver -I $SRC $@ -o test.jar $GRAMMAR + diff --git a/test/origintracking/common/common.sv b/test/origintracking/common/common.sv new file mode 100644 index 000000000..b6f2cbf9c --- /dev/null +++ b/test/origintracking/common/common.sv @@ -0,0 +1,15 @@ +imports silver:testing; +imports silver:reflect; +imports silver:langutil; + +exports silver:testing; +exports silver:reflect; +exports silver:langutil; + +function identityHashCode +Integer ::= nt::a +{ + return error("Not impl"); +} foreign { + "java" : return "(System.identityHashCode(%nt%))"; +} \ No newline at end of file diff --git a/test/origintracking/common/nat.sv b/test/origintracking/common/nat.sv new file mode 100644 index 000000000..6ca0ec4ac --- /dev/null +++ b/test/origintracking/common/nat.sv @@ -0,0 +1,20 @@ +tracked nonterminal Nat; +synthesized attribute val :: Integer occurs on Nat; +synthesized attribute plusOne :: Nat occurs on Nat; +synthesized attribute minusOne :: Nat occurs on Nat; + +abstract production z +top::Nat ::= +{ + top.val = 0; + top.plusOne = s(top); + top.minusOne = error("minusOne of z()"); +} + +abstract production s +top::Nat ::= a::Nat +{ + top.val = a.val + 1; + top.plusOne = s(a.plusOne); + top.minusOne = new(a); +} \ No newline at end of file diff --git a/test/origintracking/common/parser.sv b/test/origintracking/common/parser.sv new file mode 100644 index 000000000..450c579cc --- /dev/null +++ b/test/origintracking/common/parser.sv @@ -0,0 +1,21 @@ +tracked nonterminal CST; +synthesized attribute transform::CST occurs on CST; + +terminal Foo_t 'foo'; +ignore terminal Space_t ' '; + +concrete production foo +top::CST ::= 'foo' +{ + top.transform = bar(); +} + +abstract production bar +top::CST ::= +{ + top.transform = attachNote logicalLocationNote(txtLoc("bar")) on bar() end; +} + +parser cstParse::CST { + common; +} \ No newline at end of file diff --git a/test/origintracking/force_origins/build.test b/test/origintracking/force_origins/build.test new file mode 100644 index 000000000..e57aa152e --- /dev/null +++ b/test/origintracking/force_origins/build.test @@ -0,0 +1 @@ +run: ../../../support/bin/silver -I .. -o test.jar --clean --force-origins force_origins \ No newline at end of file diff --git a/test/origintracking/force_origins/defns.sv b/test/origintracking/force_origins/defns.sv new file mode 100644 index 000000000..3428ea18d --- /dev/null +++ b/test/origintracking/force_origins/defns.sv @@ -0,0 +1,54 @@ +imports silver:testing; +imports silver:reflect; +imports silver:langutil; + +function identityHashCode +Integer ::= nt::a +{ + return error("Not impl"); +} foreign { + "java" : return "(System.identityHashCode(%nt%))"; +} + +nonterminal Nat; +synthesized attribute val :: Integer occurs on Nat; +synthesized attribute plusOne :: Nat occurs on Nat; +synthesized attribute minusOne :: Nat occurs on Nat; + +abstract production z +top::Nat ::= +{ + top.val = 0; + top.plusOne = s(top); + top.minusOne = error("minusOne of z()"); +} + +abstract production s +top::Nat ::= a::Nat +{ + top.val = a.val + 1; + top.plusOne = s(a.plusOne); + top.minusOne = new(a); +} + +nonterminal CST; +synthesized attribute transform::CST occurs on CST; + +terminal Foo_t 'foo'; +ignore terminal Space_t ' '; + +concrete production foo +top::CST ::= 'foo' +{ + top.transform = bar(); +} + +abstract production bar +top::CST ::= +{ + top.transform = attachNote logicalLocationNote(txtLoc("bar")) on bar() end; +} + +parser cstParse::CST { + force_origins; +} \ No newline at end of file diff --git a/test/origintracking/force_origins/run.test b/test/origintracking/force_origins/run.test new file mode 100644 index 000000000..c9f68b55d --- /dev/null +++ b/test/origintracking/force_origins/run.test @@ -0,0 +1 @@ +test suite test.jar \ No newline at end of file diff --git a/test/origintracking/force_origins/test.sv b/test/origintracking/force_origins/test.sv new file mode 100644 index 000000000..5a6fe7439 --- /dev/null +++ b/test/origintracking/force_origins/test.sv @@ -0,0 +1,104 @@ + +mainTestSuite oitests; + +global three :: Nat = s(s(s(z()))); +global four :: Nat = three.plusOne; +global five :: Nat = four.plusOne; +global two :: Nat = three.minusOne; + +equalityTest(identityHashCode(three), identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(four) of + | just(originOriginInfo(_, link, _, _)) -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(five) of + | just(originOriginInfo(_, link, _, _)) -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(four), Integer, oitests); + +equalityTest( + case getOriginInfoChain(five) of + | _::originOriginInfo(_, link, _, _)::_ -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(three) of + | just(otherOriginInfo(setInGlobalOIT(), _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(two) of + | just(originAndRedexOriginInfo(_, _, _, r, _, _)) -> identityHashCode(r) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(two) of + | just(originAndRedexOriginInfo(setAtNewOIT(), _, _, _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(four) of + | just(originOriginInfo(setAtConstructionOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +global reflectedThree :: AST = reflect(three); + +equalityTest( + case getOriginInfo(reflectedThree) of + | just(originOriginInfo(setFromReflectionOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(reflectedThree) of + | just(originOriginInfo(_, o, _, _)) -> identityHashCode(o) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +global reifiedThree :: Nat = reify(reflectedThree).fromRight; + +equalityTest( + case getOriginInfo(reifiedThree) of + | just(originOriginInfo(setFromReificationOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(reifiedThree) of + | just(originOriginInfo(_, o, _, _)) -> identityHashCode(o) + | _ -> -1 + end, + identityHashCode(reflectedThree), Integer, oitests); + +-- -- -- + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree).unparse, + ":1:2", String, oitests); --parsed location + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree.transform).unparse, + ":1:2", String, oitests); --transitive parsed location + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree.transform.transform).unparse, + "bar", String, oitests); --logicalLocationNote \ No newline at end of file diff --git a/test/origintracking/no_origins/build.test b/test/origintracking/no_origins/build.test new file mode 100644 index 000000000..3cc0679e7 --- /dev/null +++ b/test/origintracking/no_origins/build.test @@ -0,0 +1 @@ +run: ../../../support/bin/silver -I .. -o test.jar --clean --no-origins no_origins \ No newline at end of file diff --git a/test/origintracking/no_origins/run.test b/test/origintracking/no_origins/run.test new file mode 100644 index 000000000..c9f68b55d --- /dev/null +++ b/test/origintracking/no_origins/run.test @@ -0,0 +1 @@ +test suite test.jar \ No newline at end of file diff --git a/test/origintracking/no_origins/test.sv b/test/origintracking/no_origins/test.sv new file mode 100644 index 000000000..b926262b8 --- /dev/null +++ b/test/origintracking/no_origins/test.sv @@ -0,0 +1,48 @@ +imports common; + +mainTestSuite oitests; + +global three :: Nat = s(s(s(z()))); +global four :: Nat = three.plusOne; +global five :: Nat = four.plusOne; +global two :: Nat = three.minusOne; + +equalityTest(identityHashCode(three), identityHashCode(three), Integer, oitests); + +equalityTest( + getOriginInfo(three).isJust, + false, Boolean, oitests); + +equalityTest( + getOriginInfo(four).isJust, + false, Boolean, oitests); + +equalityTest( + getOriginInfo(five).isJust, + false, Boolean, oitests); + +equalityTest( + getOriginInfo(two).isJust, + false, Boolean, oitests); + +global reflectedThree :: AST = reflect(three); + +equalityTest( + getOriginInfo(reflectedThree).isJust, + false, Boolean, oitests); + +global reifiedThree :: Nat = reify(reflectedThree).fromRight; + +equalityTest( + getOriginInfo(reifiedThree).isJust, + false, Boolean, oitests); + +-- -- -- + +equalityTest( + getOriginInfo(cstParse(" foo ", "").parseTree).isJust, + false, Boolean, oitests); + +equalityTest( + getParsedOriginLocation(cstParse(" foo ", "").parseTree).isJust, + false, Boolean, oitests); \ No newline at end of file diff --git a/test/origintracking/no_redex/build.test b/test/origintracking/no_redex/build.test new file mode 100644 index 000000000..45cfa6486 --- /dev/null +++ b/test/origintracking/no_redex/build.test @@ -0,0 +1 @@ +run: ../../../support/bin/silver -I .. -o test.jar --clean --no-redex no_redex \ No newline at end of file diff --git a/test/origintracking/no_redex/run.test b/test/origintracking/no_redex/run.test new file mode 100644 index 000000000..c9f68b55d --- /dev/null +++ b/test/origintracking/no_redex/run.test @@ -0,0 +1 @@ +test suite test.jar \ No newline at end of file diff --git a/test/origintracking/no_redex/test.sv b/test/origintracking/no_redex/test.sv new file mode 100644 index 000000000..71772a118 --- /dev/null +++ b/test/origintracking/no_redex/test.sv @@ -0,0 +1,13 @@ +imports common; + +mainTestSuite oitests; + +global three :: Nat = s(s(s(z()))); +global two :: Nat = three.minusOne; + +equalityTest( + case getOriginInfo(two) of + | just(originAndRedexOriginInfo(_, _, _, _, _, _)) -> "NO" + | x -> "OK" + end, + "OK", String, oitests); \ No newline at end of file diff --git a/test/origintracking/noflags/build.test b/test/origintracking/noflags/build.test new file mode 100644 index 000000000..7e8c20878 --- /dev/null +++ b/test/origintracking/noflags/build.test @@ -0,0 +1 @@ +run: ../../../support/bin/silver -I .. -o test.jar --clean noflags \ No newline at end of file diff --git a/test/origintracking/noflags/run.test b/test/origintracking/noflags/run.test new file mode 100644 index 000000000..c9f68b55d --- /dev/null +++ b/test/origintracking/noflags/run.test @@ -0,0 +1 @@ +test suite test.jar \ No newline at end of file diff --git a/test/origintracking/noflags/test.sv b/test/origintracking/noflags/test.sv new file mode 100644 index 000000000..38ecfa4e8 --- /dev/null +++ b/test/origintracking/noflags/test.sv @@ -0,0 +1,105 @@ +imports common; + +mainTestSuite oitests; + +global three :: Nat = s(s(s(z()))); +global four :: Nat = three.plusOne; +global five :: Nat = four.plusOne; +global two :: Nat = three.minusOne; + +equalityTest(identityHashCode(three), identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(four) of + | just(originOriginInfo(_, link, _, _)) -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(five) of + | just(originOriginInfo(_, link, _, _)) -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(four), Integer, oitests); + +equalityTest( + case getOriginInfoChain(five) of + | _::originOriginInfo(_, link, _, _)::_ -> identityHashCode(link) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(three) of + | just(otherOriginInfo(setInGlobalOIT(), _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(two) of + | just(originAndRedexOriginInfo(_, _, _, r, _, _)) -> identityHashCode(r) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +equalityTest( + case getOriginInfo(two) of + | just(originAndRedexOriginInfo(setAtNewOIT(), _, _, _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(four) of + | just(originOriginInfo(setAtConstructionOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +global reflectedThree :: AST = reflect(three); + +equalityTest( + case getOriginInfo(reflectedThree) of + | just(originOriginInfo(setFromReflectionOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(reflectedThree) of + | just(originOriginInfo(_, o, _, _)) -> identityHashCode(o) + | _ -> -1 + end, + identityHashCode(three), Integer, oitests); + +global reifiedThree :: Nat = reify(reflectedThree).fromRight; + +equalityTest( + case getOriginInfo(reifiedThree) of + | just(originOriginInfo(setFromReificationOIT(), _, _, _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); + +equalityTest( + case getOriginInfo(reifiedThree) of + | just(originOriginInfo(_, o, _, _)) -> identityHashCode(o) + | _ -> -1 + end, + identityHashCode(reflectedThree), Integer, oitests); + +-- -- -- + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree).unparse, + ":1:2", String, oitests); --parsed location + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree.transform).unparse, + ":1:2", String, oitests); --transitive parsed location + +equalityTest( + getParsedOriginLocationOrFallback(cstParse(" foo ", "").parseTree.transform.transform).unparse, + "bar", String, oitests); --logicalLocationNote \ No newline at end of file diff --git a/test/origintracking/tracing_origins/build.test b/test/origintracking/tracing_origins/build.test new file mode 100644 index 000000000..6095be24e --- /dev/null +++ b/test/origintracking/tracing_origins/build.test @@ -0,0 +1 @@ +run: ../../../support/bin/silver -I .. -o test.jar --clean --tracing-origins tracing_origins \ No newline at end of file diff --git a/test/origintracking/tracing_origins/run.test b/test/origintracking/tracing_origins/run.test new file mode 100644 index 000000000..c9f68b55d --- /dev/null +++ b/test/origintracking/tracing_origins/run.test @@ -0,0 +1 @@ +test suite test.jar \ No newline at end of file diff --git a/test/origintracking/tracing_origins/test.sv b/test/origintracking/tracing_origins/test.sv new file mode 100644 index 000000000..b20495f0e --- /dev/null +++ b/test/origintracking/tracing_origins/test.sv @@ -0,0 +1,20 @@ +imports common; + +mainTestSuite oitests; + +global three :: Nat = s(s(s(z()))); + +function constructFour +Nat ::= +{ + return three.plusOne; +} + +equalityTest( + case getOriginInfo(constructFour()) of + | just(originAndRedexOriginInfo(_, _, + [traceNote("nat.sv:18:16")], _, + [traceNote("test.sv:14:27"), traceNote("test.sv:10:15")], _)) -> "OK" + | _ -> "NO" + end, + "OK", String, oitests); diff --git a/test/patt/Basics.sv b/test/patt/Basics.sv index deac25c3a..1e7db0c60 100644 --- a/test/patt/Basics.sv +++ b/test/patt/Basics.sv @@ -113,6 +113,7 @@ Integer ::= p::MyTriple Maybe> return case p of mytriple(aa, bb, just(cc)) -> aa + cc | mytriple(bb, just(aa), cc) -> aa + bb +| _ -> error("Added to make this a complete match to avoid a warning") end; } @@ -125,9 +126,10 @@ Integer ::= p::Pair { return case p of | pair(1,2) -> 1 -| core:pair(1,_) -> 2 +| silver:core:pair(1,_) -> 2 | pair(2,1) -> 3 -| core:pair(_,1) -> 4 +| silver:core:pair(_,1) -> 4 +| _ -> 5 end; } diff --git a/test/patt/Completeness.sv b/test/patt/Completeness.sv new file mode 100644 index 000000000..95c1a0f4c --- /dev/null +++ b/test/patt/Completeness.sv @@ -0,0 +1,482 @@ +grammar patt; + + +--Integers +warnCode "not exhaustive" { + function fun_integer + String ::= + { + return case 0 of + | 0 -> "0" + | 2 -> "2" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_integer_complete + String ::= + { + return case 0 of + | 0 -> "1" + | 2 -> "2" + | _ -> "other" + end; + } +} + + +--Floats +warnCode "not exhaustive" { + function fun_float + String ::= + { + return case 1.0 of + | 0.0 -> "0.0" + | 1.0 -> "1.0" + | 99.9 -> "99.9" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_float_complete + String ::= + { + return case 1.0 of + | 0.0 -> "0.0" + | 1.0 -> "1.0" + | _ -> "other" + end; + } +} + + +--Strings +warnCode "not exhaustive" { + function fun_string + String ::= + { + return case "x" of + | "" -> "" + | "aaa" -> "aaa" + | "*" -> "*" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_string_complete + String ::= + { + return case "x" of + | "" -> "" + | "aaa" -> "aaa" + | x -> "other" + end; + } +} + + +--Booleans +warnCode "not exhaustive" { + function fun_only_true + String ::= + { + return case true of + | true -> "true" + end; + } +} + +warnCode "not exhaustive" { + function fun_only_false + String ::= + { + return case true of + | false -> "false" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_bool_constructors_complete + String ::= + { + return case true of + | true -> "true" + | false -> "false" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_bool_var_complete + String ::= + { + return case true of + | false -> "false" + | var -> "other" + end; + } +} + + +--Lists +warnCode "not exhaustive" { + function fun_list_no_nil + String ::= + { + return case [1, 2] of + | 1::2::_ -> "1" + | 1::_ -> "2" + | _::_ -> "3" + end; + } +} + +warnCode "not exhaustive" { + function fun_list_no_cons + String ::= + { + return case [1, 2] of + | [] -> "nil" + end; + } +} + +warnCode "not exhaustive" { + function fun_list_no_general_list + String ::= + { + return case [1, 2] of + | [] -> "nil" + | [_] -> "1" + | 1::_::_ -> "2" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_list_complete + String ::= + { + return case [1, 2] of + | [] -> "nil" + | 1::_::[] -> "1" + | _::_ -> "any" + end; + } +} + +--Correct display of list nesting +--Nothing else should require special treatment for display, since no +-- other patterns should rely on grouping +warnCode "(_::_)::_" { + function fun_list_complete_nested + String ::= + { + return case [[1, 2]] of + | [] -> "nil" + | []::tl -> "1" + end; + } +} + + +--Maybe +warnCode "not exhaustive" { + function fun_maybe_no_nothing + String ::= + { + return case nothing() of + | just(0) -> "just 0" + | just(x) -> "just x" + end; + } +} + +warnCode "not exhaustive" { + function fun_maybe_no_just + String ::= + { + return case nothing() of + | nothing() -> "nothing" + end; + } +} + +warnCode "not exhaustive" { + function fun_maybe_no_general_just + String ::= + { + return case nothing() of + | nothing() -> "nothing" + | just(0) -> "just 0" + | just(-1) -> "just -1" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_maybe_complete_constructors + String ::= + { + return case nothing() of + | nothing() -> "nothing" + | just(15) -> "just 15" + | just(_) -> "other" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_maybe_complete_var + String ::= + { + return case nothing() of + | nothing() -> "nothing" + | just(15) -> "just 15" + | _ -> "other" + end; + } +} + + +--Closed Nonterminal +closed nonterminal Closed; + +abstract production closed1 +top::Closed ::= +{ } + +abstract production closed2 +top::Closed ::= c::Closed +{ } + +warnCode "not exhaustive" { + function fun_closed_no_default + String ::= + { + return case closed1() of + | closed1() -> "1" + | closed2(closed1()) -> "2" + | closed2(_) -> "3" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_closed_complete + String ::= + { + return case closed1() of + | closed1() -> "1" + | closed2(closed1()) -> "2" + | closed2(_) -> "3" + | _ -> "other" + end; + } +} + + +--Forwarding Nonterminal +nonterminal Abstract; + +abstract production abstract1 +top::Abstract ::= +{ } + +abstract production abstract2 +top::Abstract ::= a::Abstract +{ } + +abstract production abstract3 +top::Abstract ::= a1::Abstract a2::Abstract +{ } + +abstract production abstract4 +top::Abstract ::= a::Abstract +{ forwards to abstract3(a, a); } + +warnCode "not exhaustive" { + function fun_nonterminal_missing_1 + String ::= + { + return case abstract1() of + | abstract2(_) -> "1" + | abstract3(_, abstract1()) -> "2" + | abstract3(_, _) -> "3" + end; + } +} + +warnCode "not exhaustive" { + function fun_nonterminal_missing_2 + String ::= + { + return case abstract1() of + | abstract1() -> "1" + | abstract4(abstract1()) -> "2" + | abstract3(_, _) -> "3" + end; + } +} + +warnCode "not exhaustive" { + function fun_nonterminal_missing_3 + String ::= + { + return case abstract1() of + | abstract2(_) -> "1" + | abstract1() -> "2" + | abstract4(_) -> "3" + end; + } +} + +warnCode "not exhaustive" { + function fun_nonterminal_child_combination + String ::= + { + return case abstract1() of + | abstract1() -> "0" + | abstract2(_) -> "1" + | abstract3(_, abstract1()) -> "2" + | abstract3(abstract1(), _) -> "3" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_nonterminal_complete_var + String ::= + { + return case abstract1() of + | abstract1() -> "0" + | abstract2(_) -> "1" + | _ -> "3" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_nonterminal_complete_constructors + String ::= + { + return case abstract1() of + | abstract1() -> "0" + | abstract2(abstract1()) -> "1" + | abstract2(abstract2(_)) -> "2" + | abstract2(abstract3(_, _)) -> "3" + | abstract3(_, _) -> "4" + end; + } +} + + +--When Clauses +warnCode "not exhaustive" { + function fun_when_clauses + String ::= + { + return case true of + | true when 5 > 3 -> "1" + | false -> "2" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_when_clauses_complete + String ::= + { + return case true of + | true when 5 > 3 -> "1" + | false -> "2" + | true -> "3" + end; + } +} + + +--Multiple Patterns +warnCode "not exhaustive" { + function fun_multiple_basic + String ::= + { + return case 1, 2, 3 of + | 1, 2, 3 -> "1" + | 1, _, 3 -> "2" + | _, _, 4 -> "3" + end; + } +} + +warnCode "not exhaustive" { + function fun_multiple_combination + String ::= + { + return case 1, 2, 3 of + | 1, _, _ -> "1" + | _, 2, _ -> "2" + | _, _, 3 -> "3" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_multiple_complete + String ::= + { + return case 1, 2, 3 of + | 1, _, _ -> "1" + | _, 2, 3 -> "2" + | _, _, _ -> "3" + end; + } +} + +noWarnCode "not exhaustive" { + function fun_test_nongrouped_completeness + String ::= + { + return + case just(1), just(2) of + | nothing(), nothing() -> "" + | just(0), nothing() -> "" + | just(expected), just(actual) -> "Incorrect return type, expected " + | nothing(), just(actual) -> "Unexpected return" + | just(expected), nothing() -> "Expected return value, but found valueless return" + end; + } +} + +warnCode "not exhaustive" { + function fun_test_incompleteness_under_constructor_combination + String ::= + { + return + case (3, 4), 0 of + | (1, 2), _ -> "first" + | (_, _), 8 -> "second" + end; + } +} + + +--Check not exhaustive if all patterns have conditions +warnCode "not exhaustive" { + function fun_test_incompleteness_all_conditions + String ::= + { + return + case 3, 4 of + | 1, y when y > 2 -> "first" + | x, 3 when x < 4 -> "second" + | x, y when y matches 15 -> "third" + | x, y when x == y -> "fourth" + end; + } +} + diff --git a/test/patt/Existential.sv b/test/patt/Existential.sv index 2dd12d3e2..ebf93c4cc 100644 --- a/test/patt/Existential.sv +++ b/test/patt/Existential.sv @@ -15,6 +15,14 @@ String ::= v::Existential end; } +function toStringFromInteger +String ::= v::Integer +{ return toString(v) ; } + +function toStringFromFloat +String ::= v::Float +{ return toString(v) ; } + equalityTest ( applyExist(existentialprod(1, toStringFromInteger)), "1", String, pat_tests ) ; equalityTest ( applyExist(existentialprod(2, toStringFromInteger)), "2", String, pat_tests ) ; equalityTest ( applyExist(existentialprod(1.0, toStringFromFloat)), "1.0", String, pat_tests ) ; diff --git a/test/patt/Nested.sv b/test/patt/Nested.sv new file mode 100644 index 000000000..dffecc11f --- /dev/null +++ b/test/patt/Nested.sv @@ -0,0 +1,76 @@ +grammar patt; + +-- Fixing #495 to support arbitrary nesting of patterns +function nestedlists +String ::= alist :: [[Integer]] +{ + return case alist of + | (head::firsttail)::finaltail -> "NotEmpty/NotEmpty" + | _::finaltail -> "Empty/NotEmpty" + | [] -> "Empty" + end; +} + +function nestedints +Integer ::= i ::Integer +{ + return case i of + | 0 -> 10 + | (1) -> 11 + | ((2)) -> 12 + | _ -> 0 + end; +} + +nonterminal Nest; +production nest_empty +n::Nest ::= +{ } + +production nest_int +n::Nest ::= i::Integer +{ } + +production nest_double +n::Nest ::= n1::Nest n2::Nest +{ } + +function nestedprods +String ::= n::Nest +{ + return case n of + | (nest_empty()) -> "nest_empty" + | ((nest_int((2)))) -> "nest_int_2" + | (nest_int((_))) -> "nest_int" + | ((nest_double((nest_empty()),(nest_int((_)))))) -> "nest_double_specific" + | (((nest_double(((_)),_)))) -> "nest_double_general" + end; +} + +equalityTest ( nestedlists( [ [1,2] , [3,4] ] ), "NotEmpty/NotEmpty", String, pat_tests ) ; +equalityTest ( nestedlists( [ [] , [3,4] ] ), "Empty/NotEmpty", String, pat_tests ) ; +equalityTest ( nestedlists( [ ] ), "Empty", String, pat_tests ) ; + +equalityTest ( nestedints( 0 ), 10, Integer, pat_tests ) ; +equalityTest ( nestedints( 1 ), 11, Integer, pat_tests ) ; +equalityTest ( nestedints( 2 ), 12, Integer, pat_tests ) ; +equalityTest ( nestedints( 3 ), 0, Integer, pat_tests ) ; + +equalityTest ( nestedprods( nest_empty() ), "nest_empty", String, pat_tests ) ; +equalityTest ( nestedprods( nest_int(2) ), "nest_int_2", String, pat_tests ) ; +equalityTest ( nestedprods( nest_double( nest_empty(), nest_int(2)) ), "nest_double_specific", String, pat_tests ) ; +equalityTest ( nestedprods( nest_double( nest_int(3), nest_empty()) ), "nest_double_general", String, pat_tests ) ; + + +wrongCode "1.0 is a Float but we're trying to match against Integer" { + nonterminal CompletelyValidNonterminal; + + function funfoo + String ::= n::Integer + { + return case n of + | (1.0) -> "Fail" + | _ -> "OK" + end; + } +} diff --git a/test/patt/Normal.sv b/test/patt/Normal.sv index 61e37d093..2327dd523 100644 --- a/test/patt/Normal.sv +++ b/test/patt/Normal.sv @@ -6,6 +6,7 @@ Boolean ::= s::String return case s of "true" -> true | "false" -> false + | _ -> true end; } @@ -15,6 +16,7 @@ Boolean ::= s::String return case s of | "true" -> true | "false" -> false + | _ -> true end; } @@ -28,6 +30,7 @@ Boolean ::= s::Maybe just("true") -> true | just("false") -> false | nothing() -> false + | _ -> false end; } @@ -42,6 +45,7 @@ Boolean ::= s::Maybe> just(just("true")) -> true | just(nothing()) -> false | nothing() -> false + | _ -> false end; } @@ -181,10 +185,10 @@ wrongCode "2 values, but this rule has 3" { } -- Silver used to crash because pattern used to rely on signature. -global signatureTest :: Integer = case just(1) of just(x) -> x end; +global signatureTest :: Integer = case just(1) of just(x) -> x | _ -> 0 end; -- That toString works on a pattern expression (type information is available) -equalityTest ( toString(case just(1) of just(x) -> x end), "1", String, pat_tests ) ; +equalityTest ( toString(case just(1) of just(x) -> x | _ -> 0 end), "1", String, pat_tests ) ; ------------------------ diff --git a/test/patt/Test.sv b/test/patt/Test.sv index 90c54ce7d..472a38636 100644 --- a/test/patt/Test.sv +++ b/test/patt/Test.sv @@ -1,7 +1,6 @@ grammar patt; imports silver:testing ; -imports lib:extcore ; nonterminal T; @@ -61,7 +60,52 @@ equalityTest ( tNameAll(d()), "d", String, pat_tests ) ; ---- Part 2: GADTS +--- Part 2: Match Order +nonterminal MatchOrder; + +abstract production mo1 +top::MatchOrder ::= +{ } + +abstract production mo2 +top::MatchOrder ::= +{ forwards to mo1(); } + +abstract production mo3 +top::MatchOrder ::= x::MatchOrder y::MatchOrder +{ } + +function matchOrder1 +String ::= m::MatchOrder +{ + return case m of + | mo3(mo1(), mo2()) -> "mo3 of mo1, mo2\n" + | mo3(mo2(), mo1()) -> "mo3 of mo2, mo1\n" + | _ -> "other\n" + end; +} + +function matchOrder2 +String ::= m::MatchOrder +{ + return case m of + | mo3(mo1(), mo2()) -> "mo3 of mo1, mo2\n" + | mo3(mo2(), mo3(_, _)) -> "mo3 of mo2, mo3\n" + | _ -> "other\n" + end; +} + +equalityTest( matchOrder1( mo3(mo2(), mo2()) ), "mo3 of mo1, mo2\n", String, pat_tests ); --matches through forward on first patt +equalityTest( matchOrder1( mo3(mo2(), mo1()) ), "mo3 of mo2, mo1\n", String, pat_tests ); --misses first patt because mo1 doesn't forward to mo2 +equalityTest( matchOrder1( mo3(mo1(), mo1()) ), "other\n", String, pat_tests ); --misses first two patts because mo1 doesn't forward to mo2 + +equalityTest( matchOrder2( mo3(mo2(), mo2()) ), "mo3 of mo1, mo2\n", String, pat_tests ); --matches through forward on first patt +equalityTest( matchOrder2( mo3(mo2(), mo3(mo1(), mo1())) ), "mo3 of mo2, mo3\n", String, pat_tests ); +equalityTest( matchOrder2( mo3(mo1(), mo3(mo1(), mo1())) ), "other\n", String, pat_tests ); --misses second patt because mo1 doesn't forward to mo2 + + + +--- Part 3: GADTS nonterminal Arrow; @@ -70,14 +114,14 @@ abstract production unitT t::Type ::= {} abstract production arrow t::Type> ::= Type Type {} nonterminal Eq; -abstract production eq e::Eq ::= {} +abstract production refl e::Eq ::= {} function typeEquals Maybe> ::= ta::Type tb::Type { return match ta return Maybe> with unitT() -> match tb return Maybe> with - unitT() -> just(eq()) + unitT() -> just(refl()) else -> nothing() end | arrow(aa, ab) -> @@ -85,11 +129,11 @@ Maybe> ::= ta::Type tb::Type arrow(ba, bb) -> match decorate typeEquals(aa, ba) with {} return Maybe> with just(lrn1) -> match decorate lrn1 with {} return Maybe> with - eq() -> + refl() -> match decorate typeEquals(ab, bb) with {} return Maybe> with just(lrn2) -> match decorate lrn2 with {} return Maybe> with - eq() -> just(eq()) + refl() -> just(refl()) else -> nothing() end else -> nothing() @@ -132,7 +176,7 @@ wrongCode "pattern expression should have type" { return match ta return Maybe> with arrow(aa, ab) -> match tb return Maybe> with - arrow(ba, bb) -> just(eq()) + arrow(ba, bb) -> just(refl()) else -> error("") end else -> error("") diff --git a/test/patt/TestNonPrim.sv b/test/patt/TestNonPrim.sv index 1d5a31f34..0eeecfab5 100644 --- a/test/patt/TestNonPrim.sv +++ b/test/patt/TestNonPrim.sv @@ -5,10 +5,10 @@ function typeEqualsNonPrim Maybe> ::= ta::Type tb::Type { return case ta, tb of - unitT(), unitT() -> just(eq()) + unitT(), unitT() -> just(refl()) | arrow(aa, ab), arrow(ba, bb) -> case typeEqualsNonPrim(aa,ba), typeEqualsNonPrim(ab, bb) of - just(eq()), just(eq()) -> just(eq()) + just(refl()), just(refl()) -> just(refl()) | _, _ -> nothing() end | _, _ -> nothing() diff --git a/test/patt/When.sv b/test/patt/When.sv index c7ac92664..d3f95a943 100644 --- a/test/patt/When.sv +++ b/test/patt/When.sv @@ -88,3 +88,32 @@ wrongCode "Pattern has overlapping cases" { } +--Test that things work correctly with the matches keyword for whens +function testNonterminalMatchesWhenMatches +Integer ::= nn1::Nonsense nn2::Nonsense +{ + return case nn1, nn2 of + | n3(n1(), i1), n3(c1, i2) when c1 matches n1() -> 0 + | n3(c1, i1), n3(_, i2) when c1 matches n2(a1, b1) -> 1 + | n3(_, i1), n3(_, i2) -> 2 + | n2(vara, n3(varb, i)), _ when i > 5 matches true -> 3 + | _, _ -> 4 + end; +} + +equalityTest ( testNonterminalMatchesWhenMatches( n3(n1(), 5), n3(n1(), 5) ), + 0, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n3(n2(n1(), n1()), 5), n3(n4(), 5) ), + 1, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n3(n1(), 5), n3(n4(), 5) ), + 2, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n2(n4(), n3(n4(), 6)), n4() ), + 3, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n2(n4(), n3(n4(), 0)), n4() ), + 4, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n1(), n1() ), + 4, Integer, pat_tests ) ; +equalityTest ( testNonterminalMatchesWhenMatches( n2(n4(), n1()), n2(n4(), n1()) ), + 4, Integer, pat_tests ) ; + + diff --git a/test/patt/silver-compile b/test/patt/silver-compile index 2d5785955..eef1556bd 100755 --- a/test/patt/silver-compile +++ b/test/patt/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/test/performance/Append.sv b/test/performance/Append.sv index 88934c9aa..3bbbf863c 100644 --- a/test/performance/Append.sv +++ b/test/performance/Append.sv @@ -1,7 +1,6 @@ grammar performance; imports silver:testing ; -imports lib:extcore ; mainTestSuite performance_tests ; diff --git a/test/performance/silver-compile b/test/performance/silver-compile index d1c04eaf3..1a92d0451 100755 --- a/test/performance/silver-compile +++ b/test/performance/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/test/runtest b/test/runtest index 5877a1d1a..7db923ddb 100755 --- a/test/runtest +++ b/test/runtest @@ -1,5 +1,5 @@ -#!/bin/bash +#!/usr/bin/env bash silver --clean silver:testing:bin && \ - java -Xss8M -Xmx2000M -jar silver.testing.bin.jar . + java -Xss16M -Xmx4G -jar silver.testing.bin.jar ${1:-"."} diff --git a/test/set-generated-dir b/test/set-generated-dir new file mode 100755 index 000000000..05bab2afa --- /dev/null +++ b/test/set-generated-dir @@ -0,0 +1,5 @@ +#!/usr/bin/env bash + +escaped=${1//\//\\\/} + +find $2 -name '*.test' -exec sed -i'' "s/\(run: [^ ]*silver[^ ]*\) /\1 -G $escaped /g" {} \; diff --git a/test/silver_construction/Main.sv b/test/silver_construction/Main.sv new file mode 100644 index 000000000..d14e19da9 --- /dev/null +++ b/test/silver_construction/Main.sv @@ -0,0 +1,5 @@ +grammar silver_construction; + +imports silver:testing; + +mainTestSuite silver_construction_tests; diff --git a/test/silver_construction/SilverConstruction.sv b/test/silver_construction/SilverConstruction.sv new file mode 100644 index 000000000..75aa9ee6e --- /dev/null +++ b/test/silver_construction/SilverConstruction.sv @@ -0,0 +1,46 @@ +grammar silver_construction; + +import silver:compiler:extension:patternmatching; +import silver:compiler:extension:silverconstruction; +import silver:compiler:definition:core; +import silver:compiler:definition:type:syntax; +import silver:compiler:modification:list; + +-- TESTING Silver_Expr + +function testExprBool +silver:compiler:definition:core:Expr ::= v1::Boolean +{ + return if v1 then Silver_Expr {true} else Silver_Expr {false}; +} + +equalityTest(hackUnparse(testExprBool(true)), "silver:compiler:definition:core:trueConst('true')", String, silver_construction_tests); +equalityTest(hackUnparse(testExprBool(false)), "silver:compiler:definition:core:falseConst('false')", String, silver_construction_tests); + +equalityTest(hackUnparse(Silver_Expr{1}), "silver:compiler:definition:core:intConst('1')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_Expr{1.343}), "silver:compiler:definition:core:floatConst('1.343')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_Expr{[dog, doggy, doggies]}), "silver:compiler:modification:list:fullList('[', silver:compiler:definition:core:exprsCons(silver:compiler:definition:core:baseExpr(silver:compiler:definition:core:qNameId(silver:compiler:definition:core:nameIdLower('dog'))), ',', silver:compiler:definition:core:exprsCons(silver:compiler:definition:core:baseExpr(silver:compiler:definition:core:qNameId(silver:compiler:definition:core:nameIdLower('doggy'))), ',', silver:compiler:definition:core:exprsSingle(silver:compiler:definition:core:baseExpr(silver:compiler:definition:core:qNameId(silver:compiler:definition:core:nameIdLower('doggies')))))), ']')", String, silver_construction_tests); + +-- TESTING Silver_Pattern + +function testPatternBools +Pattern ::= v1::Boolean v2::Boolean +{ + local a::Pattern = if v1 then Silver_Pattern {true} else Silver_Pattern {false}; + local b::Pattern = if v2 then Silver_Pattern {true} else Silver_Pattern {false}; + return Silver_Pattern { silver:core:pair($Pattern{a}, $Pattern{b}) }; +} + +equalityTest(hackUnparse(testPatternBools(true, true)), "silver:compiler:extension:patternmatching:prodAppPattern(silver:compiler:definition:core:qNameCons(silver:compiler:definition:core:nameIdLower('silver'), ':', silver:compiler:definition:core:qNameCons(silver:compiler:definition:core:nameIdLower('core'), ':', silver:compiler:definition:core:qNameId(silver:compiler:definition:core:nameIdLower('pair')))), '(', silver:compiler:extension:patternmatching:patternList_snoc(silver:compiler:extension:patternmatching:patternList_one(silver:compiler:extension:patternmatching:truePattern('true')), ',', silver:compiler:extension:patternmatching:truePattern('true')), ')')", String, silver_construction_tests); +equalityTest(hackUnparse(testPatternBools(false, true)), "silver:compiler:extension:patternmatching:prodAppPattern(silver:compiler:definition:core:qNameCons(silver:compiler:definition:core:nameIdLower('silver'), ':', silver:compiler:definition:core:qNameCons(silver:compiler:definition:core:nameIdLower('core'), ':', silver:compiler:definition:core:qNameId(silver:compiler:definition:core:nameIdLower('pair')))), '(', silver:compiler:extension:patternmatching:patternList_snoc(silver:compiler:extension:patternmatching:patternList_one(silver:compiler:extension:patternmatching:falsePattern('false')), ',', silver:compiler:extension:patternmatching:truePattern('true')), ')')", String, silver_construction_tests); + +equalityTest(hackUnparse(Silver_Pattern {8}), "silver:compiler:extension:patternmatching:intPattern('8')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_Pattern {8.99999}), "silver:compiler:extension:patternmatching:fltPattern('8.99999')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_Pattern {[a,b,c]}), "silver:compiler:extension:patternmatching:listPattern('[', silver:compiler:extension:patternmatching:patternList_snoc(silver:compiler:extension:patternmatching:patternList_snoc(silver:compiler:extension:patternmatching:patternList_one(silver:compiler:extension:patternmatching:varPattern(silver:compiler:definition:core:nameIdLower('a'))), ',', silver:compiler:extension:patternmatching:varPattern(silver:compiler:definition:core:nameIdLower('b'))), ',', silver:compiler:extension:patternmatching:varPattern(silver:compiler:definition:core:nameIdLower('c'))), ']')", String, silver_construction_tests); + +-- TESTING Silver_TypeExpr +equalityTest(hackUnparse(Silver_TypeExpr { Boolean }), "silver:compiler:definition:type:syntax:booleanTypeExpr('Boolean')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_TypeExpr { Integer }), "silver:compiler:definition:type:syntax:integerTypeExpr('Integer')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_TypeExpr { Float }), "silver:compiler:definition:type:syntax:floatTypeExpr('Float')", String, silver_construction_tests); +equalityTest(hackUnparse(Silver_TypeExpr {Pair }), "silver:compiler:definition:type:syntax:appTypeExpr(silver:compiler:definition:type:syntax:nominalTypeExpr(silver:compiler:definition:core:qNameTypeId('Pair')), silver:compiler:definition:type:syntax:bTypeList('<', silver:compiler:definition:type:syntax:typeListCons(silver:compiler:definition:type:syntax:stringTypeExpr('String'), silver:compiler:definition:type:syntax:typeListSingle(silver:compiler:definition:type:syntax:integerTypeExpr('Integer'))), '>'))", String, silver_construction_tests); +equalityTest (hackUnparse(Silver_TypeExpr { [Integer] }), "silver:compiler:modification:list:listTypeExpr('[', silver:compiler:definition:type:syntax:integerTypeExpr('Integer'), ']')", String, silver_construction_tests); diff --git a/test/silver_construction/build.test b/test/silver_construction/build.test new file mode 100644 index 000000000..15937bbb9 --- /dev/null +++ b/test/silver_construction/build.test @@ -0,0 +1 @@ +run: ./silver-compile --clean diff --git a/test/silver_construction/run.test b/test/silver_construction/run.test new file mode 100644 index 000000000..ce3fb0c94 --- /dev/null +++ b/test/silver_construction/run.test @@ -0,0 +1 @@ +test suite test.jar diff --git a/test/silver_construction/silver-compile b/test/silver_construction/silver-compile new file mode 100755 index 000000000..579b05893 --- /dev/null +++ b/test/silver_construction/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../support/bin/silver" "$@"; } + +SRC=.. +GRAMMAR=silver_construction + +silver -I $SRC $@ -o test.jar $GRAMMAR + diff --git a/test/silver_features/.gitignore b/test/silver_features/.gitignore new file mode 100644 index 000000000..af1f51c82 --- /dev/null +++ b/test/silver_features/.gitignore @@ -0,0 +1 @@ +test_out.txt diff --git a/test/silver_features/Collections.sv b/test/silver_features/Collections.sv index aa7b1fa1d..6ea69cde1 100644 --- a/test/silver_features/Collections.sv +++ b/test/silver_features/Collections.sv @@ -112,8 +112,9 @@ synthesized attribute colOr :: Boolean with ||; synthesized attribute colAnd :: Boolean with &&; synthesized attribute colFun :: Maybe with orElse; synthesized attribute colProd :: ColNT with colNode; +synthesized attribute colTCFun :: [Integer] with union; -attribute colList, colOr, colAnd, colFun, colProd occurs on ColNT; +attribute colList, colOr, colAnd, colFun, colProd, colTCFun occurs on ColNT; abstract production colTest1 top::ColNT ::= @@ -128,6 +129,8 @@ top::ColNT ::= top.colFun <- just(1); top.colProd := colLeaf(); top.colProd <- colLeaf(); + top.colTCFun := [1, 2, 3]; + top.colTCFun <- [2, 3, 4]; } equalityTest( colTest1().colList, ["one", "two"], [String], silver_tests ); @@ -136,6 +139,7 @@ equalityTest( colTest1().colAnd, false, Boolean, silver_tests ); equalityTest( colTest1().colFun.isJust, true, Boolean, silver_tests ); equalityTest( fromMaybe(2, colTest1().colFun), 1, Integer, silver_tests ); equalityTest( colTest1().colProd.colSyn, "( a c )( d e ) b ", String, silver_tests ); +equalityTest( colTest1().colTCFun, [1, 2, 3, 4], [Integer], silver_tests ); abstract production colTest2 top::ColNT ::= @@ -150,6 +154,7 @@ top::ColNT ::= top.colFun <- nothing(); top.colProd := colProdLeaf(); top.colProd <- colLeaf(); + top.colTCFun := []; } equalityTest( colTest2().colList, ["one", "two"], [String], silver_tests ); @@ -171,6 +176,7 @@ top::ColNT ::= top.colFun <- just(2); top.colProd := colLeaf(); top.colProd <- colProdLeaf(); + top.colTCFun := []; } equalityTest( colTest3().colList, ["one", "two"], [String], silver_tests ); @@ -180,3 +186,26 @@ equalityTest( colTest3().colFun.isJust, true, Boolean, silver_tests ); equalityTest( fromMaybe(4, colTest3().colFun), 1, Integer, silver_tests ); equalityTest( colTest3().colProd.colSyn, "( a c ) j k b ", String, silver_tests ); +-- Regression test for #209 + +nonterminal NT209 with colSyn; + +production prod209 +top::NT209 ::= +{ + top.colSyn := "foo"; +} + +aspect production prod209 +top::NT209 ::= +{ + top.colSyn <- "bar"; +} + +aspect default production +top::NT209 ::= +{ + top.colSyn <- "baz"; +} + +equalityTest(prod209().colSyn, "foobarbaz", String, silver_tests); diff --git a/test/silver_features/ConvenienceAspects.sv b/test/silver_features/ConvenienceAspects.sv new file mode 100644 index 000000000..9caffed32 --- /dev/null +++ b/test/silver_features/ConvenienceAspects.sv @@ -0,0 +1,200 @@ + + +synthesized attribute value :: Integer; +synthesized attribute prettierprint :: String; + +nonterminal FooExpr with prettierprint, value; + +abstract production subtractFoo +sum::FooExpr ::= l::FooExpr r::FooExpr +{ + sum.prettierprint = l.prettierprint ++ "-" ++ r.prettierprint; + sum.value = l.value - r.value; +} + +abstract production addfoo +sum::FooExpr ::= l::FooExpr r::FooExpr +{ + sum.prettierprint = l.prettierprint ++ "+" ++ r.prettierprint; + sum.value = l.value + r.value; +} + +synthesized attribute foopp :: String; +attribute foopp occurs on FooExpr; + + +aspect foopp on FooExpr of +| addfoo(l, _) -> "foo " ++ l.prettierprint +| subtractFoo(l,r) -> "foo " ++ l.prettierprint ++ "-" ++ r.prettierprint +| _ -> "default" +end; + +synthesized attribute ppList :: [String]; +synthesized attribute hiddenAttr :: String; +nonterminal BarExpr with ppList, value, hiddenAttr; + +abstract production barInit1 +top::BarExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList; + top.value = value; + top.hiddenAttr = toString(value); +} + +abstract production barInit2 +top::BarExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList; + top.value = value + 1; + top.hiddenAttr = "aardvarks"; +} + +abstract production barInit3 +top::BarExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList ++ ["extra"]; + top.value = value; + top.hiddenAttr = toString(value); +} + +abstract production barInit4 +top::BarExpr ::= +{ + top.ppList = []; + top.value = 10; + top.hiddenAttr = "aardvarks"; +} + +attribute foopp occurs on BarExpr; + +aspect foopp on top::BarExpr of +| barInit1([],_) -> "emptyFoopp" +| barInit1(h::t,_) -> h ++ "Foopp" ++ top.hiddenAttr +| barInit2(h :: t,value) -> h ++ " and then " ++ toString(value) +| barInit2([],value) -> toString(value) +| barInit3(_,val) -> toString(val) ++ top.hiddenAttr +| barInit4() -> "Foopp" +| _ -> top.hiddenAttr +end; + + + +wrongCode "Undeclared value 'barInit5'." { + aspect foopp on top::BarExpr of + | barInit5() -> "Foopp" + | _ -> top.hiddenAttr + end; +} + +wrongCode "barInit5 is not a production." { + aspect foopp on top::BarExpr of + | barInit5() -> "Foopp" + | _ -> top.hiddenAttr + end; +} + + +nonterminal BazExpr with ppList, value, hiddenAttr; + +abstract production bazInit1 +top::BazExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList; + top.value = value; + top.hiddenAttr = toString(value); +} + +abstract production bazInit2 +top::BazExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList; + top.value = value + 1; + top.hiddenAttr = "aardvarks"; +} + +abstract production bazInit3 +top::BazExpr ::= ppList::[String] value::Integer +{ + top.ppList = ppList ++ ["extra"]; + top.value = value; + top.hiddenAttr = toString(value); +} + +abstract production bazInit4 +top::BazExpr ::= +{ + top.ppList = []; + top.value = 10; + top.hiddenAttr = "aardvarks"; +} + +synthesized attribute bazList :: [Integer] with ++ occurs on BazExpr; + +aspect production bazInit1 +top::BazExpr ::= ppList::[String] value::Integer +{ + top.bazList := [4]; +} + +aspect bazList on BazExpr using := of +| bazInit2(h::t,value) -> [length(h), value] +| bazInit2([],value) -> [0, value] +| bazInit3(_,val) -> [val] +| bazInit4() -> [] +end; + + +synthesized attribute bagList :: [String] with ++ occurs on BazExpr; + + +aspect bagList on top::BazExpr using <- of +| bazInit2(h::t,value) -> [h, toString(value)] +| bazInit2([],value) -> [toString(value)] +| bazInit3(_,val) -> [top.hiddenAttr] +| bazInit4() -> explode(top.hiddenAttr,"\t") +| _ -> [] +end; + +wrongCode "Undeclared value 'top'." { + aspect foopp on BarExpr of + | barInit1() -> "Foopp" ++ top.hiddenAttr + | _ -> top.hiddenAttr + end; +} + + +synthesized attribute bagList2 :: [String] with ++ occurs on BazExpr; + +warnCode "This pattern and the ones that follow are being ignored." { + aspect bagList2 on top::BazExpr using <- of + | bazInit2(h::t,value) -> [h, toString(value)] + | _ -> [] + | _ -> ["ignored"] + end; +} + +synthesized attribute bagList3 :: [String] with ++ occurs on BazExpr; + +warnCode "This pattern and the ones that follow are being ignored." { + aspect bagList2 on top::BazExpr using <- of + | bazInit2(h::t,value) -> [h, toString(value)] + | bazInit2([],value) -> [toString(value)] + | coolName -> [coolName.hiddenAttr] + | _ -> ["ignored"] + end; +} + + +synthesized attribute bagList4 :: [String] with ++ occurs on BazExpr; + aspect bagList2 on top::BazExpr using <- of + | bazInit2(h::t,value) -> [h, toString(value)] + | coolName -> [coolName.hiddenAttr] + end; + +synthesized attribute gAttribute :: String occurs on BazExpr; +aspect gAttribute on BazExpr of + | bazInit2(h::t,value) -> h ++ toString(value) + | bazInit2([],value) -> toString(value) + | bazInit3(_,val) -> toString(val) + | coolName -> coolName.hiddenAttr +end; diff --git a/test/silver_features/DoNotation.sv b/test/silver_features/DoNotation.sv new file mode 100644 index 000000000..eb2a25815 --- /dev/null +++ b/test/silver_features/DoNotation.sv @@ -0,0 +1,95 @@ + +-- Test Maybe +global doRes1::Maybe = do { + a::Integer <- just(1); + if a > 0 + then pure(2) + else nothing(); +}; + +equalityTest(doRes1, just(2), Maybe, silver_tests); + +-- Test list +global doRes2::[Integer] = do { + a::Integer <- [1, 2, 3]; + b::Integer <- [4, 5, 6]; + return a * b; +}; + +equalityTest(foldr(\a::Integer b::Integer -> a + b, 0, doRes2), 90, Integer, silver_tests); + +-- Test State +global doRes3::State = do { + a::Integer <- getState(); + setState(a + 1); + return a * 2; +}; + +global res3::Pair = runState(doRes3, 2); + +equalityTest(res3.fst, 3, Integer, silver_tests); +equalityTest(res3.snd, 4, Integer, silver_tests); + +-- Test IO +global doRes4::IO = do { + writeFile("test_out.txt", "Hello"); + appendFile("test_out.txt", ", World!"); + readFile("test_out.txt"); +}; + +equalityTest(evalIO(doRes4, unsafeIO()).iovalue, "Hello, World!", String, silver_tests); + +-- Test something that is Applicative but not Monad +nonterminal AMaybe; +production justAL top::AMaybe ::= a {} +production noneAL top::AMaybe ::= {} +instance Functor AMaybe { + map = \ f::(b ::= a) m::AMaybe -> + case m of + | noneAL() -> noneAL() + | justAL(x) -> justAL(f(x)) + end; +} +instance Apply AMaybe { + ap = \ mf::AMaybe<(b ::= a)> m::AMaybe -> + case mf, m of + | justAL(f), justAL(x) -> justAL(f(x)) + | _, _ -> noneAL() + end; +} +instance Applicative AMaybe { + pure = justAL; +} + +global doRes5::Maybe = do { + a::Integer <- just(1); + b::Integer <- just(2); + return a + b; +}; + +equalityTest(doRes5, just(3), Maybe, silver_tests); + +global mdoThing::State = mdo { + b :: Boolean <- getState(); + res :: (Integer ::= Integer) <- pure(\ x::Integer -> + if b + then if x == 0 then 1 else x * res(x - 1) + else if x == 0 then 0 else x + res(x - 1)); + return res; +}; + +equalityTest(evalState(mdoThing, false)(5), 15, Integer, silver_tests); +equalityTest(evalState(mdoThing, true)(5), 120, Integer, silver_tests); + +global mutualMDo::Maybe<(Boolean ::= Integer)> = mdo { + pure(123); + even :: (Boolean ::= Integer) <- pure(\ x::Integer -> if x == 0 then true else odd(x - 1, ())); + pure(456); + let odd :: (Boolean ::= Integer ()) = \ x::Integer () -> if x == 0 then false else even(x - 1); + res::Boolean <- just(even(43)); + return even; +}; +equalityTest(mutualMDo.fromJust(0), true, Boolean, silver_tests); +equalityTest(mutualMDo.fromJust(1), false, Boolean, silver_tests); +equalityTest(mutualMDo.fromJust(2), true, Boolean, silver_tests); +equalityTest(mutualMDo.fromJust(3), false, Boolean, silver_tests); diff --git a/test/silver_features/EqualityAttr.sv b/test/silver_features/EqualityAttr.sv new file mode 100644 index 000000000..c3962dde8 --- /dev/null +++ b/test/silver_features/EqualityAttr.sv @@ -0,0 +1,82 @@ +grammar silver_features; + +nonterminal EqExpr with compareTo, isEqual, compareKey, compare; + +abstract production addEqExpr +top::EqExpr ::= e1::EqExpr e2::EqExpr +{ + propagate compareTo, isEqual, compareKey, compare; +} + +abstract production intEqExpr +top::EqExpr ::= i::Integer +{ + propagate compareTo, isEqual, compareKey, compare; +} + +abstract production appEqExpr +top::EqExpr ::= n::String e::EqExpr +{ + propagate compareTo, isEqual, compareKey, compare; +} + +{- TODO: This should give an error, but the error is in generated code at the moment so we can't test for it +abstract production polyEqExpr +Eq a => top::EqExpr ::= x::a +{ + propagate compareTo, isEqual, compare; +} +-} + +abstract production seqEqExpr +top::EqExpr ::= es::[EqExpr] +{ + propagate compareTo, isEqual, compareKey, compare; +} + +global ee1::EqExpr = addEqExpr(intEqExpr(42), appEqExpr("abc", intEqExpr(5))); +global ee2::EqExpr = addEqExpr(intEqExpr(42), appEqExpr("c", intEqExpr(5))); +global ee3::EqExpr = addEqExpr(appEqExpr("c", intEqExpr(5)), intEqExpr(42)); +global ee4::EqExpr = seqEqExpr([ee1, ee2, ee3]); +global ee5::EqExpr = seqEqExpr([ee3, ee2, ee1]); + +equalityTest(ee1 == ee1, true, Boolean, silver_tests); +equalityTest(ee1 == ee2, false, Boolean, silver_tests); +equalityTest(ee1 == ee3, false, Boolean, silver_tests); +equalityTest(ee1 == ee4, false, Boolean, silver_tests); +equalityTest(ee2 == ee1, false, Boolean, silver_tests); +equalityTest(ee2 == ee2, true, Boolean, silver_tests); +equalityTest(ee2 == ee3, false, Boolean, silver_tests); +equalityTest(ee2 == ee4, false, Boolean, silver_tests); +equalityTest(ee3 == ee1, false, Boolean, silver_tests); +equalityTest(ee3 == ee2, false, Boolean, silver_tests); +equalityTest(ee3 == ee3, true, Boolean, silver_tests); +equalityTest(ee3 == ee4, false, Boolean, silver_tests); +equalityTest(ee4 == ee1, false, Boolean, silver_tests); +equalityTest(ee4 == ee2, false, Boolean, silver_tests); +equalityTest(ee4 == ee3, false, Boolean, silver_tests); +equalityTest(ee4 == ee4, true, Boolean, silver_tests); +equalityTest(ee4 == ee5, false, Boolean, silver_tests); +equalityTest([ee1, ee2, ee3] == [ee1, ee2, ee3], true, Boolean, silver_tests); +equalityTest([ee1, ee2, ee3] == [ee3, ee2, ee1], false, Boolean, silver_tests); + +equalityTest(ee1 < ee1, false, Boolean, silver_tests); +equalityTest(ee1 < ee2, true, Boolean, silver_tests); +equalityTest(ee1 < ee3, false, Boolean, silver_tests); +equalityTest(ee1 < ee4, true, Boolean, silver_tests); +equalityTest(ee2 < ee1, false, Boolean, silver_tests); +equalityTest(ee2 < ee2, false, Boolean, silver_tests); +equalityTest(ee2 < ee3, false, Boolean, silver_tests); +equalityTest(ee2 < ee4, true, Boolean, silver_tests); +equalityTest(ee3 < ee1, true, Boolean, silver_tests); +equalityTest(ee3 < ee2, true, Boolean, silver_tests); +equalityTest(ee3 < ee3, false, Boolean, silver_tests); +equalityTest(ee3 < ee4, true, Boolean, silver_tests); +equalityTest(ee4 < ee1, false, Boolean, silver_tests); +equalityTest(ee4 < ee2, false, Boolean, silver_tests); +equalityTest(ee4 < ee3, false, Boolean, silver_tests); +equalityTest(ee4 < ee4, false, Boolean, silver_tests); +equalityTest(ee4 < ee5, false, Boolean, silver_tests); +equalityTest(ee5 < ee4, true, Boolean, silver_tests); +equalityTest([ee1, ee2, ee3] < [ee1, ee2, ee3], false, Boolean, silver_tests); +equalityTest([ee3, ee2, ee1] < [ee1, ee2, ee3], true, Boolean, silver_tests); diff --git a/test/silver_features/FuncManipulation.sv b/test/silver_features/FuncManipulation.sv index 4f6c1879f..3f1f87c5d 100644 --- a/test/silver_features/FuncManipulation.sv +++ b/test/silver_features/FuncManipulation.sv @@ -52,39 +52,72 @@ equalityTest( head([twoArgFunction])("s", 1), 1, Integer, silver_tests ) ; nonterminal Section; -synthesized attribute sec_valid :: Integer occurs on Section; -synthesized attribute sec_inv :: a; attribute sec_inv occurs on Section; -synthesized attribute sec_inv2 :: String; -- no occurs -inherited attribute sec_inv3 :: Integer occurs on Section; -- inh +synthesized attribute sec1 :: Integer occurs on Section; +synthesized attribute sec2 :: a; attribute sec2 occurs on Section; +inherited attribute sec3 :: Integer occurs on Section; -- inh + +synthesized attribute sec_inv1 :: String; -- no occurs abstract production section top::Section ::= { - top.sec_valid = 2; - top.sec_inv = "hi"; + top.sec1 = 2; + top.sec2 = "hi"; } -equalityTest( (.sec_valid)(decorate section() with {}), 2, Integer, silver_tests ); -equalityTest( (.sec_valid)(section()), 2, Integer, silver_tests ); +equalityTest( (.sec1)(decorate section() with {}), 2, Integer, silver_tests ); +equalityTest( (.sec1)(section()), 2, Integer, silver_tests ); global sections :: [Section] = [section(), section()]; +global decSections :: [Decorated Section with {sec3}] = + [decorate section() with {sec3 = 42;}, decorate section() with {sec3 = 221;}]; + +equalityTest( map((.sec1), sections), [2,2], [Integer], silver_tests ); +equalityTest( map((.sec2), sections), ["hi", "hi"], [String], silver_tests ); +equalityTest( map((.sec1), decSections), [2,2], [Integer], silver_tests ); +equalityTest( map((.sec2), decSections), ["hi", "hi"], [String], silver_tests ); +equalityTest( map((.sec3), decSections), [42,221], [Integer], silver_tests ); -equalityTest( map((.sec_valid), sections), [2,2], [Integer], silver_tests ); +equalityTest( map((.fst), [(1, "a"), (2, "b")]), [1,2], [Integer], silver_tests ); +equalityTest( map((.fromRight), [right(1), right(2)]), [1,2], [Integer], silver_tests ); -wrongCode "attribute sections currently do not work with parameterized attributes" { - -- Valid, but for the moment does not work! TODO - global s :: [String] = map((.sec_inv), sections); +wrongCode "The attribute section (.fromLeft) has an ambiguous inferred output type a, where a is unspecialized" { + global b::Boolean = null(map((.fromLeft), [right(1), right(2)])); } wrongCode "does not occur on" { - global s :: [String] = map((.sec_inv2), sections); + global s :: [String] = map((.sec_inv1), sections); } -wrongCode "Only synthesized attributes are currently supported" { - -- Valid, but for the moment does not work! TODO - global s :: [Integer] = map((.sec_inv3), sections); +wrongCode "'x' has type a and cannot have attributes." { + global s :: [Integer] = map((.sec1), []); +} +wrongCode "Attribute section (.sec2) has inferred output type Integer that does not match the attribute's type String" { + global s :: [Integer] = map((.sec2), sections); } +synthesized attribute sec5 :: a; +nonterminal SectionPoly with sec5; +production sectionPoly +top::SectionPoly ::= x::a +{ top.sec5 = x; } + +global requireSP :: (SectionPoly ::= SectionPoly) = id; + +-- Here the check that the output type is unambiguous is the only error: +wrongCode "The attribute section (.sec5) has an ambiguous inferred output type silver_features:SectionPoly, where a is unspecialized" { + global sp::String = (.sec5)(requireSP((.sec5)(sectionPoly(sectionPoly(42))))); +} + +annotation sec6::Integer; +nonterminal AnnoSection with sec6; +production annoSection +top::AnnoSection ::= +{} + +global annoSections :: [AnnoSection] = [annoSection(sec6=12), annoSection(sec6=34)]; +equalityTest( map((.sec6), annoSections), [12, 34], [Integer], silver_tests ); + ------------------------------- -- Partial function application diff --git a/test/silver_features/FuncProdTypes.sv b/test/silver_features/FuncProdTypes.sv index 8cdcf2b37..b936132e5 100644 --- a/test/silver_features/FuncProdTypes.sv +++ b/test/silver_features/FuncProdTypes.sv @@ -32,4 +32,43 @@ s::IntNT ::= l::IntNT r::IntNT equalityTest ( foldr (intAdd, intTestProd(0), aList).intValue, 6, Integer, silver_tests ) ; +-- Higher-kinded polymorphic functions and productions +function functorInc +f ::= fmapI::(f ::= (Integer ::= Integer) f) xs::f +{ + return fmapI(\ x::Integer -> x + 1, xs); +} + +equalityTest(functorInc(map, [1, 2, 3]), [2, 3, 4], [Integer], silver_tests); +equalityTest(functorInc(mapMaybe, just(42)).fromJust, 43, Integer, silver_tests); + +nonterminal FInts with intValue; +production fints +top::FInts ::= ffoldI::(Integer ::= (Integer ::= Integer Integer) Integer f) xs::f +{ + top.intValue = ffoldI(\ i1::Integer i2::Integer -> i1 + i2, 0, xs); +} + +equalityTest(fints(foldr, [1, 2, 3]).intValue, 6, Integer, silver_tests); +global foldMaybeInt::(Integer ::= (Integer ::= Integer Integer) Integer Maybe) = + \ fn::(Integer ::= Integer Integer) i0::Integer mi::Maybe -> case mi of just(i1) -> fn(i0, i1) | _ -> i0 end; +equalityTest(fints(foldMaybeInt, just(42)).intValue, 42, Integer, silver_tests); + +-- Kind mismatch +wrongCode "f has kind * -> *, but kind * is expected here" { + function badKind + f ::= f + { return error(""); } +} + +wrongCode "Missing type argument cannot be followed by a provided argument" { + type BadFunc = (_ ::= _ String); +} + +wrongCode "Return type cannot be present when argument types are missing" { + type BadFunc = (Integer ::= String _); +} +global idInt1::(Integer ::= Integer) = id; +global idInt2::(_ ::= Integer) = id; +global idInt3::(_ ::= _) = id; diff --git a/test/silver_features/Globals.sv b/test/silver_features/Globals.sv index 72621e8b1..a7d1bc23f 100644 --- a/test/silver_features/Globals.sv +++ b/test/silver_features/Globals.sv @@ -61,3 +61,31 @@ wrongCode "[String] has initialization expression with type [Integer]" { global globIntList :: [Integer] = [1,2]; global globStrList :: [String] = tail(globIntList); -- thread type state properly to detect Str!=Int } + +-- Testing polymorphism +global myMap::([b] ::= (b ::= a) [a]) = map; +global globLst::[Integer] = myMap((\x::Integer -> x + 3), [1,2,3,4,5]); +equalityTest (head(globLst), 4, Integer, silver_tests ); + +function thirdOfFour +c ::= tuple::(a,b,c,d) +{ + return tuple.3; +} + +global getThird::(c ::= (a,b,c,d)) = thirdOfFour; +equalityTest(getThird((1, 2, "three", 4)), "three", String, silver_tests); + +-- With constraints +global myEq::Eq a => (Boolean ::= a a) = eq; +equalityTest (myEq(1, 2), false, Boolean, silver_tests); + +function strComp +Boolean ::= a::String b::String +{ + return if a == b then true else false; +} + +global elem::Eq a => (Boolean ::= (Boolean ::= a a) a [a]) = containsBy; +equalityTest(elem(strComp, "ABC", ["A", "B", "C"]), false, Boolean, silver_tests); +equalityTest(elem(strComp, "B", ["A", "B", "C"]), true, Boolean, silver_tests); \ No newline at end of file diff --git a/test/silver_features/Main.sv b/test/silver_features/Main.sv index 1fc134874..c195a957a 100644 --- a/test/silver_features/Main.sv +++ b/test/silver_features/Main.sv @@ -1,12 +1,12 @@ grammar silver_features; imports silver:testing; -imports lib:extcore; import silver_features:defs; import silver_features:cond; import silver_features:anno; import silver_features:rewrite; +import silver_features:treegen; mainTestSuite silver_tests; diff --git a/test/silver_features/Monad.sv b/test/silver_features/Monad.sv deleted file mode 100644 index 8f9bc30d6..000000000 --- a/test/silver_features/Monad.sv +++ /dev/null @@ -1,41 +0,0 @@ -import core:monad; - --- Test Maybe -global monadRes1::Maybe = do (bindMaybe, returnMaybe) { - a::Integer <- just(1); - if a > 0 - then return 2; - else nothing(); -}; - -equalityTest(monadRes1.isJust, true, Boolean, silver_tests); - --- Test list -global monadRes2::[Integer] = do (bindList, returnList) { - a::Integer <- [1, 2, 3]; - b::Integer <- [4, 5, 6]; - return a * b; -}; - -equalityTest(foldr(\a::Integer b::Integer -> a + b, 0, monadRes2), 90, Integer, silver_tests); - --- Test State -global monadRes3::State = do (bindState, returnState) { - a::Integer <- getState(); - setState(a + 1); - return a * 2; -}; - -global res3::Pair = runState(monadRes3, 2); - -equalityTest(res3.fst, 3, Integer, silver_tests); -equalityTest(res3.snd, 4, Integer, silver_tests); - --- Test IO -global monadRes4::IOMonad = do (bindIO, returnIO) { - writeFileM("test_out.txt", "Hello"); - appendFileM("test_out.txt", ", World!"); - readFileM("test_out.txt"); -}; - -equalityTest(evalIO(monadRes4, unsafeIO()).iovalue, "Hello, World!", String, silver_tests); \ No newline at end of file diff --git a/test/silver_features/Monoid.sv b/test/silver_features/Monoid.sv index 62b37a64b..21452155c 100644 --- a/test/silver_features/Monoid.sv +++ b/test/silver_features/Monoid.sv @@ -1,5 +1,5 @@ -monoid attribute defs::[String] with [], ++; +monoid attribute defs::[String]; monoid attribute freeVars::[String] with [], ++; propagate defs on MStmt; @@ -11,7 +11,7 @@ nonterminal MExpr with freeVars; abstract production seqMStmt top::MStmt ::= s1::MStmt s2::MStmt { - top.freeVars := s1.freeVars ++ removeAllBy(stringEq, s1.defs, s2.freeVars); + top.freeVars := s1.freeVars ++ removeAll(s1.defs, s2.freeVars); } abstract production assignMStmt @@ -39,6 +39,12 @@ global testMStmt::MStmt = equalityTest(testMStmt.defs, ["a", "d"], [String], silver_tests); equalityTest(testMStmt.freeVars, ["b", "c"], [String], silver_tests); +wrongCode "Could not find an instance for silver:core:Monoid silver_features:NotAMonoid" { + nonterminal NotAMonoid; + monoid attribute nam::NotAMonoid occurs on MStmt, MExpr; + propagate nam on MStmt, MExpr; +} + -- Test errors in propagate monoid attribute things::[a] with [], ++; diff --git a/test/silver_features/OccursContext.sv b/test/silver_features/OccursContext.sv new file mode 100644 index 000000000..8182de5b9 --- /dev/null +++ b/test/silver_features/OccursContext.sv @@ -0,0 +1,200 @@ +grammar silver_features; + +inherited attribute extraInh1::String occurs on EqExpr; +inherited attribute extraInh2::Integer occurs on EqExpr; + +function eqA +attribute compareTo occurs on a, attribute isEqual {compareTo} occurs on a => +Boolean ::= x::a y::a +{ + x.compareTo = y; + return x.isEqual; +} + +equalityTest(eqA(ee1, ee1), true, Boolean, silver_tests); +equalityTest(eqA(ee1, ee2), false, Boolean, silver_tests); +equalityTest(eqA(ee1, ee3), false, Boolean, silver_tests); +equalityTest(eqA(ee2, ee1), false, Boolean, silver_tests); +equalityTest(eqA(ee2, ee2), true, Boolean, silver_tests); +equalityTest(eqA(ee2, ee3), false, Boolean, silver_tests); +equalityTest(eqA(ee3, ee1), false, Boolean, silver_tests); +equalityTest(eqA(ee3, ee2), false, Boolean, silver_tests); +equalityTest(eqA(ee3, ee3), true, Boolean, silver_tests); + +function eqB +attribute compareTo occurs on a, attribute isEqual {compareTo} occurs on a => +Boolean ::= x::a y::a +{ + production z::a = x; + production w::a = z; + w.compareTo = y; + return w.isEqual; +} + +equalityTest(eqB(ee1, ee1), true, Boolean, silver_tests); +equalityTest(eqB(ee1, ee2), false, Boolean, silver_tests); +equalityTest(eqB(ee1, ee3), false, Boolean, silver_tests); +equalityTest(eqB(ee2, ee1), false, Boolean, silver_tests); +equalityTest(eqB(ee2, ee2), true, Boolean, silver_tests); +equalityTest(eqB(ee2, ee3), false, Boolean, silver_tests); +equalityTest(eqB(ee3, ee1), false, Boolean, silver_tests); +equalityTest(eqB(ee3, ee2), false, Boolean, silver_tests); +equalityTest(eqB(ee3, ee3), true, Boolean, silver_tests); + +function eqC +attribute compareTo occurs on a, attribute isEqual {compareTo} occurs on a => +Boolean ::= x::(a ::= ) y::(a ::= ) +{ + production z::a = x(); + production w::a = y(); + w.compareTo = z; + return w.isEqual; +} + +equalityTest(eqC(\ -> ee1, \ -> ee1), true, Boolean, silver_tests); +equalityTest(eqC(\ -> ee1, \ -> ee2), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee1, \ -> ee3), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee2, \ -> ee1), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee2, \ -> ee2), true, Boolean, silver_tests); +equalityTest(eqC(\ -> ee2, \ -> ee3), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee3, \ -> ee1), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee3, \ -> ee2), false, Boolean, silver_tests); +equalityTest(eqC(\ -> ee3, \ -> ee3), true, Boolean, silver_tests); + +class OCEq a { + ocEq :: (Boolean ::= a a); +} + +instance OCEq Integer +{ + ocEq = \ x::Integer y::Integer -> x == y; +} + +instance OCEq String +{ + ocEq = \ x::String y::String -> x == y; +} + +instance attribute compareTo occurs on a, attribute isEqual {compareTo} occurs on a => OCEq a { + ocEq = \ x::a y::a -> decorate x with {compareTo = decorate y with {};}.isEqual; +} + +equalityTest(ocEq(ee1, ee1), true, Boolean, silver_tests); +equalityTest(ocEq(ee1, ee2), false, Boolean, silver_tests); +equalityTest(ocEq(ee1, ee3), false, Boolean, silver_tests); +equalityTest(ocEq(ee2, ee1), false, Boolean, silver_tests); +equalityTest(ocEq(ee2, ee2), true, Boolean, silver_tests); +equalityTest(ocEq(ee2, ee3), false, Boolean, silver_tests); +equalityTest(ocEq(ee3, ee1), false, Boolean, silver_tests); +equalityTest(ocEq(ee3, ee2), false, Boolean, silver_tests); +equalityTest(ocEq(ee3, ee3), true, Boolean, silver_tests); + +nonterminal OCEqPair with compareTo, isEqual; +production ocEqPair +attribute compareTo occurs on a, attribute isEqual {compareTo} occurs on a, +attribute compareTo occurs on b, attribute isEqual {compareTo} occurs on b => +top::OCEqPair ::= x::a y::b +{ + propagate compareTo, isEqual; +{- + x.compareTo = case top.compareTo of ocEqPair(a, _) -> a end; + y.compareTo = case top.compareTo of ocEqPair(_, a) -> a end; + top.isEqual = x.isEqual && y.isEqual; + -} +} + +equalityTest(decorate ocEqPair(ee1, ee2) with {compareTo = decorate ocEqPair(ee1, ee2) with {};}.isEqual, true, Boolean, silver_tests); + +function requireDec +Decorated a with i ::= x::Decorated a with i +{ return x; } + +-- decorated match on polymorphic child +equalityTest( + case decorate ocEqPair(ee1, ee2) with {compareTo = decorate ocEqPair(ee1, ee2) with {};} of + | ocEqPair(x, y) -> requireDec(x).isEqual && requireDec(y).isEqual + end, true, Boolean, silver_tests); + +equalityTest(ocEq(ocEqPair(ee1, ee2), ocEqPair(ee1, ee2)), true, Boolean, silver_tests); +equalityTest(ocEq(ocEqPair(ee1, ee2), ocEqPair(ee1, ee3)), false, Boolean, silver_tests); + +wrongCode "Could not find an instance for attribute silver:core:isEqual {silver:core:compareTo} occurs on String (arising from the use of ocEqPair)" { + global err::OCEqPair = ocEqPair(ee1, "abc"); +} +wrongCode "Could not find an instance for attribute silver:core:compareTo occurs on String (arising from the use of ocEqPair)" { + global err::OCEqPair = ocEqPair(ee1, "abc"); +} + +synthesized attribute isEqual2::Boolean occurs on OCEqPair; +aspect production ocEqPair +top::OCEqPair ::= x::a y::b +{ + top.isEqual2 = x.isEqual && y.isEqual; +} + +equalityTest(decorate ocEqPair(ee1, ee2) with {compareTo = decorate ocEqPair(ee1, ee2) with {};}.isEqual2, true, Boolean, silver_tests); + +synthesized attribute prodName::String; +nonterminal OCExpr with prodName; +production constOCE +top::OCExpr ::= i::Integer +{ top.prodName = "const"; } +production addOCE +top::OCExpr ::= e1::OCExpr e2::OCExpr +{ top.prodName = "add"; } + +inherited attribute prodNameIn::String; +nonterminal OCThing with prodNameIn, prodName; +production ocThing +top::OCThing ::= +{ top.prodName = top.prodNameIn; } + +class ProdName1 a { + prodName1 :: (String ::= a); +} +instance attribute prodName i occurs on a => ProdName1 Decorated a with i { + prodName1 = (.prodName); +} +instance attribute prodName {} occurs on a => ProdName1 a { + prodName1 = (.prodName); +} + +equalityTest(prodName1(constOCE(42)), "const", String, silver_tests); +equalityTest(prodName1(decorate ocThing() with {prodNameIn = "thing";}), "thing", String, silver_tests); + +class attribute prodName {} occurs on a => ProdName2 a { + prodName2 :: (String ::= a) = (.prodName); +} +instance ProdName2 OCExpr {} + +equalityTest(prodName2(constOCE(42)), "const", String, silver_tests); + +function prodName3 +ProdName2 a => String ::= x::a +{ + return x.prodName; +} + +equalityTest(prodName3(constOCE(42)), "const", String, silver_tests); + +wrongCode "error: Attribute 'prodName' does not occur on 'a'" { + function prodName4 + String ::= x::a + { return x.prodName; } +} + +nonterminal OCPolyWrap with prodNameIn, prodName; +production ocPolyWrap +attribute prodNameIn occurs on a, attribute prodName {prodNameIn} occurs on a => +top::OCPolyWrap ::= x::a +{ + x.prodNameIn = top.prodNameIn; + top.prodName = x.prodName; +} + +equalityTest(decorate ocPolyWrap(ocThing()) with {prodName = "blah";}.prodName, "blah", String, silver_tests); +equalityTest(decorate ocPolyWrap(ocPolyWrap(ocThing())) with {prodName = "blah";}.prodName, "blah", String, silver_tests); +equalityTest( + case decorate ocPolyWrap(ocThing()) with {prodName = "blah";} of + | ocPolyWrap(thing) -> thing.prodName + end, "blah", String, silver_tests); diff --git a/test/silver_features/PartialDec.sv b/test/silver_features/PartialDec.sv new file mode 100644 index 000000000..bcc4e9a1b --- /dev/null +++ b/test/silver_features/PartialDec.sv @@ -0,0 +1,87 @@ +grammar silver_features; + +synthesized attribute errors1::Boolean; +synthesized attribute errors2::Boolean; + +nonterminal PDExpr with env1, env2, errors1, errors2; + +production pdVar +top::PDExpr ::= n::String +{ + top.errors1 = !contains(n, top.env1); + top.errors2 = top.errors1 || !contains(n, top.env2); +} + +production pdOp1 +top::PDExpr ::= e::PDExpr +{ + e.env1 = top.env1; + forwards to pdOp1Impl(e); +} + +production pdOp1Impl +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + e.env2 = top.env2; + top.errors1 = e.errors1; + top.errors2 = e.errors2; +} + +production pdOp2 +top::PDExpr ::= e::PDExpr +{ + e.env1 = top.env1; + forwards to pdOp2Impl(e); +} + +production pdOp2Impl +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + local e2::PartiallyDecorated PDExpr with {env1} = e; + e2.env2 = top.env2; + top.errors1 = e2.errors1; + top.errors2 = e2.errors2; +} + +production pdOp3 +top::PDExpr ::= e::PDExpr +{ + --forwards to pdOp3Impl(decorate e with {env1 = top.env1;}); -- TODO + e.env1 = top.env1; + forwards to pdOp3Impl(e); +} + +production pdOp3Impl +top::PDExpr ::= e::PartiallyDecorated PDExpr with {env1} +{ + local e2::Decorated PDExpr = decorate withEnv1(e) with {env2 = top.env2;}; + top.errors1 = e2.errors1; + top.errors2 = e2.errors2; +} + +global withEnv1::(PartiallyDecorated PDExpr with {env1} ::= PartiallyDecorated PDExpr with {env1}) = id; + +production pdOp4 +top::PDExpr ::= e::PDExpr +{ + e.env1 = top.env1; + local e2::PartiallyDecorated PDExpr with {env1} = e; + e2.env2 = top.env2; + forwards to pdOp4Impl(e2); +} + +production pdOp4Impl +top::PDExpr ::= e::PartiallyDecorated PDExpr +{ + local e2::PartiallyDecorated PDExpr = e; + top.errors1 = e2.errors1; + top.errors2 = e2.errors2; +} + +global pdTerm::PDExpr = pdOp1(pdOp2(pdOp3(pdOp4(pdVar("foo"))))); +equalityTest(decorate pdTerm with { env1 = ["foo"]; env2 = ["foo"]; }.errors1, false, Boolean, silver_tests); +equalityTest(decorate pdTerm with { env1 = ["foo"]; env2 = ["foo"]; }.errors2, false, Boolean, silver_tests); +equalityTest(decorate pdTerm with { env1 = ["foo"]; env2 = []; }.errors1, false, Boolean, silver_tests); +equalityTest(decorate pdTerm with { env1 = ["foo"]; env2 = []; }.errors2, true, Boolean, silver_tests); +equalityTest(decorate pdTerm with { env1 = []; env2 = ["foo"]; }.errors1, true, Boolean, silver_tests); +equalityTest(decorate pdTerm with { env1 = []; env2 = ["foo"]; }.errors2, true, Boolean, silver_tests); diff --git a/test/silver_features/Reflection.sv b/test/silver_features/Reflection.sv index 2366f90e4..ed8bfad18 100644 --- a/test/silver_features/Reflection.sv +++ b/test/silver_features/Reflection.sv @@ -173,16 +173,31 @@ equalityTest(reifyResToString(reify(anyAST(baz(anno2=_, anno1=_)))), "", String, silver_tests); equalityTest(reifyResToString(reify(anyAST(baz(anno1=_, anno2=2.0)))), "", String, silver_tests); -wrongCode "skolem" { - function reifySkolem +function reifySkolem +runtimeTypeable a => Either ::= x::AST +{ + return reify(x); +} + +wrongCode "runtimeTypeable" { + function reifySkolemErr Either ::= x::AST { return reify(x); } } -warnCode "skolem" { - function reifySkolem2 +equalityTest(reifySkolem(reflect(pair("abc", 123))), right(pair("abc", 123)), Either>, silver_tests); + +function reifySkolem2 +runtimeTypeable a => Either ::= +{ + local fn::(a ::= Integer) = \ i::Integer -> error(toString(i)); + return reify(anyAST(fn)); +} + +wrongCode "runtimeTypeable" { + function reifySkolemErr2 Either ::= { local fn::(a ::= Integer) = \ i::Integer -> error(toString(i)); @@ -192,6 +207,24 @@ warnCode "skolem" { equalityTest(case reifySkolem2() of left(_) -> false | right(_) -> true end, true, Boolean, silver_tests); +function makeSpecializedId +(a ::= a) ::= a +{ + return \x::a -> x; +} + +equalityTest(applyAST(anyAST(makeSpecializedId(42)), [just(reflect(12))], []).isLeft, true, Boolean, silver_tests); +equalityTest(applyAST(anyAST(makeSpecializedId(42)), [just(reflect(3.14))], []).isLeft, true, Boolean, silver_tests); + +function makeSpecializedId2 +runtimeTypeable a => (a ::= a) ::= a +{ + return \x::a -> x; +} + +equalityTest(case applyAST(anyAST(makeSpecializedId2(42)), [just(reflect(12))], []) of left(m) -> m | right(a) -> reifyResToString(reify(a)) end, "12", String, silver_tests); +equalityTest(case applyAST(anyAST(makeSpecializedId2(42)), [just(reflect(3.14))], []) of left(m) -> m | right(a) -> reifyResToString(reify(a)) end, "Reification error in argument 0 at ?:\nreify is constructing Integer, but found Float AST.", String, silver_tests); + global testValue::Pair<[Expr] Baz> = pair([testExpr, intConstExpr(5, lineNum=4)], baz(anno1=1, anno2=2.0)); global serializeRes::Either = reflect(testValue).serialize; global deserializeRes::Either = deserializeAST(lessHackyUnparse(testValue), case serializeRes of left(msg) -> msg | right(a) -> a end); @@ -231,11 +264,11 @@ global terminalReifyRes::Either>> = reify(case equalityTest( lessHackyUnparse(terminalTestValue), - s"""core:pair([terminal(silver_features:Foo_t, "foo", ??:-1:-1), terminal(silver_features:Foo_t, "foo", ??:-1:-1)], core:just(terminal(silver_features:Bar_t, "bar42", a:1:2)))""", + s"""silver:core:pair([terminal(silver_features:Foo_t, "foo", ??:-1:-1), terminal(silver_features:Foo_t, "foo", ??:-1:-1)], silver:core:just(terminal(silver_features:Bar_t, "bar42", a:1:2)))""", String, silver_tests); equalityTest( case terminalSerializeRes of left(msg) -> msg | right(a) -> a end, - s"""core:pair([terminal(silver_features:Foo_t, "foo", core:loc("??", -1, -1, -1, -1, -1, -1)), terminal(silver_features:Foo_t, "foo", core:loc("??", -1, -1, -1, -1, -1, -1))], core:just(terminal(silver_features:Bar_t, "bar42", core:loc("a", 1, 2, 3, 4, 5, 6))))""", + s"""silver:core:pair([terminal(silver_features:Foo_t, "foo", silver:core:loc("??", -1, -1, -1, -1, -1, -1)), terminal(silver_features:Foo_t, "foo", silver:core:loc("??", -1, -1, -1, -1, -1, -1))], silver:core:just(terminal(silver_features:Bar_t, "bar42", silver:core:loc("a", 1, 2, 3, 4, 5, 6))))""", String, silver_tests); equalityTest(case terminalDeserializeRes of left(msg) -> msg | right(a) -> show(80, a.pp) end, lessHackyUnparse(terminalTestValue), String, silver_tests); equalityTest(reifyResToString(terminalReifyRes), lessHackyUnparse(terminalTestValue), String, silver_tests); @@ -244,11 +277,11 @@ global reifyRes10::Either = reify(terminalAST("silver_features:Foo_t equalityTest(reifyResToString(reifyRes10), "Reification error at ?:\nreify is constructing silver_features:Baz, but found terminal silver_features:Foo_t AST.", String, silver_tests); global reifyRes11::Either> = reify(reflect(pair("asdf", 'foo'))); -equalityTest(reifyResToString(reifyRes11), "Reification error at core:pair(_, ?):\nreify is constructing core:Location, but found terminal silver_features:Foo_t AST.", String, silver_tests); +equalityTest(reifyResToString(reifyRes11), "Reification error at silver:core:pair(_, ?):\nreify is constructing silver:core:Location, but found terminal silver_features:Foo_t AST.", String, silver_tests); equalityTest( - case deserializeAST("test", "terminal(asdf, \"a\", core:loc(\"a\", 2, 3.14, 4, 5, 6, 7))") of left(msg) -> msg | right(a) -> show(80, a.pp) end, - "test:1:20: error: Reification error at core:loc(_, _, ?, _, _, _, _):\nreify is constructing Integer, but found Float AST.", + case deserializeAST("test", "terminal(asdf, \"a\", silver:core:loc(\"a\", 2, 3.14, 4, 5, 6, 7))") of left(msg) -> msg | right(a) -> show(80, a.pp) end, + "test:1:20: error: Reification error at silver:core:loc(_, _, ?, _, _, _, _):\nreify is constructing Integer, but found Float AST.", String, silver_tests); global serializeRes1::Either = serialize(pair("hello", [1, 2, 3, 4])); @@ -256,20 +289,27 @@ global reifyRes12::Either> = deserialize("test", f equalityTest( case serializeRes1 of left(msg) -> msg | right(a) -> a end, - s"""core:pair("hello", [1, 2, 3, 4])""", + s"""silver:core:pair("hello", [1, 2, 3, 4])""", String, silver_tests); equalityTest( reifyResToString(reifyRes12), - s"""core:pair("hello", [1, 2, 3, 4])""", + s"""silver:core:pair("hello", [1, 2, 3, 4])""", String, silver_tests); +type ForeignString foreign = "String"; +wrongCode "Could not find an instance for runtimeTypeable silver_features:ForeignString (arising from the use of reifyUnchecked)" { + function reifyForeignString + ForeignString ::= x::AST + { return reifyUnchecked(x); } +} + global add::(Integer ::= Integer Integer) = \ i::Integer j::Integer -> i + j; global applyRes1::Either = applyAST(reflect(add), [just(reflect(1)), just(reflect(2))], []); -equalityTest(lessHackyUnparse(applyRes1), "core:right(core:reflect:integerAST(3))", String, silver_tests); +equalityTest(lessHackyUnparse(applyRes1), "silver:core:right(silver:core:integerAST(3))", String, silver_tests); global applyRes2::Either = applyAST(applyAST(reflect(add), [nothing(), just(reflect(2))], []).fromRight, [just(reflect(1))], []); -equalityTest(lessHackyUnparse(applyRes2), "core:right(core:reflect:integerAST(3))", String, silver_tests); +equalityTest(lessHackyUnparse(applyRes2), "silver:core:right(silver:core:integerAST(3))", String, silver_tests); global applyRes3::Either = applyAST(reflect(add), [just(reflect(1)), nothing(), just(reflect(2))], []); equalityTest(applyRes3.isLeft, true, Boolean, silver_tests); @@ -301,3 +341,26 @@ equalityTest( global applyRes8::Either = applyAST(reflect(baz), [], [pair("anno1", just(reflect(3.14))), pair("anno2", just(reflect(42)))]); equalityTest(applyRes8.isLeft, true, Boolean, silver_tests); +-- Reflection of productions with type constraints +equalityTest(decorate let res :: OCEqPair = reifyUnchecked(reflect(ocEqPair(ee1, ee2))) in res end with {compareTo = decorate ocEqPair(ee1, ee2) with {};}.isEqual, true, Boolean, silver_tests); + +nonterminal WrapTypeable; +production wrapTypeable +runtimeTypeable a => top::WrapTypeable ::= x::a +{} + +equalityTest( + let res :: WrapTypeable = reifyUnchecked(reflect(wrapTypeable(pair("hi", 42)))) in case res of wrapTypeable(x) -> reifyUnchecked(reflect(x)) end end, + pair("hi", 42), + Pair, silver_tests); + +equalityTest( + let res :: Either = reify(reflect(rtProd(\ xs::[Integer] -> length(xs)))) in res.fromLeft end, + "Reification error at ?:\nCan't construct production silver_features:rtProd because context runtimeTypeable a cannot be resolved at runtime", + String, silver_tests); + +equalityTest( + let res :: Either> = reify(reflect(eqPair(42, [1, 2, 3]))) in res.fromLeft end, + "Reification error at ?:\nCan't construct production silver_features:eqPair because context silver_features:myeq:MyEq a cannot be resolved at runtime", + String, silver_tests); + diff --git a/test/silver_features/Regex.sv b/test/silver_features/Regex.sv new file mode 100644 index 000000000..6553b9f1a --- /dev/null +++ b/test/silver_features/Regex.sv @@ -0,0 +1,23 @@ +grammar silver_features; + +import silver:regex; + +equalityTest("abc" =~ /abc/, true, Boolean, silver_tests); +equalityTest("cba" =~ /abc/, false, Boolean, silver_tests); +equalityTest("acxyxyxyxyccxyc" =~ /a(xy|c)+/, true, Boolean, silver_tests); +equalityTest("acxyxyxyxxyccxyc" =~ /a(xy|c)+/, false, Boolean, silver_tests); +equalityTest("acxyxyxyxyccxyca" =~ /a(xy|c)+a/, true, Boolean, silver_tests); +equalityTest("acxyxyxyxxyccxyca" =~ /a(xy|c)+a/, false, Boolean, silver_tests); +equalityTest("" =~ //, true, Boolean, silver_tests); +equalityTest("ac" =~ //, false, Boolean, silver_tests); +equalityTest("asd_f323" =~ /[a-zA-Z_][a-zA-Z_0-9]*/, true, Boolean, silver_tests); +equalityTest("asd_f3@23" =~ /[a-zA-Z_][a-zA-Z_0-9]*/, false, Boolean, silver_tests); +equalityTest("{-{-a-}{--bc--}d-}" =~ /\{\-(\{\-([^\-]|\-+[^\}\-])*\-+\}|[^\-]|\-+[^\}\-])*\-+\}/, true, Boolean, silver_tests); +equalityTest("{-{-a{--}-}{--bc--}d-}" =~ /\{\-(\{\-([^\-]|\-+[^\}\-])*\-+\}|[^\-]|\-+[^\}\-])*\-+\}/, false, Boolean, silver_tests); + +equalityTest("// abc" =~ /\/\/.*/, true, Boolean, silver_tests); +equalityTest("// a\nbc" =~ /\/\/.*/, false, Boolean, silver_tests); + +equalityTest(" \r\n\t" =~ /[\r\n\t ]+/, true, Boolean, silver_tests); +equalityTest(" blah\n" =~ /[\r\n\t ]+/, false, Boolean, silver_tests); +equalityTest("" =~ /[\r\n\t ]+/, false, Boolean, silver_tests); diff --git a/test/silver_features/Strategy.sv b/test/silver_features/Strategy.sv index 8d41ea92b..ce0214b40 100644 --- a/test/silver_features/Strategy.sv +++ b/test/silver_features/Strategy.sv @@ -1,7 +1,5 @@ grammar silver_features; -import core:monad; - strategy attribute elimPlusZero = bottomUp(try(rule on SExpr of addSExpr(e, constSExpr(0)) -> e end)); @@ -35,23 +33,25 @@ top::SStmt ::= n::String e::SExpr propagate elimPlusZero; } +attribute compareTo, isEqual occurs on SExpr, SStmt; +propagate compareTo, isEqual on SExpr, SStmt; + equalityTest( - hackUnparse(addSExpr(constSExpr(42), constSExpr(0)).elimPlusZero), - "silver_features:constSExpr(42)", - String, silver_tests); + addSExpr(constSExpr(42), constSExpr(0)).elimPlusZero, + constSExpr(42), + SExpr, silver_tests); equalityTest( - hackUnparse(addSExpr(addSExpr(constSExpr(42), constSExpr(0)), constSExpr(0)).elimPlusZero), - "silver_features:constSExpr(42)", - String, silver_tests); + addSExpr(addSExpr(constSExpr(42), constSExpr(0)), constSExpr(0)).elimPlusZero, + constSExpr(42), + SExpr, silver_tests); equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), - assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(0)), constSExpr(0)))).elimPlusZero), - "silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:constSExpr(42)), silver_features:assignSStmt(\"b\", silver_features:idSExpr(\"a\")))", - String, silver_tests); + seqSStmt( + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), + assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(0)), constSExpr(0)))).elimPlusZero, + seqSStmt(assignSStmt("a", constSExpr(42)), assignSStmt("b", idSExpr("a"))), + SStmt, silver_tests); partial strategy attribute removeLastStmt = rule on SStmt of @@ -62,24 +62,21 @@ partial strategy attribute removeLastStmt = propagate removeLastStmt on SStmt, SExpr; equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), - assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(0)), constSExpr(0)))).removeLastStmt), - "core:just(silver_features:assignSStmt(\"a\", silver_features:addSExpr(silver_features:constSExpr(42), silver_features:constSExpr(0))))", - String, silver_tests); + seqSStmt( + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), + assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(0)), constSExpr(0)))).removeLastStmt, + just(assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0)))), + Maybe, silver_tests); equalityTest( - hackUnparse( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))).removeLastStmt), - "core:nothing()", - String, silver_tests); + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))).removeLastStmt, + nothing(), + Maybe, silver_tests); equalityTest( - hackUnparse( - addSExpr(constSExpr(42), constSExpr(0)).removeLastStmt), - "core:nothing()", - String, silver_tests); + addSExpr(constSExpr(42), constSExpr(0)).removeLastStmt, + nothing(), + Maybe, silver_tests); functor attribute incConstsF occurs on SStmt, SExpr; @@ -101,10 +98,9 @@ strategy attribute incTwice = incConstsF <* incConsts propagate incTwice on SStmt, SExpr; equalityTest( - hackUnparse( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))).incTwice), - "silver_features:assignSStmt(\"a\", silver_features:addSExpr(silver_features:constSExpr(44), silver_features:constSExpr(2)))", - String, silver_tests); + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))).incTwice, + assignSStmt("a", addSExpr(constSExpr(44), constSExpr(2))), + SStmt, silver_tests); autocopy attribute target::String occurs on SStmt, SExpr; @@ -117,24 +113,22 @@ strategy attribute incTargetConsts = propagate incTargetConsts on SStmt, SExpr; equalityTest( - hackUnparse( - decorate - seqSStmt( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), - assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(2)), constSExpr(17)))) - with {target = "b";}.incTargetConsts), - "silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:addSExpr(silver_features:constSExpr(42), silver_features:constSExpr(0))), silver_features:assignSStmt(\"b\", silver_features:addSExpr(silver_features:addSExpr(silver_features:idSExpr(\"a\"), silver_features:constSExpr(3)), silver_features:constSExpr(18))))", - String, silver_tests); + decorate + seqSStmt( + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), + assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(2)), constSExpr(17)))) + with {target = "b";}.incTargetConsts, + seqSStmt(assignSStmt("a", addSExpr(constSExpr(42), constSExpr(0))), assignSStmt("b", addSExpr(addSExpr(idSExpr("a"), constSExpr(3)), constSExpr(18)))), + SStmt, silver_tests); strategy attribute incThenElim = incConsts <* elimPlusZero occurs on SStmt, SExpr; propagate incThenElim on SStmt, SExpr; equalityTest( - hackUnparse( - assignSStmt("a", addSExpr(constSExpr(42), constSExpr(-1))).incThenElim), - "silver_features:assignSStmt(\"a\", silver_features:constSExpr(43))", - String, silver_tests); + assignSStmt("a", addSExpr(constSExpr(42), constSExpr(-1))).incThenElim, + assignSStmt("a", constSExpr(43)), + SStmt, silver_tests); strategy attribute incAll = all(incConsts) occurs on SStmt, SExpr; @@ -144,40 +138,49 @@ partial strategy attribute incFstElimSnd = seqSStmt(incConsts, elimPlusZero) occ propagate incAll, incSome, incOne, incFstElimSnd on SStmt, SExpr; equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", constSExpr(1)), - assignSStmt("b", constSExpr(2))).incAll), - "silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:constSExpr(2)), silver_features:assignSStmt(\"b\", silver_features:constSExpr(3)))", - String, silver_tests); + seqSStmt( + assignSStmt("a", constSExpr(1)), + assignSStmt("b", constSExpr(2))).incAll, + seqSStmt(assignSStmt("a", constSExpr(2)), assignSStmt("b", constSExpr(3))), + SStmt, silver_tests); equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", constSExpr(1)), - assignSStmt("b", constSExpr(2))).incSome), - "core:just(silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:constSExpr(2)), silver_features:assignSStmt(\"b\", silver_features:constSExpr(3))))", - String, silver_tests); + seqSStmt( + assignSStmt("a", constSExpr(1)), + assignSStmt("b", constSExpr(2))).incSome, + just(seqSStmt(assignSStmt("a", constSExpr(2)), assignSStmt("b", constSExpr(3)))), + Maybe, silver_tests); equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", constSExpr(1)), - assignSStmt("b", constSExpr(2))).incOne), - "core:just(silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:constSExpr(2)), silver_features:assignSStmt(\"b\", silver_features:constSExpr(2))))", - String, silver_tests); + seqSStmt( + assignSStmt("a", constSExpr(1)), + assignSStmt("b", constSExpr(2))).incOne, + just(seqSStmt(assignSStmt("a", constSExpr(2)), assignSStmt("b", constSExpr(2)))), + Maybe, silver_tests); equalityTest( - hackUnparse( - seqSStmt( - assignSStmt("a", addSExpr(constSExpr(1), constSExpr(0))), - assignSStmt("b", addSExpr(constSExpr(2), constSExpr(0)))).incFstElimSnd), - "core:just(silver_features:seqSStmt(silver_features:assignSStmt(\"a\", silver_features:addSExpr(silver_features:constSExpr(2), silver_features:constSExpr(1))), silver_features:assignSStmt(\"b\", silver_features:constSExpr(2))))", - String, silver_tests); + seqSStmt( + assignSStmt("a", addSExpr(constSExpr(1), constSExpr(0))), + assignSStmt("b", addSExpr(constSExpr(2), constSExpr(0)))).incFstElimSnd, + just(seqSStmt(assignSStmt("a", addSExpr(constSExpr(2), constSExpr(1))), assignSStmt("b", constSExpr(2)))), + Maybe, silver_tests); + +partial strategy attribute onlySStmt = assignSStmt(id, onlySExpr) + occurs on SStmt; +propagate onlySStmt on SStmt; +partial strategy attribute onlySExpr = rule on SExpr of constSExpr(i) -> constSExpr(i + 1) end + occurs on SExpr; +propagate onlySExpr on SExpr; +equalityTest(assignSStmt("a", constSExpr(42)).onlySStmt, just(assignSStmt("a", constSExpr(43))), Maybe, silver_tests); -- Negative tests inherited attribute badInh::a; -wrongCode "cannot be used as total strategy" { +wrongCode "cannot be used as a total strategy" { strategy attribute badInhS = badInh; } +synthesized attribute badSyn::Boolean; +wrongCode "cannot be used as a total strategy" { + strategy attribute badSynS = badSyn; +} + warnCode "is not total" { strategy attribute notTotal = rule on SExpr of constSExpr(i) -> constSExpr(i + 1) end; } diff --git a/test/silver_features/Templating.sv b/test/silver_features/Templating.sv index 8cce98f04..70fef926d 100644 --- a/test/silver_features/Templating.sv +++ b/test/silver_features/Templating.sv @@ -1,8 +1,6 @@ import silver:testing; -import lib:extcore; import silver:langutil:pp; -import silver:util:deque; -- empty equalityTest ( s"""""", "", String, silver_tests ) ; diff --git a/test/silver_features/Threaded.sv b/test/silver_features/Threaded.sv new file mode 100644 index 000000000..3971ed314 --- /dev/null +++ b/test/silver_features/Threaded.sv @@ -0,0 +1,29 @@ +grammar silver_features; + +threaded attribute tinh1, tsyn1 :: [Integer] direction = left to right; +threaded attribute tinh2, tsyn2 :: [Integer] direction = right to left; + +nonterminal TNT with tinh1, tinh2, tsyn1, tsyn2; + +production tFork +top::TNT ::= t1::TNT t2::TNT +{ propagate tinh1, tinh2, tsyn1, tsyn2; } + +production tLeaf +top::TNT ::= i::Integer +{ + top.tsyn1 = top.tinh1 ++ [i]; + top.tsyn2 = top.tinh2 ++ [i]; +} + +production tFwrd +top::TNT ::= f::TNT +{ + propagate tinh1, tinh2, tsyn1, tsyn2; + forwards to tLeaf(4); +} + +global tnt::TNT = tFork(tFork(tLeaf(1), tLeaf(2)), tFwrd(tLeaf(3))); + +equalityTest(decorate tnt with {tinh1 = [0];}.tsyn1, [0, 1, 2, 3, 4], [Integer], silver_tests); +equalityTest(decorate tnt with {tinh2 = [0];}.tsyn2, [0, 3, 4, 2, 1], [Integer], silver_tests); diff --git a/test/silver_features/Tuple.sv b/test/silver_features/Tuple.sv new file mode 100644 index 000000000..3c424241a --- /dev/null +++ b/test/silver_features/Tuple.sv @@ -0,0 +1,276 @@ +import silver:testing; + +-- Testing that a tuple of two elements behaves like a pair +equalityTest((1,2).fst, 1, Integer, silver_tests); +equalityTest((3, "b").snd, "b", String, silver_tests); + +-- Testing that a tuple of > two elements behaves as nested pairs +equalityTest((1,2,3).snd.fst, 2, Integer, silver_tests); +equalityTest((1,"a",2,"b").snd.snd.snd, "b", String, silver_tests); + +-- Pattern matching tests + +function tupleMatch1 +Boolean ::= tuple::(String, Integer, Integer) +{ + return case tuple of + | (_, _, 2) -> true + | _ -> false + end; +} + +equalityTest(tupleMatch1(("a",1,2)), true, Boolean, silver_tests); +equalityTest(tupleMatch1(("b", 3, 4)), false, Boolean, silver_tests); + +function tupleMatch3rd +Integer ::= tuple::(String, Integer, Integer) +{ + return case tuple of + | (_, _, x) -> x + end; +} + +equalityTest(tupleMatch3rd(("a",1,2)), 2, Integer, silver_tests); +equalityTest(tupleMatch3rd(("a",1,49)), 49, Integer, silver_tests); + +function tupleMatch2nd +String ::= tuple::(String, String, Integer) +{ + return case tuple of + | (_, x, _) -> x + end; +} + +equalityTest(tupleMatch2nd(("a","hello",2)), "hello", String, silver_tests); +equalityTest(tupleMatch2nd(("a","I like dogs",49)), "I like dogs", String, silver_tests); + +function tupleMatch4 +Integer ::= tuple::(Integer, String, String, Integer) +{ + return case tuple of + | (fst, _, _, 4) -> fst + | (8, _, _, frth) -> frth + | _ -> error("match failed") + end; +} + +equalityTest(tupleMatch4((1, "test", "more test", 4)), 1, Integer, silver_tests); +equalityTest(tupleMatch4((8, "test", "more test", 4)), 8, Integer, silver_tests); +equalityTest(tupleMatch4((8, "test", "more test", 3)), 3, Integer, silver_tests); + +function dogListMatch +String ::= tuple::(String, [String]) +{ + return case tuple of + | ("best dog", ["coriander", "mint", "basil"]) -> "basil" + | ("biggest dog", ["charlie", _, _]) -> "charlie" + | ("first dog", fst::_) -> fst + | ("reddest dog", "clifford"::_) -> "clifford" + | _ -> "unknown dog" + end; +} + +equalityTest(dogListMatch(("best dog", ["coriander", "mint", "basil"])), "basil", String, silver_tests); +equalityTest(dogListMatch(("biggest dog", ["charlie", "corman", "calvin"])), "charlie", String, silver_tests); +equalityTest(dogListMatch(("first dog", ["basil", "charlie", "corman", "calvin", "pepper"])), "basil", String, silver_tests); +equalityTest(dogListMatch(("reddest dog", ["clifford", "lucy"])), "clifford", String, silver_tests); +equalityTest(dogListMatch(("coolest dog", ["basil", "charlie"])), "unknown dog", String, silver_tests); + +function studentGPAMatch +Float ::= tuple::(String, Boolean, (Integer, Float)) +{ + return case tuple of + | ("Student1", _, (2020, gpa)) -> gpa + | ("Student2", _, (2020, gpa)) -> gpa + | _ -> 0.00 + end; +} + +equalityTest(studentGPAMatch(("Student1", true, (2020, 3.45))), 3.45, Float, silver_tests); +equalityTest(studentGPAMatch(("Student2", false, (2020, 2.96))), 2.96, Float, silver_tests); + +-- Empty tuple +equalityTest(hackUnparse(()), "silver:core:unit()", String, silver_tests); + +function emptyTupleTest +Boolean ::= tuple::() +{ + return case tuple of + | () -> true + | _ -> false + end; +} + +equalityTest(emptyTupleTest(()), true, Boolean, silver_tests); + +function tupleMatchWhen +Boolean ::= tuple::(Integer, Integer, Integer) +{ + return case tuple of + | (_, _, x) when x > 3 -> true + | (x, _, _) when x < 5 -> false + | (_, x, _) when x == 4 -> true + | _ -> false + end; +} + +equalityTest(tupleMatchWhen((3, 4, 5)), true, Boolean, silver_tests); +equalityTest(tupleMatchWhen((3, 4, 2)), false, Boolean, silver_tests); +equalityTest(tupleMatchWhen((6, 4, 2)), true, Boolean, silver_tests); +equalityTest(tupleMatchWhen((6, 7, 2)), false, Boolean, silver_tests); + +-- Tuple creation +function makeDate +(Integer, Integer, Integer) ::= day::Integer month::Integer year::Integer +{ + return (day, month, year); +} + +equalityTest(hackUnparse(makeDate(1, 12, 2021)), "silver:core:pair(1, silver:core:pair(12, 2021))", String, silver_tests); + +function temp +String ::= +{ + local attribute goodDog:: (String, Integer); + goodDog = ("Basil", 9); + return goodDog.1; +} + +equalityTest(temp(), "Basil", String, silver_tests); + +-- Errors + +wrongCode "Argument 1 of function 'testingTupleType' expected (String, Integer, Integer) but argument is of type (Integer, String, String)" { + + function testingTupleType + Boolean ::= tuple::(String, Integer, Integer) + { + return true; + } + + equalityTest(testingTupleType((1, "bad", "type")), true, Boolean, silver_tests); +} + +wrongCode "Expected return type is String, but the expression has actual type (Integer, String, Integer)" { + + function testTupleAccess1 + String ::= tuple::(Integer, Integer, String, Integer) + { + return tuple.snd; + } + + equalityTest(testTupleAccess1((1,2,"three",4)), "hi", String, silver_tests); + +} + +wrongCode "Expected return type is String, but the expression has actual type Integer" { + + function testTupleAccess2 + String ::= tuple::(Integer, Integer, String, Integer) + { + return case tuple of + | (_,x,_,_) -> x + end; + } + + equalityTest(testTupleAccess2((1,2,"three",4)), "hi", String, silver_tests); + +} + +wrongCode "Argument 1 of function 'testEmptyTupleType' expected () but argument is of type (Integer, Integer)" { + + function testEmptyTupleType + Boolean ::= tuple::() + { + return true; + } + + equalityTest(testEmptyTupleType((2, 3)), true, Boolean, silver_tests); + +} + +-- Note that given our inductive implementation of tuples, +-- a type (String, (Integer, String)) is equivalent to (String, Integer, String) +wrongCode "Argument 1 of function 'testingTupleType2' expected (String, Integer, String) but argument is of type (String, Integer)" { + + function testingTupleType2 + Boolean ::= tuple::(String, (Integer, String)) + { + return true; + } + + equalityTest(testingTupleType2(("dog", 4)), true, Boolean, silver_tests); + +} + +wrongCode "Argument 1 of function 'testingTupleType3' expected ((String, Integer), String) but argument is of type (String, Integer)" { + + function testingTupleType3 + Boolean ::= tuple::((String, Integer), String) + { + return true; + } + + equalityTest(testingTupleType3(("dog", 4)), true, Boolean, silver_tests); + +} + +-- Testing tuple access selector + +equalityTest((3, 2, "dog", "cat").1, 3, Integer, silver_tests); +equalityTest(("hello", "I", "am", "test").3, "am", String, silver_tests); +equalityTest((4, 3, "testing last", true).4, true, Boolean, silver_tests); +equalityTest(("nested tuple", "should", (2,3,4,"work"), "too").3.2, 3, Integer, silver_tests); + +wrongCode "Invalid tuple selector index." { + + equalityTest((3, 2, 5, true).5, true, Boolean, silver_tests); + +} + +wrongCode "Invalid tuple selector index." { + + equalityTest((3, 2, 5, true).0, true, Boolean, silver_tests); + +} + +equalityTest((3, 2).2, 2, Integer, silver_tests); +equalityTest(("one", 2).1, "one", String, silver_tests); + +-- tests that tuple selectors do not hide child errors + +wrongCode "Undeclared value 'notdefined'" { + +function fun +String ::= +{ + return notdefined.1; +} + +} + +-- polymorphism +function thirdOfFive +c ::= tuple::(a,b,c,d,e) +{ + return tuple.3; +} +equalityTest(thirdOfFive((1,2,"three", 4, "five")), "three", String, silver_tests); +equalityTest(thirdOfFive((1,2,3,"four", "five")), 3, Integer, silver_tests); + +global lst::[(Integer, Integer)] = [(0, 1), (1, 2), (2, 3)]; +global x::Integer = head(lst).1; + +equalityTest(x, 0, Integer, silver_tests); + +function id +a ::= a::a +{ + return a; +} + +global y::Integer = id((1, 3)).1; +global z::String = id(("hello", "this", "is", "test")).4; + +equalityTest(y, 1, Integer, silver_tests); +equalityTest(z, "test", String, silver_tests); \ No newline at end of file diff --git a/test/silver_features/TypeClasses.sv b/test/silver_features/TypeClasses.sv new file mode 100644 index 000000000..d6c6c8202 --- /dev/null +++ b/test/silver_features/TypeClasses.sv @@ -0,0 +1,455 @@ +grammar silver_features; + +import silver_features:myeq; +import silver:reflect; + +class CFoo a +{ + cx :: a; +} + +class CFoo a => CBar a +{} + +class CBaz a +{ + cy :: a; + bazFromInt :: (a ::= Integer) = \ Integer -> cy; + bazEq :: MyEq a => (Boolean ::= a) = \ x::a -> myeq(x, cy); +} + +instance CBaz a => CFoo [a] +{ + cx = [cy]; +} + +instance CBaz Integer +{ + cy = 42; + bazFromInt = \ i::Integer -> i; + bazEq = myeq(_, 42); +} + +instance CBaz String +{ + cy = "hello"; +} + +instance CBaz a => CBar [a] +{} + +global cxi::[Integer] = cx; +equalityTest(cxi, [42], [Integer], silver_tests); + +global cxs::[String] = cx; +equalityTest(cxs, ["hello"], [String], silver_tests); + +global bfii::Integer = bazFromInt(24); +global bfis::String = bazFromInt(24); +equalityTest(bfii, 24, Integer, silver_tests); +equalityTest(bfis, "hello", String, silver_tests); + +equalityTest(bazEq(42), true, Boolean, silver_tests); +equalityTest(bazEq(1234), false, Boolean, silver_tests); +equalityTest(bazEq("hello"), true, Boolean, silver_tests); +equalityTest(bazEq("abc"), false, Boolean, silver_tests); + +equalityTest(myeq([1, 2, 3], [1, 2, 3]), true, Boolean, silver_tests); +equalityTest(myeq([1, 2, 3], [1, 2, 3, 2, 1]), false, Boolean, silver_tests); +equalityTest(myeq([1, 2, 3], [1, 2, 1]), false, Boolean, silver_tests); +equalityTest(myeq(pair([1, 2], 3), pair([1, 2], 3)), true, Boolean, silver_tests); +equalityTest(myeq(pair([1, 2], 3), pair([1, 4], 3)), false, Boolean, silver_tests); + +equalityTest(myneq([1, 2, 3], [1, 2, 3]), false, Boolean, silver_tests); +equalityTest(myneq([1, 2, 3], [1, 2, 3, 2, 1]), true, Boolean, silver_tests); +equalityTest(myneq([1, 2, 3], [1, 2, 1]), true, Boolean, silver_tests); +equalityTest(myneq(pair([1, 2], 3), pair([1, 2], 3)), false, Boolean, silver_tests); +equalityTest(myneq(pair([1, 2], 3), pair([1, 4], 3)), true, Boolean, silver_tests); + +function myRemove +MyEq a => [a] ::= x::a xs::[a] +{ + return removeBy(myeq, x, xs); +} +equalityTest(myRemove(3, [1, 2, 3, 4]), [1, 2, 4], [Integer], silver_tests); + +inherited attribute isEqTo::a; +synthesized attribute isEq::Boolean; +nonterminal EqPair with fst, snd, isEqTo>, isEq; +production eqPair +MyEq a, MyEq b => top::EqPair ::= x::a y::b +{ + top.fst = x; + top.snd = y; + top.isEq = case top.isEqTo of eqPair(x1, y1) -> myeq(x1, x) && myeq(y1, y) end; +} + +equalityTest(decorate eqPair(42, [1, 2, 3]) with {isEqTo=eqPair(42, [1, 2, 3]);}.isEq, true, Boolean, silver_tests); +equalityTest(decorate eqPair(42, [1, 2, 3]) with {isEqTo=eqPair(42, [1, 23, 3]);}.isEq, false, Boolean, silver_tests); +equalityTest(case eqPair(42, [1, 2, 3]), eqPair(42, [1, 2, 3]) of eqPair(w, x), eqPair(y, z) -> myeq(w, y) && myeq(x, z) end, true, Boolean, silver_tests); + +synthesized attribute isEq2::Boolean occurs on EqPair; +aspect production eqPair +top::EqPair ::= x::a y::b +{ + top.isEq2 = myeq(top.isEqTo.fst, x) && myeq(top.isEqTo.snd, y); +} + +equalityTest(decorate eqPair(42, [1, 2, 3]) with {isEqTo=eqPair(42, [1, 2, 3]);}.isEq2, true, Boolean, silver_tests); +equalityTest(decorate eqPair(42, [1, 2, 3]) with {isEqTo=eqPair(42, [1, 23, 3]);}.isEq2, false, Boolean, silver_tests); + +nonterminal EqRank1; +production eqR1 +Eq a => top::EqRank1 ::= x::a +{} + +global eqR1Res::Boolean = case eqR1([1, 2, 3]) of eqR1(a) -> a == a end; +equalityTest(eqR1Res, true, Boolean, silver_tests); + +wrongCode "Could not find an instance for silver_features:CBaz Float (arising from the instance for silver_features:CFoo [Float], arising from the use of cx)" { + global cxf::[Float] = cx; +} + +wrongCode "Member cy has expected type Float, but the expression has actual type Boolean" { + instance CBaz Float + { + cy = false; + } +} + +wrongCode "must be unqualified" { + class gram:Blah a {} +} + +{- This is a parse error, currently +wrongCode "must be capitalized" { + class blah a {} +} +-} + +wrongCode "Cycle exists" { + class C1 a => C2 a {} + class C2 a => C1 a {} +} + +wrongCode "is already bound" { + class M a { f :: ([a] ::= a); f :: (a ::= a a); } +} + +wrongCode "is not a type class" { + instance Maybe Integer {} +} + +wrongCode "Undeclared type" { + instance Blarch Integer {} +} + +wrongCode "Undeclared type" { + instance Blarch a => CBaz a {} +} + +wrongCode "Overlapping instances" { + instance CBaz Integer { cy = 24; } +} + +wrongCode "Overlapping instances" { + instance CFoo [a] { cx = []; } +} + +warnCode "Orphaned instance" { + instance MyEq a => MyEq Maybe + { + myeq = \ m1::Maybe m2::Maybe -> + case m1, m2 of + | just(x), just(y) -> myeq(x, y) + | nothing(), nothing() -> true + | _, _ -> false + end; + } +} + +warnCode "Orphaned default instance" { + instance MyEq a + { + myeq = \ a a -> false; + } +} +equalityTest(myeq(3.14, 3.14), false, Boolean, silver_tests); + +nonterminal ABCD; +production abcd top::ABCD ::= {} + +class CQux a { + cqx :: CQux a => a = cqy; + cqy :: a; +} + +instance CQux Integer { + cqy = 1234; +} + +equalityTest(cqx, 1234, Integer, silver_tests); + +-- Type class from another grammar, but not orphaned +instance MyEq ABCD +{ + myeq = \ ABCD ABCD -> true; +} +equalityTest(myeq(abcd(), abcd()), true, Boolean, silver_tests); + +wrongCode "Missing instance member silver_features:cx" { + instance CFoo ABCD {} +} + +wrongCode "Unexpected instance member cy" { + instance CFoo ABCD + { + cy = 42; + } +} + +wrongCode "is no smaller than the instance head" { + instance CFoo a => CBaz a + { + cy = cx; + } +} + +class MyFunctor f { + myfmap :: (f ::= (b ::= a) f); +} + +instance MyFunctor Maybe { + myfmap = mapMaybe; +} +function mapMaybe +Maybe ::= f::(b ::= a) m::Maybe +{ + return case m of + | just(x) -> just(f(x)) + | nothing() -> nothing() + end; +} + +instance MyFunctor Either { + myfmap = \ fn::(c ::= b) x::Either -> case x of left(l) -> left(l) | right(r) -> right(fn(r)) end; +} + +instance MyFunctor [] { + myfmap = map; +} + +equalityTest(myfmap(\ x::Integer -> toFloat(x), [1, 2, 3]), [1.0, 2.0, 3.0], [Float], silver_tests); +equalityTest(myfmap(\ x::Integer -> toFloat(x), just(42)).fromJust, 42.0, Float, silver_tests); +equalityTest(myfmap(\ x::Integer -> toFloat(x), left("abc")).fromLeft, "abc", String, silver_tests); +equalityTest(myfmap(\ x::Integer -> toFloat(x), let e::Either = right(42) in e end).fromRight, 42.0, Float, silver_tests); + +wrongCode "Ambiguous type variable a (arising from the use of myfmap) prevents the constraint silver_features:MyFunctor silver:core:Either from being solved" { + global ambRes::String = hackUnparse(myfmap(\ x::Integer -> toFloat(x), right(42)).fromRight); +} + +wrongCode "Either has kind * -> * -> *, but the class MyFunctor expected kind * -> *" { + instance MyFunctor Either {} +} + +wrongCode "f has kind * -> *, but there are 2 type arguments supplied here" { + class MyFunctorBad f { + myfmap2 :: (f ::= (b ::= a) f); + } +} + +class CDefaultVal a { + cdv1 :: Pair; + cdv2 :: a = cdv1.snd; +} + +instance CDefaultVal String { + cdv1 = pair(42, "abc"); +} + +equalityTest(cdv2, "abc", String, silver_tests); + +class AmbInst a { + ambval :: a; +} +instance AmbInst Integer { + ambval = 42; +} +instance AmbInst Float { + ambval = 3.14; +} + +wrongCode "Ambiguous type variable a (arising from the use of ambval) prevents the constraint silver_features:AmbInst a from being solved." { + global ambStr::String = hackUnparse(ambval); +} + +global intIsEqual::(Boolean ::= Integer Integer) = myeq; +equalityTest(intIsEqual(42, 42), true, Boolean, silver_tests); +equalityTest(intIsEqual(42, 34), false, Boolean, silver_tests); + +function myeq2 +MyEq a => Boolean ::= x::a y::a +{ + return myeq(x, y); +} +global intIsEqual2::(Boolean ::= Integer Integer) = myeq2; +equalityTest(intIsEqual2(42, 42), true, Boolean, silver_tests); +equalityTest(intIsEqual2(42, 34), false, Boolean, silver_tests); + +global isSingleDigit::(Boolean ::= String) = contains(_, ["1", "2", "3", "4", "5", "6", "7", "8", "9"]); +equalityTest(isSingleDigit("5"), true, Boolean, silver_tests); +equalityTest(isSingleDigit("42"), false, Boolean, silver_tests); + +class Semigroupoid (a :: * -> * -> *) { + sgcompose :: (a ::= a a); +} + +instance Semigroupoid (_ ::= _) { + sgcompose = compose; +} + +equalityTest(sgcompose(\ x::Integer -> x * 2, \ s::String -> toInteger(s))("42"), 84, Integer, silver_tests); + +wrongCode "(_ ::= _ _) has kind * -> * -> * -> *, but the class Semigroupoid expected kind * -> * -> *" { + instance Semigroupoid (_ ::= _ _) { + sgcompose = compose; + } +} + +wrongCode "Member sgcompose has expected type ((b ::= Integer c) ::= (b ::= Integer a) (a ::= Integer c)), but the expression has actual type ((b ::= c) ::= (b ::= a) (a ::= c))" { + instance Semigroupoid (_ ::= Integer _) { + sgcompose = compose; + } +} + +wrongCode "is a type alias" { + -- This caused a kind mismatch crash previously + type Func = (b ::= a); + instance Semigroupoid Func { + sgcompose = error("compose"); --\f::(d ::= c) g::(c ::= b) -> \x::b -> f(g(x)); + } +} + +wrongCode "a has kind * -> * -> *, but kind * is expected here" { + function badKind + Semigroupoid a => Integer ::= a + { + return 42; + } +} + +class BoolThing a { + bteq :: Eq b => (a ::= b b); +} + +instance BoolThing Maybe { + bteq = \ x::b y::b -> if x == y then just(unit()) else nothing(); +} + +equalityTest(bteq(42, 42), just(unit()), Maybe, silver_tests); +equalityTest(bteq(234, 42), nothing(), Maybe, silver_tests); + +class runtimeTypeable a => MyTypeable a { + myreify :: (a ::= AST) = reifyUnchecked; +} + +instance runtimeTypeable a => MyTypeable a {} + +instance MyTypeable Integer { + myreify = \ a::AST -> case a of integerAST(i) -> i | _ -> error("not integer") end; +} + +instance runtimeTypeable a, MyTypeable b => MyTypeable Pair { + myreify = \ a::AST -> case a of AST { silver:core:pair(fst, snd) } -> pair(reifyUnchecked(fst), myreify(snd)) | _ -> error("not pair") end; +} + +equalityTest(myreify(reflect(42)), 42, Integer, silver_tests); +equalityTest(myreify(reflect("hello")), "hello", String, silver_tests); +equalityTest(myreify(reflect(pair("abc", 123))), pair("abc", 123), Pair, silver_tests); + +wrongCode "a has kind * -> * -> *, but kind * is expected here" { + function badKind + Semigroupoid a, runtimeTypeable a => Integer ::= + { + return 42; + } +} + +nonterminal RTNT; +production rtProd +runtimeTypeable a => top::RTNT ::= fn::(Integer ::= a) +{} + +global rtCaseRes::Integer = + case rtProd(\ xs::[Integer] -> length(xs)) of + | rtProd(f) -> f(reifyUnchecked(reflect([1, 2, 3]))) + end; +equalityTest(rtCaseRes, 3, Integer, silver_tests); + +nonterminal SNT; +production sProd +i1 subset i2 => top::SNT ::= x::Decorated a with i2 f::([String] ::= Decorated a with i1) +{} + +global sCaseRes::[String] = + case sProd(decorate mkDExpr() with {env1 = ["a"]; env2 = ["2"];}, \ x::Decorated DExpr with {env2} -> x.env2) of + | sProd(x, f) -> f(castRef(x)) + end; +equalityTest(sCaseRes, ["2"], [String], silver_tests); + +function transitiveSubset +i1 subset i2, i2 subset i3, i3 subset i4 => Decorated a with i1 ::= x::Decorated a with i4 +{ + return castRef(x); +} + +function transitiveSubset2 +i1 subset {env1}, {env1, env2} subset i2 => Decorated a with i1 ::= x::Decorated a with i2 +{ + return castRef(x); +} + +wrongCode "i3 is not a subset of i2 (arising from the use of castRef)" { + function transitiveSubsetBad + i1 subset i2, i2 subset i3, i3 subset i4 => Decorated a with i3 ::= x::Decorated a with i2 + { + return castRef(x); + } +} + +wrongCode "i1 is not a subset of i2 (arising from the use of castRef)" { + function transitiveSubsetBad2 + i1 subset {env1, env2}, {env1} subset i2 => Decorated a with i1 ::= x::Decorated a with i2 + { + return castRef(x); + } +} + +class NotDecThing a { + consumeNDT :: (String ::= a); +} +instance NotDecThing a { + consumeNDT = \ x::a -> reflectTypeName(x).fromJust; +} +instance typeError "Not decorated" => NotDecThing Decorated a with i { + consumeNDT = error("type error"); +} + +global ndt1::String = consumeNDT(pair(12, false)); +equalityTest(ndt1, "silver:core:Pair", String, silver_tests); + +wrongCode "Not decorated (arising from the instance for silver_features:NotDecThing Decorated silver:core:Pair with {}, arising from the use of consumeNDT)" { + global ndt2::String = consumeNDT(decorate pair(12, false) with {}); +} + +function ndtMaybeDec +String ::= l::Location +{ + -- Should specialize to undecorated Location + return consumeNDT(l); +} +equalityTest(ndtMaybeDec(txtLoc("foo")), "silver:core:Location", String, silver_tests); + +wrongCode "typeError constraint is only permitted on instances" { + class typeError "bad" => TEClass a {} +} diff --git a/test/silver_features/Types.sv b/test/silver_features/Types.sv index 4f77a8e21..969a4b09e 100644 --- a/test/silver_features/Types.sv +++ b/test/silver_features/Types.sv @@ -1,24 +1,24 @@ import silver:testing; -------------------------------------- Number of parameters to type constructors +------------------------------------- Number/kinds of parameters to type constructors terminal ATerminalType 'doesnotmatter'; -wrongCode "ATerminalType has 0 type variables" { +wrongCode "ATerminalType has kind *, but there are 1 type arguments supplied here" { global t :: ATerminalType = error(""); } nonterminal NTZero; -wrongCode "NTZero has 0 type variables, but there are 1 supplied here" { +wrongCode "NTZero has kind *, but there are 1 type arguments supplied here" { global t :: NTZero = error(""); } nonterminal NTOne; -wrongCode "NTOne has 1 type variables, but there are 0 supplied here" { +wrongCode "NTOne has kind * -> *, but kind * is expected here" { global t :: NTOne = error(""); } -wrongCode "NTOne has 1 type variables, but there are 2 supplied here" { +wrongCode "NTOne has kind * -> *, but there are 2 type arguments supplied here" { global t :: NTOne = error(""); } @@ -29,8 +29,27 @@ wrongCode "NTZero' is already bound" { wrongCode "repeats type variable names" { nonterminal NTTwoBad; } +wrongCode "cannot contain _" { + nonterminal NTTwoBad; +} nonterminal NTTwo; +production ntt top::NTTwo ::= a b {} + +wrongCode "NTTwo has kind * -> * -> *, but kind * is expected here" { + global t :: NTTwo = error(""); +} +wrongCode "NTTwo<_ _> has kind * -> * -> *, but kind * is expected here" { + global t :: NTTwo<_ _> = error(""); +} +wrongCode "NTTwo has kind * -> *, but kind * is expected here" { + global t :: NTTwo = error(""); +} +wrongCode "Missing type argument cannot be followed by a provided argument" { + global t :: NTTwo<_ Integer> = error(""); +} + +global t2 :: NTTwo<_ _><_> = ntt(42, "abc"); synthesized attribute typeTest :: a; @@ -38,8 +57,49 @@ wrongCode "repeats type variable names" { attribute typeTest occurs on NTTwo; } ---nonterminal IO; -- parse error +wrongCode "Type parameter list cannot contain _" { + attribute typeTest occurs on NTTwo; +} + +wrongCode "Attribute type arguments cannot contain _" { + attribute typeTest occurs on NTTwo; +} + +global ctrList::[] = [1, 2, 3]; + +wrongCode "[] has kind * -> *, but kind * is expected here" { + global badCtrList1::[] = [1, 2, 3]; +} +wrongCode "[] has kind * -> *, but there are 2 type arguments supplied here" { + global badCtrList2::[] = [1, 2, 3]; +} +wrongCode "[Integer] has kind *, but there are 1 type arguments supplied here" { + global badCtrList2::[Integer] = [1, 2, 3]; +} + +wrongCode "ast has kind * -> * but type variable(s) have kind(s) * -> *." { + synthesized attribute ast::a; + nonterminal Nt with ast<[]>; + + -- We can still write an equation for the attr with the kind error in its occurs-on + abstract production ntThing + top::Nt ::= + { top.ast = [1]; } +} +-------------------------------------- Type variables are named properly in error messages +wrongCode "(bar, baz)" { + global foo::(bar, baz) = 42; +} +wrongCode "(b, a)" { + global foo::(b, a) = 42; +} +wrongCode "(a1, a)" { + global foo::(a1, a) = 42; +} +wrongCode "(a, b) has initialization expression with type (silver:core:Pair ::= c a)" { + global foo::(a, b) = pair(_, _); +} -------------------------------------- Type Decls @@ -54,14 +114,32 @@ type MyType2 = Integer; global anum1 :: MyType2 = 2; global astr3 :: MyType = toString(anum1); -wrongCode "MyType has 1 type variables, but there are 0 supplied here." { +wrongCode "MyType is a type alias, expecting 1 type arguments." { global t :: MyType = error(""); } -wrongCode "MyType has 1 type variables, but there are 2 supplied here" { +wrongCode "MyType expects 1 type arguments, but there are 2 supplied here" { global t :: MyType = error(""); } +wrongCode "MyType is a type alias and cannot be partially applied." { + global t :: MyType<_> = error(""); +} + +type MyTypePartial1 = NTTwo; + +global mt1::MyTypePartial1 = error(""); +wrongCode "silver_features:MyTypePartial1 expects 1 type arguments, but there are 2 supplied here" { + global mt2::MyTypePartial1 = error(""); +} + +type MyTypePartial2 = NTTwo<_ _>; + +wrongCode "MyTypePartial2 has kind * -> * -> *, but there are 1 type arguments supplied here" { + global mt3::MyTypePartial2 = error(""); +} +global mt4::MyTypePartial2 = error(""); + -- For the moment, errors ignore type names -wrongCode "Operands to == must be the same type. Instead they are String and Integer" { +wrongCode "Argument 2 of function 'silver:core:eq' expected String but argument is of type Integer" { global t :: Boolean = astr1 == anum1; } @@ -69,6 +147,16 @@ wrongCode "repeats type variable names" { type TypeTwo = Integer; } +wrongCode "Definition of silver_features:RecAliasFoo is self-referential" { + type RecAliasFoo = RecAliasBar; + type RecAliasBar = RecAliasFoo; +} + +wrongCode "Definition of silver_features:RecAliasBaz is self-referential" { + type RecAliasBaz = RecAliasQux; + type RecAliasQux = Either>; +} + ----------------------------------------- toString implementations equalityTest(toString("foo"), "foo", String, silver_tests); @@ -79,7 +167,7 @@ equalityTest(toString(false), "false", String, silver_tests); type MyType3 = Pair; -wrongCode "Operand to toString must be concrete types String, Integer, Float, or Boolean. Instead it is of type core:Pair" { +wrongCode "Could not find an instance for silver:core:ConvertiblePrim silver:core:Pair (arising from the use of toString)" { global m3t :: MyType3 = pair(0, ""); equalityTest(toString(m3t), "", String, silver_tests); } @@ -108,3 +196,132 @@ wrongCode "Declaration of global aft2 with type silver_features:FType ha global aft2 :: FType = aft1; } +-------------------------------------- Decorated/InhSet types + +inherited attribute env1::[String]; +inherited attribute env2::[String]; +nonterminal DExpr with env1, env2; +flowtype DExpr = decorate {env1}; +production mkDExpr +top::DExpr ::= +{ + production d::DExpr = mkDExpr(); + d.env1 = top.env1; + d.env2 = top.env2; + + production d1 :: Decorated DExpr = d; + production d2 :: Decorated DExpr with {env1} = d; + production d3 :: Decorated DExpr = d; + production d4 :: Decorated DExpr with {env1, env2} = d; + production d5 :: Decorated DExpr with {decorate} = d; + production d6 :: Decorated DExpr with {decorate, env2} = d; +} + +global d1 :: Decorated DExpr = decorate mkDExpr() with {env1 = [];}; +global d2 :: Decorated DExpr with {env1} = decorate mkDExpr() with {env1 = [];}; +global d3 :: Decorated DExpr = decorate mkDExpr() with {env1 = [];}; +global d4 :: Decorated DExpr with {env1, env2} = decorate mkDExpr() with {env1 = []; env2 = [];}; +global d5 :: Decorated DExpr with {decorate} = decorate mkDExpr() with {env1 = [];}; +global d6 :: Decorated DExpr with {decorate, env2} = decorate mkDExpr() with {env1 = []; env2 = [];}; + +type Inhs1 = {env1}; +global d7 :: Decorated DExpr with Inhs1 = decorate mkDExpr() with {env1 = [];}; + +global d8 :: Decorated DExpr with {env1} = castRef(decorate mkDExpr() with {env1 = []; env2 = [];}); +global d9 :: Decorated DExpr with {env1} = castRef(decorate mkDExpr() with {env1 = [];}); + +function getEnv1 +{env1} subset i => [String] ::= x::Decorated DExpr with i +{ + return let y::Decorated DExpr with {env1} = castRef(x) in y.env1 end; +} +global d10 :: [String] = getEnv1(decorate mkDExpr() with {env1 = []; env2 = [];}); +global d11 :: [String] = getEnv1(decorate mkDExpr() with {env1 = [];}); + +function getEnv1Direct +{env1} subset i => [String] ::= x::Decorated DExpr with i +{ + return x.env1; +} +global d12 :: [String] = getEnv1Direct(decorate mkDExpr() with {env1 = []; env2 = [];}); +global d13 :: [String] = getEnv1Direct(decorate mkDExpr() with {env1 = [];}); + +function getEnv1Chained +{env1} subset i1, i1 subset i2 => [String] ::= Decorated DExpr with i1 x::Decorated DExpr with i2 +{ + return x.env1; +} + +global d14 :: [String] = getEnv1Chained(decorate mkDExpr() with {env1 = []; env2 = [];}, decorate mkDExpr() with {env1 = []; env2 = [];}); +global d15 :: [String] = getEnv1Chained(decorate mkDExpr() with {env1 = [];}, decorate mkDExpr() with {env1 = []; env2 = [];}); +global d16 :: [String] = getEnv1Chained(decorate mkDExpr() with {env1 = [];}, decorate mkDExpr() with {env1 = [];}); + +function getEnv1Cycle +{env1} subset i1, i1 subset i2, i2 subset i1 => [String] ::= Decorated DExpr with i1 x::Decorated DExpr with i2 +{ + return x.env1; +} + +global d17 :: [String] = getEnv1Cycle(decorate mkDExpr() with {env1 = []; env2 = [];}, decorate mkDExpr() with {env1 = []; env2 = [];}); +global d18 :: [String] = getEnv1Cycle(decorate mkDExpr() with {env1 = [];}, decorate mkDExpr() with {env1 = [];}); + +wrongCode "{silver_features:env1, :env2} is not a subset of {silver_features:env1} (arising from the use of getEnv1Cycle)" { + global dBad :: [String] = getEnv1Cycle(decorate mkDExpr() with {env1 = [];}, decorate mkDExpr() with {env1 = []; env2 = [];}); +} + +function getEnv1ChainedAmb +{env1} subset i1, i1 subset i2 => [String] ::= x::Decorated DExpr with i2 +{ + return x.env1; +} + +wrongCode "Ambiguous type variable a (arising from the use of getEnv1ChainedAmb) prevents the constraint a subset {silver_features:env1, :env2} from being solved." { + global dAmb :: [String] = getEnv1ChainedAmb(decorate mkDExpr() with {env1 = []; env2 = [];}); +} + +wrongCode "type Decorated silver_features:DExpr with {silver_features:env1, :env2} has initialization expression with type Decorated silver_features:DExpr with {silver_features:env1}" { + global dBad :: Decorated DExpr with {env1, env2} = decorate mkDExpr() with {env1 = [];}; +} + +wrongCode "Integer has kind *, but kind InhSet is expected here" { + global dBad :: Decorated DExpr with Integer = error(""); +} + +wrongCode "{env1} has kind InhSet, but kind * is expected here" { + global inhBad :: {env1} = 42; +} + +wrongCode "type Decorated silver_features:DExpr with {silver_features:env1} has initialization expression with type Decorated silver_features:DExpr with {silver_features:env2}" { + global dBad :: Decorated DExpr with {env1} = let res :: Decorated DExpr with {env2} = error("") in res end; +} + +wrongCode "Expected return type is Decorated silver_features:DExpr with {silver_features:env1}, but the expression has actual type Decorated silver_features:DExpr with i" { + function decBad + Decorated DExpr with {env1} ::= x::Decorated DExpr with i + { + return let res :: Decorated DExpr with i = x in res end; + } +} + +wrongCode "{silver_features:env1, :env2} is not a subset of {silver_features:env1} (arising from the use of castRef)" { + global dSuper :: Decorated DExpr with {env1, env2} = castRef(decorate mkDExpr() with {env1 = [];}); +} +wrongCode "{silver_features:env2} is not a subset of {silver_features:env1} (arising from the use of castRef)" { + global dDisjoint :: Decorated DExpr with {env2} = castRef(decorate mkDExpr() with {env1 = [];}); +} +wrongCode "{silver_features:env1} is not a subset of {silver_features:env2} (arising from the use of getEnv1)" { + global dDisjoint2 :: [String] = getEnv1(decorate mkDExpr() with {env2 = [];}); +} + +-------------------------------------- Production LHSs +wrongCode "Production LHS type must be a nonterminal. Instead it is of type a" { + production varLHS + top::a ::= + {} +} + +wrongCode "Type incorrect in aspect signature. Expected: silver:core:Maybe Got: silver:core:Maybe" { + aspect production just + top::Maybe ::= x::String + {} +} diff --git a/test/silver_features/anno/Anno.sv b/test/silver_features/anno/Anno.sv index 62c521dd3..dd7731357 100644 --- a/test/silver_features/anno/Anno.sv +++ b/test/silver_features/anno/Anno.sv @@ -1,7 +1,6 @@ import silver_features; import silver:testing; -import lib:extcore; nonterminal AnnoNT with what; -- our target of abuse! @@ -79,9 +78,40 @@ wrongCode "Too few arguments provided " { } -- Let's ensure we can still pattern match alright -global grabstr :: String = case nt5 of moreAnnoNT(s) -> s end; +global grabstr :: String = case nt5 of moreAnnoNT(s) -> s | _ -> error("match failed") end; equalityTest ( grabstr, "str", String, silver_tests ) ; +-- Occurs-on contexts +function getWhat +annotation what occurs on a => +Integer ::= x::a +{ + return x.what; +} + +equalityTest ( getWhat(nt1), 1, Integer, silver_tests ) ; +equalityTest ( getWhat(nt2), 2, Integer, silver_tests ) ; + +class What a { + getWhat2 :: (Integer ::= a); +} + +instance annotation what occurs on a => What a { + getWhat2 = (.what); +} + +equalityTest ( getWhat2(nt1), 1, Integer, silver_tests ) ; +equalityTest ( getWhat2(nt2), 2, Integer, silver_tests ) ; + +class annotation what occurs on a => What2 a {} +instance What2 AnnoNT {} + +function getWhat3 +What2 a => Integer ::= x::a +{ return x.what; } + +equalityTest ( getWhat3(nt1), 1, Integer, silver_tests ) ; +equalityTest ( getWhat3(nt2), 2, Integer, silver_tests ) ; nonterminal AnnoNT2 with anno1, anno2; diff --git a/test/silver_features/cond/Test.sv b/test/silver_features/cond/Test.sv index b5efa329b..1b412f777 100644 --- a/test/silver_features/cond/Test.sv +++ b/test/silver_features/cond/Test.sv @@ -5,7 +5,6 @@ import silver_features:cond:b; import silver_features; import silver:testing; -import lib:extcore; -- should get :c. diff --git a/test/silver_features/defs/AttrDefs.sv b/test/silver_features/defs/AttrDefs.sv index 3fc95d6d8..7d32987b8 100644 --- a/test/silver_features/defs/AttrDefs.sv +++ b/test/silver_features/defs/AttrDefs.sv @@ -52,7 +52,7 @@ wrongCode "is not a nonterminal" { } wrongCode "Undeclared type 'a'" { - synthesized attribute foobad :: Function(a ::= a); + synthesized attribute foobad :: (a ::= a); } diff --git a/test/silver_features/global_sub/MoreGlobals.sv b/test/silver_features/global_sub/MoreGlobals.sv index a78605e83..fc2238e0b 100644 --- a/test/silver_features/global_sub/MoreGlobals.sv +++ b/test/silver_features/global_sub/MoreGlobals.sv @@ -1,7 +1,6 @@ import silver_features; import silver:testing; -import lib:extcore; global globalint1 :: Integer = 1 + 2 + 3; diff --git a/test/silver_features/myeq/MyEq.sv b/test/silver_features/myeq/MyEq.sv new file mode 100644 index 000000000..9e10e16f0 --- /dev/null +++ b/test/silver_features/myeq/MyEq.sv @@ -0,0 +1,27 @@ +grammar silver_features:myeq; + +class MyEq a +{ + myeq :: (Boolean ::= a a); + myneq :: (Boolean ::= a a) = \ x::a y::a -> !myeq(x, y); +} + +instance MyEq Integer +{ + myeq = \ x::Integer y::Integer -> x == y; +} + +instance MyEq String +{ + myeq = \ x::String y::String -> x == y; +} + +instance MyEq a => MyEq [a] +{ + myeq = \ xs::[a] ys::[a] -> length(xs) == length(ys) && all(zipWith(myeq, xs, ys)); +} + +instance MyEq a, MyEq b => MyEq Pair +{ + myeq = \ x::Pair y::Pair -> myeq(x.fst, y.fst) && myeq(x.snd, y.snd); +} diff --git a/test/silver_features/rewrite/Tests.sv b/test/silver_features/rewrite/Tests.sv index e7517c66c..a069dd2c3 100644 --- a/test/silver_features/rewrite/Tests.sv +++ b/test/silver_features/rewrite/Tests.sv @@ -1,7 +1,6 @@ grammar silver_features:rewrite; imports silver:testing; -imports lib:extcore; imports silver_features; imports silver:rewrite as s; @@ -9,32 +8,22 @@ imports silver:reflect; imports silver:langutil; imports silver:langutil:pp; -function showRes -String ::= res::Maybe -{ - return - case res of - | just(a) -> show(80, reflect(a).pp) - | nothing() -> "fail" - end; -} - global s1::s:Strategy = rule on [Integer] of | [1, 2, 3] -> [4, 5, 6] end; -equalityTest(showRes(rewriteWith(s1, [1, 2, 3])), "[4, 5, 6]", String, silver_tests); -equalityTest(showRes(rewriteWith(s1, [3, 2, 1])), "fail", String, silver_tests); +equalityTest(s:rewriteWith(s1, [1, 2, 3]), just([4, 5, 6]), Maybe<[Integer]>, silver_tests); +equalityTest(s:rewriteWith(s1, [3, 2, 1]), nothing(), Maybe<[Integer]>, silver_tests); global s2::s:Strategy = rule on [a] of | a :: [_, b, c] -> b :: [c, a] end; -equalityTest(showRes(rewriteWith(s2, [1, 2, 3, 4])), "[3, 4, 1]", String, silver_tests); -equalityTest(showRes(rewriteWith(s2, [true, false, true, false])), "[true, false, true]", String, silver_tests); -equalityTest(showRes(rewriteWith(s2, ["a", "b", "c", "d"])), "[\"c\", \"d\", \"a\"]", String, silver_tests); +equalityTest(s:rewriteWith(s2, [1, 2, 3, 4]), just([3, 4, 1]), Maybe<[Integer]>, silver_tests); +equalityTest(s:rewriteWith(s2, [true, false, true, false]), just([true, false, true]), Maybe<[Boolean]>, silver_tests); +equalityTest(s:rewriteWith(s2, ["a", "b", "c", "d"]), just(["c", "d", "a"]), Maybe<[String]>, silver_tests); global s3::s:Strategy = rule on Pair of @@ -42,8 +31,8 @@ global s3::s:Strategy = | pair(a, b) when a == b -> pair(a, 17) end; -equalityTest(showRes(rewriteWith(s3, pair(1, 2))), "core:pair(2, 1)", String, silver_tests); -equalityTest(showRes(rewriteWith(s3, pair(42, 42))), "core:pair(42, 17)", String, silver_tests); +equalityTest(s:rewriteWith(s3, pair(1, 2)), just(pair(2, 1)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s3, pair(42, 42)), just(pair(42, 17)), Maybe>, silver_tests); global s4::s:Strategy = rule on Pair of @@ -51,65 +40,60 @@ global s4::s:Strategy = | pair(a, b) when a != b -> pair(b, a) end; -equalityTest(showRes(rewriteWith(s4, pair(1, 2))), "core:pair(2, 1)", String, silver_tests); -equalityTest(showRes(rewriteWith(s4, pair(42, 42))), "core:pair(42, 17)", String, silver_tests); +equalityTest(s:rewriteWith(s4, pair(1, 2)), just(pair(2, 1)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s4, pair(42, 42)), just(pair(42, 17)), Maybe>, silver_tests); global s5::s:Strategy = rule on Pair of - | pair(n, s) when all(map(containsBy(stringEq, _, ["1", "2", "3", "4", "5", "6", "7", "8", "9"]), explode("", s))) -> + | pair(n, s) when all(map(contains(_, ["1", "2", "3", "4", "5", "6", "7", "8", "9"]), explode("", s))) -> pair(toInteger(s), toString(n)) | a -> a end; -equalityTest(showRes(rewriteWith(s5, pair(123, "4"))), "core:pair(4, \"123\")", String, silver_tests); -equalityTest(showRes(rewriteWith(s5, pair(467, "foo"))), "core:pair(467, \"foo\")", String, silver_tests); +equalityTest(s:rewriteWith(s5, pair(123, "4")), just(pair(4, "123")), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s5, pair(467, "foo")), just(pair(467, "foo")), Maybe>, silver_tests); global s6::s:Strategy = rule on Pair of | p -> case p of - | pair(n, s) when all(map(containsBy(stringEq, _, ["1", "2", "3", "4", "5", "6", "7", "8", "9"]), explode("", s))) -> + | pair(n, s) when all(map(contains(_, ["1", "2", "3", "4", "5", "6", "7", "8", "9"]), explode("", s))) -> pair(toInteger(s), toString(n)) | a -> a end end; -equalityTest(showRes(rewriteWith(s6, pair(123, "4"))), "core:pair(4, \"123\")", String, silver_tests); -equalityTest(showRes(rewriteWith(s6, pair(467, "foo"))), "core:pair(467, \"foo\")", String, silver_tests); +equalityTest(s:rewriteWith(s6, pair(123, "4")), just(pair(4, "123")), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s6, pair(467, "foo")), just(pair(467, "foo")), Maybe>, silver_tests); global s7::s:Strategy = rule on Pair of | p -> pair(p.snd, p.fst) end; -equalityTest(showRes(rewriteWith(s7, pair(123, 456))), "core:pair(456, 123)", String, silver_tests); -equalityTest(showRes(rewriteWith(s7, pair(123, "hello"))), "fail", String, silver_tests); - - -synthesized attribute isEqual::Boolean; -inherited attribute isEqualTo::Foo; - -nonterminal Foo with isEqual, isEqualTo; +equalityTest(s:rewriteWith(s7, pair(123, 456)), just(pair(456, 123)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s7, pair(123, "hello")), nothing(), Maybe>, silver_tests); +nonterminal Foo with isEqual, compareTo; abstract production foo top::Foo ::= n::Integer -{ - top.isEqual = case top.isEqualTo of foo(n1) -> n1 == n end; -} +{} + +propagate compareTo, isEqual on Foo; global s8::s:Strategy = rule on [Foo] of - | f1 :: f2 :: rest when decorate f1 with {isEqualTo = f2;}.isEqual -> f1 :: rest + | f1 :: f2 :: rest when decorate f1 with {compareTo = decorate f2 with {};}.isEqual -> f1 :: rest end; -equalityTest(showRes(rewriteWith(s8, [foo(1), foo(2), foo(3)])), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s8, [foo(2), foo(2), foo(3)])), "[silver_features:rewrite:foo(2), silver_features:rewrite:foo(3)]", String, silver_tests); +equalityTest(s:rewriteWith(s8, [foo(1), foo(2), foo(3)]), nothing(), Maybe<[Foo]>, silver_tests); +equalityTest(s:rewriteWith(s8, [foo(2), foo(2), foo(3)]), just([foo(2), foo(3)]), Maybe<[Foo]>, silver_tests); global s9::s:Strategy = rule on Pair of | pair(a, b) -> pair(b, _)(a) end; -equalityTest(showRes(rewriteWith(s9, pair(123, 456))), "core:pair(456, 123)", String, silver_tests); +equalityTest(s:rewriteWith(s9, pair(123, 456)), just(pair(456, 123)), Maybe>, silver_tests); annotation a1::Integer; annotation a2::Integer; @@ -120,19 +104,23 @@ abstract production barI top::Bar ::= Integer {} +instance Eq Bar { + eq = \ a::Bar b::Bar -> a.a1 == b.a1 && a.a2 == b.a2; +} + global s10::s:Strategy = rule on Bar of | barI(x, a1=y, a2=z) -> barI(_, a1=x, a2=_)(z, y) end; -equalityTest(showRes(rewriteWith(s10, barI(1, a1=2, a2=3))), "silver_features:rewrite:barI(3, silver_features:rewrite:a1=1, silver_features:rewrite:a2=2)", String, silver_tests); +equalityTest(s:rewriteWith(s10, barI(1, a1=2, a2=3)), just(barI(3, a1=1, a2=2)), Maybe, silver_tests); global s11::s:Strategy = rule on Pair> of | pair(a, b) when b.fst < 10 -> pair(b.fst, pair(b.snd, a)) end; -equalityTest(showRes(rewriteWith(s11, pair(1, pair(2, 3)))), "core:pair(2, core:pair(3, 1))", String, silver_tests); +equalityTest(s:rewriteWith(s11, pair(1, pair(2, 3))), just(pair(2, pair(3, 1))), Maybe>>, silver_tests); global s12::s:Strategy = rule on Maybe> of @@ -141,72 +129,72 @@ global s12::s:Strategy = -- Result contains a decorated node, so tricky to test exactly. -- Mostly just concerned that this one compiles properly. -equalityTest(rewriteWith(s12, just(decorate pair(123, 456) with {})).isJust, true, Boolean, silver_tests); +equalityTest(s:rewriteWith(s12, just(decorate pair(123, 456) with {})).isJust, true, Boolean, silver_tests); global s13::s:Strategy = rule on Pair of | pair(a, b) -> pair(b, a) end; -equalityTest(showRes(rewriteWith(s13, pair(123, 456))), "core:pair(456, 123)", String, silver_tests); -equalityTest(showRes(rewriteWith(s13, pair(123, "hello"))), "fail", String, silver_tests); +equalityTest(s:rewriteWith(s13, pair(123, 456)), just(pair(456, 123)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s13, pair(123, "hello")), nothing(), Maybe>, silver_tests); global s14::s:Strategy = rule on [[Integer]] of | [n] :: rest -> rest ++ [[n + 1]] end; -equalityTest(showRes(rewriteWith(s14, [[2], [1, 2]])), "[[1, 2], [3]]", String, silver_tests); -equalityTest(showRes(rewriteWith(s13, [[]])), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s13, [["a"]])), "fail", String, silver_tests); +equalityTest(s:rewriteWith(s14, [[2], [1, 2]]), just([[1, 2], [3]]), Maybe<[[Integer]]>, silver_tests); +equalityTest(s:rewriteWith(s13, [[]]), nothing(), Maybe<[[Integer]]>, silver_tests); +equalityTest(s:rewriteWith(s13, [["a"]]), nothing(), Maybe<[[String]]>, silver_tests); global inc::s:Strategy = rule on Integer of i -> i + 1 end; global s15::s:Strategy = traverse pair(_, inc); -equalityTest(showRes(rewriteWith(s15, pair(1, 2))), "core:pair(1, 3)", String, silver_tests); -equalityTest(showRes(rewriteWith(s15, pair("a", "b"))), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s15, [["a"]])), "fail", String, silver_tests); +equalityTest(s:rewriteWith(s15, pair(1, 2)), just(pair(1, 3)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s15, pair("a", "b")), nothing(), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s15, [["a"]]), nothing(), Maybe<[[String]]>, silver_tests); global s16::s:Strategy = traverse barI(inc, a1=inc, a1=_, a1=inc); -equalityTest(showRes(rewriteWith(s16, barI(1, a1=2, a2=3))), "silver_features:rewrite:barI(2, silver_features:rewrite:a1=4, silver_features:rewrite:a2=3)", String, silver_tests); +equalityTest(s:rewriteWith(s16, barI(1, a1=2, a2=3)), just(barI(2, a1=4, a2=3)), Maybe, silver_tests); global s17::s:Strategy = s:rec(\ s::s:Strategy -> traverse pair(s, s) <+ s:try(inc)); -equalityTest(showRes(rewriteWith(s17, pair(1, 2))), "core:pair(2, 3)", String, silver_tests); +equalityTest(s:rewriteWith(s17, pair(1, 2)), just(pair(2, 3)), Maybe>, silver_tests); equalityTest( - showRes(rewriteWith(s17, pair(pair(pair("a", 1), pair("b", 2)), pair(true, 3)))), - "core:pair(core:pair(core:pair(\"a\", 2), core:pair(\"b\", 3)), core:pair(true, 4))", - String, silver_tests); + s:rewriteWith(s17, pair(pair(pair("a", 1), pair("b", 2)), pair(true, 3))), + just(pair(pair(pair("a", 2), pair("b", 3)), pair(true, 4))), + Maybe Pair> Pair>>, silver_tests); global s18::s:Strategy = s:rec(\ s::s:Strategy -> traverse (s :: s) <+ traverse [] <+ s:try(inc)); -equalityTest(showRes(rewriteWith(s18, [[1], [], [2, 3]])), "[[2], [], [3, 4]]", String, silver_tests); -equalityTest(showRes(rewriteWith(s18, [[just(1)]])), "[[core:just(1)]]", String, silver_tests); +equalityTest(s:rewriteWith(s18, [[1], [], [2, 3]]), just([[2], [], [3, 4]]), Maybe<[[Integer]]>, silver_tests); +equalityTest(s:rewriteWith(s18, [[just(1)]]), just([[just(1)]]), Maybe<[[Maybe]]>, silver_tests); global s19::s:Strategy = s:rec(\ s::s:Strategy -> traverse (_ :: s) <+ rule on [Integer] of [] -> [42] end); -equalityTest(showRes(rewriteWith(s19, [1, 2, 3])), "[1, 2, 3, 42]", String, silver_tests); +equalityTest(s:rewriteWith(s19, [1, 2, 3]), just([1, 2, 3, 42]), Maybe<[Integer]>, silver_tests); global s20::s:Strategy = s:rec(\ s::s:Strategy -> traverse [_, s, _] <+ s:try(inc)); -equalityTest(showRes(rewriteWith(s20, [1, 2, 3])), "[1, 3, 3]", String, silver_tests); -equalityTest(showRes(rewriteWith(s20, [1, 2, 3, 4])), "[1, 2, 3, 4]", String, silver_tests); -equalityTest(showRes(rewriteWith(s20, [[1, 2, 3], [4, 5, 6], [7, 8, 9]])), "[[1, 2, 3], [4, 6, 6], [7, 8, 9]]", String, silver_tests); +equalityTest(s:rewriteWith(s20, [1, 2, 3]), just([1, 3, 3]), Maybe<[Integer]>, silver_tests); +equalityTest(s:rewriteWith(s20, [1, 2, 3, 4]), just([1, 2, 3, 4]), Maybe<[Integer]>, silver_tests); +equalityTest(s:rewriteWith(s20, [[1, 2, 3], [4, 5, 6], [7, 8, 9]]), just([[1, 2, 3], [4, 6, 6], [7, 8, 9]]), Maybe<[[Integer]]>, silver_tests); global s21::s:Strategy = s:all(inc); -equalityTest(showRes(rewriteWith(s21, pair(1, 2))), "core:pair(2, 3)", String, silver_tests); -equalityTest(showRes(rewriteWith(s21, pair(true, 2))), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s21, pair(true, false))), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s21, [1, 2, 3])), "fail", String, silver_tests); +equalityTest(s:rewriteWith(s21, pair(1, 2)), just(pair(2, 3)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s21, pair(true, 2)), nothing(), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s21, pair(true, false)), nothing(), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s21, [1, 2, 3]), nothing(), Maybe<[Integer]>, silver_tests); global s22::s:Strategy = s:some(inc); -equalityTest(showRes(rewriteWith(s22, pair(1, 2))), "core:pair(2, 3)", String, silver_tests); -equalityTest(showRes(rewriteWith(s22, pair(true, 2))), "core:pair(true, 3)", String, silver_tests); -equalityTest(showRes(rewriteWith(s22, pair(true, false))), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s22, [1, 2, 3])), "[2, 2, 3]", String, silver_tests); +equalityTest(s:rewriteWith(s22, pair(1, 2)), just(pair(2, 3)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s22, pair(true, 2)), just(pair(true, 3)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s22, pair(true, false)), nothing(), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s22, [1, 2, 3]), just([2, 2, 3]), Maybe<[Integer]>, silver_tests); global s23::s:Strategy = s:one(inc); -equalityTest(showRes(rewriteWith(s23, pair(1, 2))), "core:pair(2, 2)", String, silver_tests); -equalityTest(showRes(rewriteWith(s23, pair(true, 2))), "core:pair(true, 3)", String, silver_tests); -equalityTest(showRes(rewriteWith(s23, pair(true, false))), "fail", String, silver_tests); -equalityTest(showRes(rewriteWith(s23, [1, 2, 3])), "[2, 2, 3]", String, silver_tests); +equalityTest(s:rewriteWith(s23, pair(1, 2)), just(pair(2, 2)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s23, pair(true, 2)), just(pair(true, 3)), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s23, pair(true, false)), nothing(), Maybe>, silver_tests); +equalityTest(s:rewriteWith(s23, [1, 2, 3]), just([2, 2, 3]), Maybe<[Integer]>, silver_tests); diff --git a/test/silver_features/rewrite/expreval/AbstractSyntax.sv b/test/silver_features/rewrite/expreval/AbstractSyntax.sv index 65445df60..6b5292897 100644 --- a/test/silver_features/rewrite/expreval/AbstractSyntax.sv +++ b/test/silver_features/rewrite/expreval/AbstractSyntax.sv @@ -1,9 +1,9 @@ grammar silver_features:rewrite:expreval; +imports silver:core hiding add, sub, mul, div; imports silver:langutil; imports silver:langutil:pp; imports silver:rewrite; -imports core:monad; synthesized attribute needsParens::Boolean; @@ -74,14 +74,16 @@ Strategy ::= n::String e::Expr end)); } +global intGCD::(Integer ::= Integer Integer) = gcd; + global evalStep::Strategy = rule on Expr of | add(const(a), const(b)) -> const(a + b) | sub(const(a), const(b)) -> const(a - b) | mul(const(a), const(b)) -> const(a * b) | div(const(a), const(b)) when b != 0 && a % b == 0 -> const(a / b) - | div(const(a), const(b)) when b != 0 && gcd(a, b) > 1 -> - let g::Integer = gcd(a, b) in div(const(a / g), const(b / g)) end + | div(const(a), const(b)) when b != 0 && intGCD(a, b) > 1 -> + let g::Integer = intGCD(a, b) in div(const(a / g), const(b / g)) end -- This rule does not respect lexical shadowing; -- it is assumed that the overall rewrite will be done in an innermost order. | letE(n, e1, e2) -> rewriteWith(subst(n, e1), e2).fromJust diff --git a/test/silver_features/rewrite/expreval/Tests.sv b/test/silver_features/rewrite/expreval/Tests.sv index 214867832..c6403fafe 100644 --- a/test/silver_features/rewrite/expreval/Tests.sv +++ b/test/silver_features/rewrite/expreval/Tests.sv @@ -1,7 +1,6 @@ grammar silver_features:rewrite:expreval; import silver:testing; -import lib:extcore; import silver_features; global test1::Expr = parseExpr("1 + (2 * 3)"); diff --git a/test/silver_features/silver-compile b/test/silver_features/silver-compile index 2ad356627..358faadf2 100755 --- a/test/silver_features/silver-compile +++ b/test/silver_features/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/test/silver_features/treegen/Treegen.sv b/test/silver_features/treegen/Treegen.sv new file mode 100644 index 000000000..659f5756c --- /dev/null +++ b/test/silver_features/treegen/Treegen.sv @@ -0,0 +1,24 @@ +grammar silver_features:treegen; + +imports silver_features; +imports silver:testing; +imports silver:util:random; + +terminal A 'a' lexer classes C1; +terminal AB /a|b/ lexer classes C2; +lexer class C2; +lexer class C1 dominates C2; + +synthesized attribute name::String; +nonterminal Foo with name; +concrete productions top::Foo +| ab::AB { top.name = ab.lexeme; } + +generator genFoo::Foo { + silver_features:treegen; +} + +equalityTest( + map((.name), runSeedRandomGen(42, sequence(repeat(genFoo(3, 4), 10)))), + repeat("b", 10), + [String], silver_tests); diff --git a/test/stdlib/.gitignore b/test/stdlib/.gitignore new file mode 100644 index 000000000..dfe78b01e --- /dev/null +++ b/test/stdlib/.gitignore @@ -0,0 +1 @@ +test_svb.svb \ No newline at end of file diff --git a/test/stdlib/Function.sv b/test/stdlib/Function.sv new file mode 100644 index 000000000..6ada30a60 --- /dev/null +++ b/test/stdlib/Function.sv @@ -0,0 +1,6 @@ +grammar stdlib; + +global inc::(Integer ::= Integer) = \ i::Integer -> i + 1; +global incTwice::(Integer ::= Integer) = compose(inc, inc); + +equalityTest(incTwice(42), 44, Integer, core_tests); diff --git a/test/stdlib/List.sv b/test/stdlib/List.sv index 5186f0ad8..29e77b559 100644 --- a/test/stdlib/List.sv +++ b/test/stdlib/List.sv @@ -19,11 +19,11 @@ equalityTest ( foldl(sub, 1, [3]), -2, Integer, core_tests ) ; equalityTest ( foldl(sub, 10, [2,4]), 4, Integer, core_tests ) ; -equalityTest ( foldl(stringConcat, "1 ", ["10 ","2 ","4"]), "1 10 2 4", +equalityTest ( foldl(append, "1 ", ["10 ","2 ","4"]), "1 10 2 4", String, core_tests ) ; equalityTest ( foldr(sub, 2, [10,8,4]), 4, Integer, core_tests ) ; -equalityTest ( foldr(stringConcat, "2", ["10","8","4"]), "10842", +equalityTest ( foldr(append, "2", ["10","8","4"]), "10842", String, core_tests ) ; equalityTest ( foldr1(sub,[10,4,2]), 8, Integer, core_tests ) ; @@ -58,38 +58,38 @@ equalityTest ( partition(even, [1,2,3,4,5,6,7,8]).fst, [2,4,6,8], [Integer], core_tests ) ; --- containsBy TODO +-- contains TODO --- nubBy tests -equalityTest ( nubBy (equalsInteger, [1,2,3,4,3,2,1]), [1,2,3,4], +-- nub tests +equalityTest ( nub ([1,2,3,4,3,2,1]), [1,2,3,4], [Integer], core_tests ) ; -equalityTest ( nubBy (equalsInteger, [1,2,3,4]), [1,2,3,4], +equalityTest ( nub ([1,2,3,4]), [1,2,3,4], [Integer], core_tests ) ; -equalityTest ( nubBy (equalsInteger, [ ]), [ ], +equalityTest ( nub ([ ]), [ ], [Integer], core_tests ) ; --- removeBy tests -equalityTest ( removeBy (equalsInteger, 2, [1,2,3,4,3,2,1]), [1,3,4,3,1], +-- remove tests +equalityTest ( remove (2, [1,2,3,4,3,2,1]), [1,3,4,3,1], [Integer], core_tests ) ; -equalityTest ( removeBy (equalsInteger, 9, [1,2,3,4,3,2,1]), [1,2,3,4,3,2,1], +equalityTest ( remove (9, [1,2,3,4,3,2,1]), [1,2,3,4,3,2,1], [Integer], core_tests ) ; -equalityTest ( removeBy (equalsInteger, 9, [ ]), [ ], +equalityTest ( remove (9, [ ]), [ ], [Integer], core_tests ) ; --- removeAllBy tests -equalityTest ( removeAllBy (equalsInteger, [], [1,2,3]), [1,2,3], +-- removeAll tests +equalityTest ( removeAll ([], [1,2,3]), [1,2,3], [Integer], core_tests ) ; -equalityTest ( removeAllBy (equalsInteger, [1,3,5], [1,2,3,4,5,6]), [2,4,6], +equalityTest ( removeAll ([1,3,5], [1,2,3,4,5,6]), [2,4,6], [Integer], core_tests ) ; -equalityTest ( removeAllBy (equalsInteger, [1,3,5], [1,1,3,4,5,3]), [4], +equalityTest ( removeAll ([1,3,5], [1,1,3,4,5,3]), [4], [Integer], core_tests ) ; -equalityTest ( removeAllBy (equalsInteger, [1,3,5], [5,3,1,3,5]), [], +equalityTest ( removeAll ([1,3,5], [5,3,1,3,5]), [], [Integer], core_tests ) ; -- last @@ -147,33 +147,34 @@ equalityTest ( takeUntil (equals1, []), [], [Integer], core_tests ) ; -- positionOf -equalityTest ( positionOf ( equalsInteger, 1, []), -1, Integer, core_tests) ; -equalityTest ( positionOf ( equalsInteger, 1, [1,2]), 0, Integer, core_tests) ; -equalityTest ( positionOf ( equalsInteger, 3, [0,1,2,3]), 3, Integer, core_tests) ; +equalityTest ( positionOf ( 1, []), -1, Integer, core_tests) ; +equalityTest ( positionOf ( 1, [1,2]), 0, Integer, core_tests) ; +equalityTest ( positionOf ( 3, [0,1,2,3]), 3, Integer, core_tests) ; -- repeat TODO -- zipWith TODO -- reverse TODO --- sortBy -equalityTest ( null(sortBy(stringLte, [])), true, +-- sort +global emptyIntList::[Integer] = []; +equalityTest ( null(sort(emptyIntList)), true, Boolean, core_tests ) ; -equalityTest ( sortBy(stringLte, ["cd", "ca", "b", "z", "a"]), - ["a", "b", "ca", "cd", "z"], +equalityTest ( sort(["cd", "ca", "b", "z", "a"]), + ["a", "b", "ca", "cd", "z"], [String], core_tests ) ; --- groupBy -equalityTest ( null(groupBy(equalsString, [])), true, +-- group +equalityTest ( null(group(emptyIntList)), true, Boolean, core_tests ) ; -equalityTest ( map(head, groupBy(equalsInteger, [1, 1, 2, 1, 2, 2])), [1,2,1,2], +equalityTest ( map(head, group([1, 1, 2, 1, 2, 2])), [1,2,1,2], [Integer], core_tests ) ; -equalityTest ( head(groupBy(equalsInteger, [1, 1, 2, 1, 2, 2])), [1,1], +equalityTest ( head(group([1, 1, 2, 1, 2, 2])), [1,1], [Integer], core_tests ) ; -equalityTest ( map(head,groupBy(equalsInteger, [1, 2, 1, 3])), [1,2,1,3], +equalityTest ( map(head,group([1, 2, 1, 3])), [1,2,1,3], [Integer], core_tests ) ; -equalityTest ( head(tail(groupBy(equalsInteger, [1, 1, 2, 1, 2, 2]))), [2], +equalityTest ( head(tail(group([1, 1, 2, 1, 2, 2]))), [2], [Integer], core_tests ) ; -equalityTest ( map(listLength, groupBy(equalsInteger, [1, 1, 2, 1, 2, 2])), [2,1,1,2], +equalityTest ( map(listLength, group([1, 1, 2, 1, 2, 2])), [2,1,1,2], [Integer], core_tests ) ; -- intersperse @@ -182,51 +183,51 @@ equalityTest ( intersperse(1, [2]), [2], [Integer], core_tests ) ; equalityTest ( intersperse(1, []), [], [Integer], core_tests ) ; -- set operations -equalityTest ( unionBy (equalsInteger, [], []), [], +equalityTest ( union ([], []), [], [Integer], core_tests ) ; -equalityTest ( unionBy (equalsInteger, [1,2,3], []), [1,2,3], +equalityTest ( union ([1,2,3], []), [1,2,3], [Integer], core_tests ) ; -equalityTest ( unionBy (equalsInteger, [], [1,2,3]), [1,2,3], +equalityTest ( union ([], [1,2,3]), [1,2,3], [Integer], core_tests ) ; -equalityTest ( unionBy (equalsInteger, [1,2,3], [4,5,6]), [1,2,3,4,5,6], +equalityTest ( union ([1,2,3], [4,5,6]), [1,2,3,4,5,6], [Integer], core_tests ) ; -equalityTest ( unionBy (equalsInteger, [1,2,3], [1,4,5,6]), [2,3,1,4,5,6], +equalityTest ( union ([1,2,3], [1,4,5,6]), [2,3,1,4,5,6], [Integer], core_tests ) ; -equalityTest ( unionBy (equalsInteger, [1,2,3], [3,4,5,6,1]), [2,3,4,5,6,1], +equalityTest ( union ([1,2,3], [3,4,5,6,1]), [2,3,4,5,6,1], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [], []), [], +equalityTest ( intersect ([], []), [], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [1,2,3], []), [], +equalityTest ( intersect ([1,2,3], []), [], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [], [1,2,3]), [], +equalityTest ( intersect ([], [1,2,3]), [], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [1,2,3], [4,5,6]), [], +equalityTest ( intersect ([1,2,3], [4,5,6]), [], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [1,2,3], [4,2,6]), [2], +equalityTest ( intersect ([1,2,3], [4,2,6]), [2], [Integer], core_tests ) ; -equalityTest ( intersectBy (equalsInteger, [1,2,3], [4,2,3,6]), [2,3], +equalityTest ( intersect ([1,2,3], [4,2,3,6]), [2,3], [Integer], core_tests ) ; -equalityTest ( unionsBy (equalsInteger, [ [1,2], [2,3], [1,4,5,6] ] ), [1,2,3,4,5,6] , +equalityTest ( unions ([ [1,2], [2,3], [1,4,5,6] ] ), [1,2,3,4,5,6] , [Integer], core_tests ) ; - ----- from lib:extcore! -equalityTest ( equalsList ( equalsInteger, [], []), true, Boolean, core_tests) ; -equalityTest ( equalsList ( equalsInteger, [1], [1]), true, Boolean, core_tests) ; -equalityTest ( equalsList ( equalsInteger, [1,2], [1,2]), true, Boolean, core_tests) ; -equalityTest ( equalsList ( equalsInteger, [1], [2]), false, Boolean, core_tests) ; -equalityTest ( equalsList ( equalsInteger, [1], [1,2]), false, Boolean, core_tests) ; -equalityTest ( equalsList ( equalsInteger, [1,2], []), false, Boolean, core_tests) ; - -equalityTest ( notEqualsList ( notEqualsInteger, [], []), false, Boolean, core_tests) ; -equalityTest ( notEqualsList ( notEqualsInteger, [1], [1]), false, Boolean, core_tests) ; -equalityTest ( notEqualsList ( notEqualsInteger, [1,2], [1,2]), false, Boolean, core_tests) ; -equalityTest ( notEqualsList ( notEqualsInteger, [1], [2]), true, Boolean, core_tests) ; -equalityTest ( notEqualsList ( notEqualsInteger, [1], [1,2]), true, Boolean, core_tests) ; -equalityTest ( notEqualsList ( notEqualsInteger, [1,2], []), true, Boolean, core_tests) ; +-- eq +equalityTest ( emptyIntList == [], true, Boolean, core_tests) ; +equalityTest ( [1] == [1], true, Boolean, core_tests) ; +equalityTest ( [1,2] == [1,2], true, Boolean, core_tests) ; +equalityTest ( [1] == [2], false, Boolean, core_tests) ; +equalityTest ( [1] == [1,2], false, Boolean, core_tests) ; +equalityTest ( [1,2] == [], false, Boolean, core_tests) ; + +-- neq +equalityTest ( emptyIntList != [], false, Boolean, core_tests) ; +equalityTest ( [1] != [1], false, Boolean, core_tests) ; +equalityTest ( [1,2] != [1,2], false, Boolean, core_tests) ; +equalityTest ( [1] != [2], true, Boolean, core_tests) ; +equalityTest ( [1] != [1,2], true, Boolean, core_tests) ; +equalityTest ( [1,2] != [], true, Boolean, core_tests) ; diff --git a/test/stdlib/Main.sv b/test/stdlib/Main.sv index 79cfe27a9..f18cf73fd 100644 --- a/test/stdlib/Main.sv +++ b/test/stdlib/Main.sv @@ -1,16 +1,13 @@ grammar stdlib ; imports silver:testing ; -imports lib:extcore ; -import stdlib:treemap; -- run test tests, too. -import stdlib:fixedmap; import stdlib:cmdargs; import stdlib:deque; import stdlib:pplib; -import stdlib:rawtreeset; -import stdlib:rawtreemap; -import stdlib:rawgraph; +import stdlib:treeset; +import stdlib:treemap; +import stdlib:graph; import stdlib:xml; diff --git a/test/stdlib/Maybe.sv b/test/stdlib/Maybe.sv index 24dcd7317..fe7e07714 100644 --- a/test/stdlib/Maybe.sv +++ b/test/stdlib/Maybe.sv @@ -16,3 +16,21 @@ equalityTest ( orElse(just(false), nothing()).fromJust, false, equalityTest ( orElse(nothing(), nothing()).isJust, false, Boolean, core_tests ) ; +-- MaybeT +global mts::MaybeT String> = + do { + x::Integer <- lift(getState()); + lift(setState(x + 2)); + if x < 0 then fail("negative") else pure(unit()); + when_(x % 2 != 0, lift(setState(x + 1))); + y::Integer <- lift(getState()); + return toString(y / (x + 1)); + }; + +equalityTest(runState(mts.run, -4), pair(-2, nothing()), Pair>, core_tests); +equalityTest(runState(mts.run, -1), pair(1, nothing()), Pair>, core_tests); +equalityTest(runState(mts.run, 0), pair(2, just("2")), Pair>, core_tests); +equalityTest(runState(mts.run, 1), pair(2, just("1")), Pair>, core_tests); +equalityTest(runState(mts.run, 2), pair(4, just("1")), Pair>, core_tests); +equalityTest(runState(mts.run, 3), pair(4, just("1")), Pair>, core_tests); +equalityTest(runState(mts.run, 5), pair(6, just("1")), Pair>, core_tests); diff --git a/test/stdlib/NativeSerialize.sv b/test/stdlib/NativeSerialize.sv new file mode 100644 index 000000000..a72fa4684 --- /dev/null +++ b/test/stdlib/NativeSerialize.sv @@ -0,0 +1,68 @@ +import silver:reflect:nativeserialize; + +annotation thingy::Integer; + +nonterminal NSNT with thingy; + +terminal NSTerm_t /z*/; + +abstract production ns_foo +top::NSNT ::= z::Integer f::Float b::Boolean s::String t::NSTerm_t +{} + +abstract production ns_bar +top::NSNT ::= a::NSNT b::NSNT c::[String] +{} + + +global val1 :: NSNT = ns_bar(ns_foo(1, 0.5, false, "AAA", terminal(NSTerm_t, "zzz"), thingy=42), + ns_foo(2, 0.25, true, "BBB", terminal(NSTerm_t, "zzzzzz"), thingy=24), + ["garfield", "odie", "nermal"], thingy=2442); +global ser1 :: Either = nativeSerialize(val1); +global des1 :: Either = if ser1.isRight then nativeDeserialize(ser1.fromRight) else left(ser1.fromLeft); +global fin1 :: String = if des1.isRight then hackUnparse(des1.fromRight) else des1.fromLeft; + +equalityTest( + hackUnparse(val1), fin1, + String, core_tests); + + + +global val2 :: [Integer] = [111, 222, 333]; +global ser2 :: Either = nativeSerialize(val2); +global des2 :: Either = if ser2.isRight then nativeDeserialize(ser2.fromRight) else left(ser2.fromLeft); +global fin2 :: String = if des2.isRight then hackUnparse(des2.fromRight) else des2.fromLeft; + +equalityTest( + hackUnparse(val2), fin2, + String, core_tests); + + + +global val3 :: [Integer] = [111, 222, 333]; +global ser3 :: Either = nativeSerialize(val3); +global des3 :: Either = if ser3.isRight then nativeDeserialize(ser3.fromRight) else left(ser3.fromLeft); +global fin3 :: String = if des3.isRight then hackUnparse(des3.fromRight) else des3.fromLeft; + +equalityTest( + "nativeDeserialize is constructing [String], but found [Integer]", fin3, + String, core_tests); + + +global val4 :: [Integer] = repeat(123, 65537); -- Test long lists +global ser4 :: Either = nativeSerialize(val4); +global des4 :: Either = if ser4.isRight then nativeDeserialize(ser4.fromRight) else left(ser4.fromLeft); +global fin4 :: String = if des4.isRight then hackUnparse(des4.fromRight) else des4.fromLeft; + +equalityTest( + hackUnparse(val4), fin4, + String, core_tests); + + +global bytefiletest::IO = do { + writeBinaryFile("test_svb.svb", nativeSerialize(val1).fromRight); + readBinaryFile("test_svb.svb"); +}; + +equalityTest(hackUnparse(nativeDeserialize(evalIO(bytefiletest, unsafeIO()).iovalue).fromRight), hackUnparse(val1), + String, core_tests); diff --git a/test/stdlib/Pair.sv b/test/stdlib/Pair.sv index bc60af068..0e39f6011 100644 --- a/test/stdlib/Pair.sv +++ b/test/stdlib/Pair.sv @@ -16,24 +16,24 @@ global pairtester :: [Pair] = pair("3", 2)]; -equalityTest ( lookupBy(stringEq, "A", pairtester).isJust, false, +equalityTest ( lookup("A", pairtester).isJust, false, Boolean, core_tests ) ; -equalityTest ( lookupBy(stringEq, "1", pairtester).isJust, true, +equalityTest ( lookup("1", pairtester).isJust, true, Boolean, core_tests ) ; -equalityTest ( lookupBy(stringEq, "2", pairtester).fromJust, 2, +equalityTest ( lookup("2", pairtester).fromJust, 2, Integer, core_tests ) ; -equalityTest ( lookupBy(stringEq, "1", pairtester).fromJust, 1, +equalityTest ( lookup("1", pairtester).fromJust, 1, Integer, core_tests ) ; -equalityTest ( lookupBy(stringEq, "99", pairtester).fromJust, 99, +equalityTest ( lookup("99", pairtester).fromJust, 99, Integer, core_tests ) ; -equalityTest ( lookupBy(stringEq, "4", pairtester).fromJust, 4, +equalityTest ( lookup("4", pairtester).fromJust, 4, Integer, core_tests ) ; -equalityTest(lookupAllBy(stringEq, "1", pairtester), [1], [Integer], core_tests); -equalityTest(lookupAllBy(stringEq, "2", pairtester), [2], [Integer], core_tests); -equalityTest(lookupAllBy(stringEq, "4", pairtester), [4,3], [Integer], core_tests); -equalityTest(lookupAllBy(stringEq, "3", pairtester), [3,2], [Integer], core_tests); -equalityTest(lookupAllBy(stringEq, "A", pairtester), [], [Integer], core_tests); +equalityTest(lookupAll("1", pairtester), [1], [Integer], core_tests); +equalityTest(lookupAll("2", pairtester), [2], [Integer], core_tests); +equalityTest(lookupAll("4", pairtester), [4,3], [Integer], core_tests); +equalityTest(lookupAll("3", pairtester), [3,2], [Integer], core_tests); +equalityTest(lookupAll("A", pairtester), [], [Integer], core_tests); equalityTest(unzipPairs(pairtester).fst, ["1", "2", "3", "99", "4", "4", "3"], [String], core_tests); equalityTest(unzipPairs(pairtester).snd, [1, 2, 3, 99, 4, 3, 2], [Integer], core_tests); diff --git a/test/stdlib/String.sv b/test/stdlib/String.sv index 665866107..503d93d32 100644 --- a/test/stdlib/String.sv +++ b/test/stdlib/String.sv @@ -96,16 +96,16 @@ equalityTest ( toIntSafe("2147483647").fromJust, 2147483647, equalityTest ( toIntSafe("2147483648").isJust, false, Boolean, core_tests ) ; --- sconcat -equalityTest ( sconcat(["12", "34"]), "1234", String, core_tests ) ; -equalityTest ( sconcat([]), "", String, core_tests ) ; +-- concat +equalityTest ( concat(["12", "34"]), "1234", String, core_tests ) ; +equalityTest ( concat([]), "", String, core_tests ) ; --- sflatMap +-- flatMap function dupItemString String ::= i::Integer { return toString(i) ++ toString(i); } -equalityTest ( sflatMap(dupItemString, [1, 2]), "1122", String, core_tests ) ; +equalityTest ( flatMap(dupItemString, [1, 2]), "1122", String, core_tests ) ; -- from lib:extcore! @@ -116,9 +116,9 @@ equalityTest ( stripWhiteSpace ("asdf qwer \n asdf \t asdf\n"), equalityTest ( stripExtraWhiteSpace (" asdf qwer \n asdf \t asdf\n aa"), "asdf qwer asdf asdf aa", String, core_tests) ; -equalityTest ( replaceChars ( ".", "_", "sdf..sd_"), "sdf__sd_", String, core_tests) ; +equalityTest ( substitute ( ".", "_", "sdf..sd_"), "sdf__sd_", String, core_tests) ; -equalityTest ( replaceChars ( ".", "_", ".1.2.3."), "_1_2_3_", String, core_tests) ; +equalityTest ( substitute ( ".", "_", ".1.2.3."), "_1_2_3_", String, core_tests) ; diff --git a/test/stdlib/cmdargs/TestCmdArgs.sv b/test/stdlib/cmdargs/TestCmdArgs.sv index 9e1c45d4d..8a9aed012 100644 --- a/test/stdlib/cmdargs/TestCmdArgs.sv +++ b/test/stdlib/cmdargs/TestCmdArgs.sv @@ -1,7 +1,6 @@ import silver:util:cmdargs; import silver:testing ; -import lib:extcore ; import stdlib; synthesized attribute isVerbose :: Boolean occurs on CmdArgs; @@ -34,10 +33,10 @@ top::CmdArgs ::= rest::CmdArgs } -global flags1 :: [Pair] = - [pair("-verbose", flag(verboseFlag)), - pair("-silly", flag(sillyFlag)), - pair("-nosilly", flag(noSillyFlag))]; +global flags1 :: [FlagSpec] = + [flagSpec(name="-verbose", paramString=nothing(), help="", flagParser=flag(verboseFlag)), + flagSpec(name="-silly", paramString=nothing(), help="", flagParser=flag(sillyFlag)), + flagSpec(name="-nosilly", paramString=nothing(), help="", flagParser=flag(noSillyFlag))]; -- Don't parse anything unrecognized. equalityTest( interpretCmdArgs(flags1, ["a", "b", "c"]).cmdRemaining, @@ -84,8 +83,8 @@ equalityTest( interpretCmdArgs(flags1, ["-silly", "-nosilly", "-silly"]).isSilly -- an "option" is a flag with one parameter. -global flags2 :: [Pair] = - [pair("-I", option(includeFlag))] ++ flags1; +global flags2 :: [FlagSpec] = + [flagSpec(name="-I", paramString=nothing(), help="", flagParser=option(includeFlag))] ++ flags1; synthesized attribute includePaths :: [String] occurs on CmdArgs; diff --git a/test/stdlib/deque/TestDeque.sv b/test/stdlib/deque/TestDeque.sv index f7454a6a5..b320d8f3f 100644 --- a/test/stdlib/deque/TestDeque.sv +++ b/test/stdlib/deque/TestDeque.sv @@ -1,68 +1,67 @@ -import silver:util:deque; +import silver:util:deque as dq; import silver:testing ; -import lib:extcore ; import stdlib; -equalityTest ( dqIsEmpty(dqEmpty()), true, Boolean, core_tests ) ; +equalityTest ( dq:isEmpty(dq:empty()), true, Boolean, core_tests ) ; -global dq1 :: Deque = dqCons(1, dqEmpty()); -- 1 +global dq1 :: dq:Deque = dq:cons(1, dq:empty()); -- 1 -equalityTest ( dqIsEmpty(dq1), false, Boolean, core_tests ) ; +equalityTest ( dq:isEmpty(dq1), false, Boolean, core_tests ) ; -- head == last -equalityTest ( dqHead(dq1), 1, Integer, core_tests ) ; -equalityTest ( dqLast(dq1), 1, Integer, core_tests ) ; +equalityTest ( dq:head(dq1), 1, Integer, core_tests ) ; +equalityTest ( dq:last(dq1), 1, Integer, core_tests ) ; -- tail/init empty. -equalityTest ( dqIsEmpty(dqTail(dq1)), true, Boolean, core_tests ) ; -equalityTest ( dqIsEmpty(dqInit(dq1)), true, Boolean, core_tests ) ; +equalityTest ( dq:isEmpty(dq:tail(dq1)), true, Boolean, core_tests ) ; +equalityTest ( dq:isEmpty(dq:init(dq1)), true, Boolean, core_tests ) ; -- test cons -global dq2 :: Deque = dqCons(2, dq1); -- 2 1 +global dq2 :: dq:Deque = dq:cons(2, dq1); -- 2 1 -equalityTest ( dqHead(dq2), 2, Integer, core_tests ) ; -equalityTest ( dqLast(dq2), 1, Integer, core_tests ) ; +equalityTest ( dq:head(dq2), 2, Integer, core_tests ) ; +equalityTest ( dq:last(dq2), 1, Integer, core_tests ) ; -equalityTest ( dqHead(dqTail(dq2)), 1, Integer, core_tests ) ; -equalityTest ( dqLast(dqTail(dq2)), 1, Integer, core_tests ) ; +equalityTest ( dq:head(dq:tail(dq2)), 1, Integer, core_tests ) ; +equalityTest ( dq:last(dq:tail(dq2)), 1, Integer, core_tests ) ; -equalityTest ( dqHead(dqInit(dq2)), 2, Integer, core_tests ) ; -equalityTest ( dqLast(dqInit(dq2)), 2, Integer, core_tests ) ; +equalityTest ( dq:head(dq:init(dq2)), 2, Integer, core_tests ) ; +equalityTest ( dq:last(dq:init(dq2)), 2, Integer, core_tests ) ; -- test snoc -global dq3 :: Deque = dqSnoc(dq2, 3); -- 2 1 3 +global dq3 :: dq:Deque = dq:snoc(dq2, 3); -- 2 1 3 -equalityTest ( dqHead(dq3), 2, Integer, core_tests ) ; -equalityTest ( dqLast(dq3), 3, Integer, core_tests ) ; +equalityTest ( dq:head(dq3), 2, Integer, core_tests ) ; +equalityTest ( dq:last(dq3), 3, Integer, core_tests ) ; -equalityTest ( dqHead(dqTail(dq3)), 1, Integer, core_tests ) ; -equalityTest ( dqLast(dqTail(dq3)), 3, Integer, core_tests ) ; +equalityTest ( dq:head(dq:tail(dq3)), 1, Integer, core_tests ) ; +equalityTest ( dq:last(dq:tail(dq3)), 3, Integer, core_tests ) ; -equalityTest ( dqHead(dqInit(dq3)), 2, Integer, core_tests ) ; -equalityTest ( dqLast(dqInit(dq3)), 1, Integer, core_tests ) ; +equalityTest ( dq:head(dq:init(dq3)), 2, Integer, core_tests ) ; +equalityTest ( dq:last(dq:init(dq3)), 1, Integer, core_tests ) ; -- cons enough to rotate -global dq4 :: Deque = dqCons(12, dqCons(11, dqCons(10, dqCons(9, dqCons(8, dqCons(7, dqCons(6, dqCons(5, dqCons(4, dq3))))))))); +global dq4 :: dq:Deque = dq:cons(12, dq:cons(11, dq:cons(10, dq:cons(9, dq:cons(8, dq:cons(7, dq:cons(6, dq:cons(5, dq:cons(4, dq3))))))))); -equalityTest ( dqHead(dq4), 12, Integer, core_tests ) ; -equalityTest ( dqLast(dq4), 3, Integer, core_tests ) ; +equalityTest ( dq:head(dq4), 12, Integer, core_tests ) ; +equalityTest ( dq:last(dq4), 3, Integer, core_tests ) ; -- init enough to rotate -global dq5 :: Deque = dqInit(dqInit(dqInit(dqInit(dqInit(dqInit(dq4)))))); +global dq5 :: dq:Deque = dq:init(dq:init(dq:init(dq:init(dq:init(dq:init(dq4)))))); -equalityTest ( dqHead(dq5), 12, Integer, core_tests ) ; -equalityTest ( dqLast(dq5), 7, Integer, core_tests ) ; +equalityTest ( dq:head(dq5), 12, Integer, core_tests ) ; +equalityTest ( dq:last(dq5), 7, Integer, core_tests ) ; -- tail enough to rotate -global dq6 :: Deque = dqTail(dqTail(dqTail(dqTail(dqTail(dqTail(dq4)))))); +global dq6 :: dq:Deque = dq:tail(dq:tail(dq:tail(dq:tail(dq:tail(dq:tail(dq4)))))); -equalityTest ( dqHead(dq6), 6, Integer, core_tests ) ; -equalityTest ( dqLast(dq6), 3, Integer, core_tests ) ; +equalityTest ( dq:head(dq6), 6, Integer, core_tests ) ; +equalityTest ( dq:last(dq6), 3, Integer, core_tests ) ; -global dq7 :: Deque = dqTail(dqTail(dqTail(dqTail(dqTail(dqTail(dq5)))))); +global dq7 :: dq:Deque = dq:tail(dq:tail(dq:tail(dq:tail(dq:tail(dq:tail(dq5)))))); -equalityTest ( dqIsEmpty(dq7), true, Boolean, core_tests ) ; +equalityTest ( dq:isEmpty(dq7), true, Boolean, core_tests ) ; diff --git a/test/stdlib/fixedmap/FixedMap.sv b/test/stdlib/fixedmap/FixedMap.sv deleted file mode 100644 index 34225c908..000000000 --- a/test/stdlib/fixedmap/FixedMap.sv +++ /dev/null @@ -1,49 +0,0 @@ - - -import silver:testing; -import lib:extcore; -import silver:util:fixedmap as fm; -import stdlib; - -global t2 :: fm:Map = - fm:create([pair("g", 1), pair("f", 2), pair("d", 3), pair("s", 4), pair("a", 5), pair("p", 6), pair("q", 1), pair("h", 2), pair("i", 0), pair("q", 7), pair("1", 1), pair("2", 2), pair("3", 3), pair("4", 4), pair("5", 5), pair("6", 6), pair("1", 1), pair("2", 2), pair("0", 0), pair("7", 7)]); - - -equalityTest ( fm:lookup("1", t2), [1,1], [Integer], core_tests ) ; -equalityTest ( fm:lookup("2", t2), [2,2], [Integer], core_tests ) ; -equalityTest ( fm:lookup("3", t2), [3], [Integer], core_tests ) ; -equalityTest ( fm:lookup("4", t2), [4], [Integer], core_tests ) ; -equalityTest ( fm:lookup("5", t2), [5], [Integer], core_tests ) ; -equalityTest ( fm:lookup("6", t2), [6], [Integer], core_tests ) ; -equalityTest ( fm:lookup("7", t2), [7], [Integer], core_tests ) ; -equalityTest ( fm:lookup("8", t2), [], [Integer], core_tests ) ; -equalityTest ( fm:lookup("0", t2), [0], [Integer], core_tests ) ; - -equalityTest ( fm:lookup("g", t2), [1], [Integer], core_tests ) ; -equalityTest ( fm:lookup("h", t2), [2], [Integer], core_tests ) ; -equalityTest ( fm:lookup("j", t2), [], [Integer], core_tests ) ; -equalityTest ( fm:lookup("k", t2), [], [Integer], core_tests ) ; -equalityTest ( fm:lookup("i", t2), [0], [Integer], core_tests ) ; -equalityTest ( fm:lookup("f", t2), [2], [Integer], core_tests ) ; -equalityTest ( fm:lookup("a", t2), [5], [Integer], core_tests ) ; - - -global l1 :: [Pair] = - [pair("hi", 2), pair("hello", 4), pair("hola", 6), pair("quepasa", 11)]; - -global l2 :: [Pair] = - fm:toList( fm:create( l1 ) ); - -equalityTest ( head(l2).fst, "hello", String, core_tests ) ; -equalityTest ( head(tail(l2)).fst, "hi", String, core_tests ) ; -equalityTest ( head(tail(tail(l2))).fst, "hola", String, core_tests ) ; -equalityTest ( head(tail(tail(tail(l2)))).fst, "quepasa", String, core_tests ) ; - -equalityTest ( head(l2).snd, 4, Integer, core_tests ) ; -equalityTest ( head(tail(l2)).snd, 2, Integer, core_tests ) ; -equalityTest ( head(tail(tail(l2))).snd, 6, Integer, core_tests ) ; -equalityTest ( head(tail(tail(tail(l2)))).snd, 11, Integer, core_tests ) ; - -equalityTest ( null(tail(tail(tail(tail(l2))))), true, Boolean, core_tests ) ; - - diff --git a/test/stdlib/rawgraph/TestGraph.sv b/test/stdlib/graph/TestGraph.sv similarity index 79% rename from test/stdlib/rawgraph/TestGraph.sv rename to test/stdlib/graph/TestGraph.sv index b5e89448c..938f89a28 100644 --- a/test/stdlib/rawgraph/TestGraph.sv +++ b/test/stdlib/graph/TestGraph.sv @@ -1,22 +1,16 @@ import silver:testing; -import lib:extcore; -import silver:util:raw:graph as g; -import silver:util:raw:treeset as set; +import silver:util:graph as g; +import silver:util:treeset as set; import stdlib; -function compareInteger -Integer ::= a::Integer b::Integer -{ - return a - b; -} function iset set:Set ::= l::[Integer] { - return set:add(l, set:empty(compareInteger)); + return set:add(l, set:empty()); } -global e :: g:Graph = g:empty(compareInteger); +global e :: g:Graph = g:empty(); global g1 :: g:Graph = g:add([ @@ -49,10 +43,10 @@ equalityTest ( g:contains(pair(3,2), g1), false, Boolean, core_tests ) ; equalityTest ( g:contains(pair(99,87), g1), false, Boolean, core_tests ) ; -- set equality on edges from... -equalityTest ( set:equals(g:edgesFrom(1, g1), iset([1,2])), true, Boolean, core_tests ) ; -equalityTest ( set:equals(g:edgesFrom(1, g2), iset([1,2])), true, Boolean, core_tests ) ; -equalityTest ( set:equals(g:edgesFrom(6, g1), iset([])), true, Boolean, core_tests ) ; -equalityTest ( set:equals(g:edgesFrom(56, g1), iset([])), true, Boolean, core_tests ) ; +equalityTest ( g:edgesFrom(1, g1) == iset([1,2]), true, Boolean, core_tests ) ; +equalityTest ( g:edgesFrom(1, g2) == iset([1,2]), true, Boolean, core_tests ) ; +equalityTest ( g:edgesFrom(6, g1) == iset([]), true, Boolean, core_tests ) ; +equalityTest ( g:edgesFrom(56, g1) == iset([]), true, Boolean, core_tests ) ; -- test length as a proxy for correctness... equalityTest ( length(g:toList(g1)), 5, Integer, core_tests ) ; diff --git a/test/stdlib/pplib/PrettyTests.sv b/test/stdlib/pplib/PrettyTests.sv index 1ecd15bed..61fd657cb 100644 --- a/test/stdlib/pplib/PrettyTests.sv +++ b/test/stdlib/pplib/PrettyTests.sv @@ -1,9 +1,8 @@ import silver:testing; -import lib:extcore; import silver:langutil:pp; import stdlib; -import silver:util:deque; +import silver:util:deque as dq; -- should always be hello equalityTest ( show(20, text("hello")), "hello", String, core_tests ) ; @@ -142,4 +141,14 @@ equalityTest ( show(0, ppImplode(text(", "), doclist1)), "a, b, c", String, core equalityTest ( show(0, terminate(text(";"), doclist1)), "a;b;c;", String, core_tests ); equalityTest ( show(0, initiate(text(";"), doclist1)), ";a;b;c", String, core_tests ); +-- show things besides Document types +equalityTest ( show(0, [("abcd", 42, 3.14), ("hello", 123, 6.28)]), "[(\"abcd\", 42, 3.14), (\"hello\", 123, 6.28)]", String, core_tests ); +equalityTest ( show(0, [left(true), right(just(())), right(nothing())]), "[left(true), right(just(())), right(nothing())]", String, core_tests ); +-- templates +equalityTest ( pp"abc${123} ${just(3.14)}", cat(cat(text("abc"), text("123")), cat(text(" "), cat(cat(text("just("), text("3.14")), text(")")))), Document, core_tests ); +equalityTest ( pp"abc\ndef", cat(cat(text("abc"), realLine()), text("def")), Document, core_tests ); +equalityTest ( pp"""abc +def""", cat(cat(text("abc"), realLine()), text("def")), Document, core_tests ); + +equalityTest ( show(0, pp"abc${123} ${just(3.14)}"), "abc123 just(3.14)", String, core_tests ); diff --git a/test/stdlib/rawtreeset/SetTests.sv b/test/stdlib/rawtreeset/SetTests.sv deleted file mode 100644 index 3370fbfa2..000000000 --- a/test/stdlib/rawtreeset/SetTests.sv +++ /dev/null @@ -1,43 +0,0 @@ - -import silver:testing; -import lib:extcore; -import silver:util:raw:treeset as ts; -import stdlib; - -function integerCompare -Integer ::= l::Integer r::Integer -{ - return l - r; -} - -equalityTest ( ts:toList(ts:empty(integerCompare)), [], [Integer], core_tests ) ; - -global set1 :: ts:Set = ts:add([8,2,4,1,1,0], ts:empty(integerCompare)); - -equalityTest ( ts:toList(set1), [0,1,2,4,8], [Integer], core_tests ) ; - -global set2 :: ts:Set = ts:add([-1, 4, 7, 11, 4], ts:empty(integerCompare)); - -equalityTest ( ts:toList(set2), [-1,4,7,11], [Integer], core_tests ) ; - -equalityTest ( ts:toList(ts:union(set1,set2)), [-1,0,1,2,4,7,8,11], [Integer], core_tests ) ; - -equalityTest ( ts:toList(ts:intersect(set1,set2)), [4], [Integer], core_tests ) ; - -equalityTest ( ts:toList(ts:difference(set1,set2)), [0,1,2,8], [Integer], core_tests ) ; -equalityTest ( ts:toList(ts:difference(set2,set1)), [-1,7,11], [Integer], core_tests ) ; - -equalityTest ( ts:contains(4, set2), true, Boolean, core_tests ) ; -equalityTest ( ts:contains(5, set2), false, Boolean, core_tests ) ; - -equalityTest ( ts:containsAll([4,11], set2), true, Boolean, core_tests ) ; -equalityTest ( ts:containsAll([4,5], set2), false, Boolean, core_tests ) ; - -equalityTest ( ts:subset(set1, set2), false, Boolean, core_tests ) ; -equalityTest ( ts:subset(set1, ts:union(set1,set2)), true, Boolean, core_tests ) ; -equalityTest ( ts:subset(set2, ts:union(set1,set2)), true, Boolean, core_tests ) ; - -equalityTest ( ts:equals(set1, set2), false, Boolean, core_tests ) ; -equalityTest ( ts:equals(set1, ts:union(set1,set2)), false, Boolean, core_tests ) ; -equalityTest ( ts:equals(set1, set1), true, Boolean, core_tests ) ; - diff --git a/test/stdlib/silver-compile b/test/stdlib/silver-compile index 3766e8231..e8fdaf86f 100755 --- a/test/stdlib/silver-compile +++ b/test/stdlib/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/test/stdlib/rawtreemap/TestTreeMap.sv b/test/stdlib/treemap/TestTreeMap.sv similarity index 96% rename from test/stdlib/rawtreemap/TestTreeMap.sv rename to test/stdlib/treemap/TestTreeMap.sv index 60c7a69ef..1adb29b79 100644 --- a/test/stdlib/rawtreemap/TestTreeMap.sv +++ b/test/stdlib/treemap/TestTreeMap.sv @@ -1,11 +1,10 @@ import silver:testing; -import lib:extcore; -import silver:util:raw:treemap as tm; +import silver:util:treemap as tm; import stdlib; -global e :: tm:Map = tm:empty(compareString); +global e :: tm:Map = tm:empty(); global t1 :: tm:Map = tm:add([pair("1", 1), pair("2", 2), pair("3", 3), pair("4", 4), pair("5", 5), pair("6", 6), pair("1", 1), pair("2", 2), pair("0", 0), pair("7", 7)], e); diff --git a/test/stdlib/treemap/TreeMap.sv b/test/stdlib/treemap/TreeMap.sv deleted file mode 100644 index 20f12fe55..000000000 --- a/test/stdlib/treemap/TreeMap.sv +++ /dev/null @@ -1,152 +0,0 @@ - - -import silver:testing; -import lib:extcore; -import silver:util:treemap; -import stdlib; - -global e :: TreeMap = treeNew(compareString); - -global t1 :: TreeMap = - treeInsert("1", 1, - treeInsert("2", 2, - treeInsert("3", 3, - treeInsert("4", 4, - treeInsert("5", 5, - treeInsert("6", 6, - treeInsert("1", 1, - treeInsert("2", 2, - treeInsert("0", 0, - treeInsert("7", 7, e)))))))))); - -global t2 :: TreeMap = - treeInsert("g", 1, - treeInsert("f", 2, - treeInsert("d", 3, - treeInsert("s", 4, - treeInsert("a", 5, - treeInsert("p", 6, - treeInsert("q", 1, - treeInsert("h", 2, - treeInsert("i", 0, - treeInsert("q", 7, t1)))))))))); - -equalityTest ( t1.blackHeight == 3, true, Boolean, core_tests ) ; -equalityTest ( t2.blackHeight == 3, true, Boolean, core_tests ) ; -equalityTest ( t1.debugHeight == 4, true, Boolean, core_tests ) ; -equalityTest ( t2.debugHeight == 6, true, Boolean, core_tests ) ; - -equalityTest ( treeLookup("1", t2), [1,1], [Integer], core_tests ) ; -equalityTest ( treeLookup("2", t2), [2,2], [Integer], core_tests ) ; -equalityTest ( treeLookup("3", t2), [3], [Integer], core_tests ) ; -equalityTest ( treeLookup("4", t2), [4], [Integer], core_tests ) ; -equalityTest ( treeLookup("5", t2), [5], [Integer], core_tests ) ; -equalityTest ( treeLookup("6", t2), [6], [Integer], core_tests ) ; -equalityTest ( treeLookup("7", t2), [7], [Integer], core_tests ) ; -equalityTest ( treeLookup("8", t2), [], [Integer], core_tests ) ; -equalityTest ( treeLookup("0", t2), [0], [Integer], core_tests ) ; - -equalityTest ( treeLookup("g", t2), [1], [Integer], core_tests ) ; -equalityTest ( treeLookup("h", t2), [2], [Integer], core_tests ) ; -equalityTest ( treeLookup("j", t2), [], [Integer], core_tests ) ; -equalityTest ( treeLookup("k", t2), [], [Integer], core_tests ) ; -equalityTest ( treeLookup("i", t2), [0], [Integer], core_tests ) ; -equalityTest ( treeLookup("f", t2), [2], [Integer], core_tests ) ; -equalityTest ( treeLookup("a", t2), [5], [Integer], core_tests ) ; - -function genTree -TreeMap ::= i::Integer t::TreeMap -{ - return if i > 0 then genTree(i-1, treeInsert(toString(i), i, t)) - else t; -} - -global t3 :: TreeMap = genTree(50, e); -equalityTest ( t3.blackHeight * 2 >= t3.debugHeight, true, Boolean, core_tests ) ; -equalityTest ( 6 <= t3.debugHeight, true, Boolean, core_tests ) ; - -global t4 :: TreeMap = genTree(500, e); -equalityTest ( t4.blackHeight * 2 >= t4.debugHeight, true, Boolean, core_tests ) ; -equalityTest ( 9 <= t4.debugHeight, true, Boolean, core_tests ) ; - -global t5 :: TreeMap = genTree(5000, e); -equalityTest ( t4.blackHeight * 2 >= t4.debugHeight, true, Boolean, core_tests ) ; -equalityTest ( 12 <= t4.debugHeight, true, Boolean, core_tests ) ; - - -global l1 :: [Pair] = - [pair("hi", 2), pair("hello", 4), pair("hola", 6), pair("quepasa", 11)]; - -global t6 :: TreeMap = treeConvert(l1, t2); -equalityTest ( treeLookup("1", t6), [1,1], [Integer], core_tests ) ; -equalityTest ( treeLookup("5", t6), [5], [Integer], core_tests ) ; -equalityTest ( treeLookup("4", t6), [4], [Integer], core_tests ) ; -equalityTest ( treeLookup("h", t6), [2], [Integer], core_tests ) ; -equalityTest ( treeLookup("8", t6), [], [Integer], core_tests ) ; -equalityTest ( treeLookup("j", t6), [], [Integer], core_tests ) ; -equalityTest ( treeLookup("i", t6), [0], [Integer], core_tests ) ; -equalityTest ( treeLookup("f", t6), [2], [Integer], core_tests ) ; -equalityTest ( treeLookup("a", t6), [5], [Integer], core_tests ) ; -equalityTest ( treeLookup("hi", t6), [2], [Integer], core_tests ) ; -equalityTest ( treeLookup("hello", t6), [4], [Integer], core_tests ) ; -equalityTest ( treeLookup("hola", t6), [6], [Integer], core_tests ) ; -equalityTest ( treeLookup("quepasa", t6), [11], [Integer], core_tests ) ; - -global l2 :: [Pair] = - treeDeconvert( treeConvert( l1, e ) ); - -equalityTest ( head(l2).fst, "hello", String, core_tests ) ; -equalityTest ( head(tail(l2)).fst, "hi", String, core_tests ) ; -equalityTest ( head(tail(tail(l2))).fst, "hola", String, core_tests ) ; -equalityTest ( head(tail(tail(tail(l2)))).fst, "quepasa", String, core_tests ) ; - -equalityTest ( head(l2).snd, 4, Integer, core_tests ) ; -equalityTest ( head(tail(l2)).snd, 2, Integer, core_tests ) ; -equalityTest ( head(tail(tail(l2))).snd, 6, Integer, core_tests ) ; -equalityTest ( head(tail(tail(tail(l2)))).snd, 11, Integer, core_tests ) ; - -equalityTest ( null(tail(tail(tail(tail(l2))))), true, Boolean, core_tests ) ; - --- Extra testing stuff -synthesized attribute debugIdentity :: String occurs on TreeMap; -synthesized attribute debugDot :: String occurs on TreeMap; -synthesized attribute debugHeight :: Integer occurs on TreeMap; -synthesized attribute blackHeight :: Integer occurs on TreeMap; - -function escape -String ::= s::String -{ - return implode("\\\"", explode("\"", s)); -} - -aspect production leaf -top::TreeMap ::= CMP :: (Integer ::= a a) -{ - top.debugIdentity = "leaf" ++ toString(genInt()); - top.debugDot = top.debugIdentity ++ "[color=black, style=filled];\n"; - top.debugHeight = 0; - top.blackHeight = 0; -} - -aspect production node -top::TreeMap ::= black::Boolean lefttree::TreeMap righttree::TreeMap - label::a values::[b] - CMP :: (Integer ::= a a) -{ - top.debugIdentity = "node" ++ toString(genInt()); - top.debugDot = top.debugIdentity ++ "[color=" ++ (if black then "black" else "red") ++ ", label=\"" ++ escape(hackUnparse(label) ++ " -> " ++ hackUnparse(values)) ++ "\"];\n" - ++ top.debugIdentity ++ " -> " ++ lefttree.debugIdentity ++ " [label=\"l\"];\n" - ++ top.debugIdentity ++ " -> " ++ righttree.debugIdentity ++ " [label=\"r\"];\n" - ++ lefttree.debugDot ++ righttree.debugDot; - top.debugHeight = if lefttree.debugHeight > righttree.debugHeight - then lefttree.debugHeight + 1 - else righttree.debugHeight + 1; - top.blackHeight = if lefttree.blackHeight != righttree.blackHeight - then -9999999 - else righttree.blackHeight + if black then 1 else 0; -} - --- Write out a DOT file of the tree, for visual inspection if desired. -equalityTest ( unsafeTrace("", writeFile("rbtree.dotty.test.output", "digraph test {\n" ++ t6.debugDot ++ "}", unsafeIO())), "", String, core_tests ) ; - - diff --git a/test/stdlib/treeset/SetTests.sv b/test/stdlib/treeset/SetTests.sv new file mode 100644 index 000000000..45aebd1bd --- /dev/null +++ b/test/stdlib/treeset/SetTests.sv @@ -0,0 +1,36 @@ + +import silver:testing; +import silver:util:treeset as ts; +import stdlib; + +equalityTest ( ts:toList(ts:empty()), [], [Integer], core_tests ) ; + +global set1 :: ts:Set = ts:add([8,2,4,1,1,0], ts:empty()); + +equalityTest ( ts:toList(set1), [0,1,2,4,8], [Integer], core_tests ) ; + +global set2 :: ts:Set = ts:add([-1, 4, 7, 11, 4], ts:empty()); + +equalityTest ( ts:toList(set2), [-1,4,7,11], [Integer], core_tests ) ; + +equalityTest ( ts:toList(ts:union(set1,set2)), [-1,0,1,2,4,7,8,11], [Integer], core_tests ) ; + +equalityTest ( ts:toList(ts:intersect(set1,set2)), [4], [Integer], core_tests ) ; + +equalityTest ( ts:toList(ts:difference(set1,set2)), [0,1,2,8], [Integer], core_tests ) ; +equalityTest ( ts:toList(ts:difference(set2,set1)), [-1,7,11], [Integer], core_tests ) ; + +equalityTest ( ts:contains(4, set2), true, Boolean, core_tests ) ; +equalityTest ( ts:contains(5, set2), false, Boolean, core_tests ) ; + +equalityTest ( ts:containsAll([4,11], set2), true, Boolean, core_tests ) ; +equalityTest ( ts:containsAll([4,5], set2), false, Boolean, core_tests ) ; + +equalityTest ( ts:subset(set1, set2), false, Boolean, core_tests ) ; +equalityTest ( ts:subset(set1, ts:union(set1,set2)), true, Boolean, core_tests ) ; +equalityTest ( ts:subset(set2, ts:union(set1,set2)), true, Boolean, core_tests ) ; + +equalityTest ( set1 == set2, false, Boolean, core_tests ) ; +equalityTest ( set1 == ts:union(set1,set2), false, Boolean, core_tests ) ; +equalityTest ( set1, set1, ts:Set, core_tests ) ; + diff --git a/test/stdlib/xml/XmlTests.sv b/test/stdlib/xml/XmlTests.sv index 9b36853f8..dd016e0fd 100644 --- a/test/stdlib/xml/XmlTests.sv +++ b/test/stdlib/xml/XmlTests.sv @@ -1,8 +1,7 @@ import silver:testing; -import lib:extcore; -import lib:xml; -import lib:xml:foreigntypes; +import silver:xml; +import silver:xml:foreigntypes; import stdlib; global books :: XML_Document = parseXMLFileF("xml/books.xml").parseTree; diff --git a/tutorials/dc/AbstractSyntax.sv b/tutorials/dc/AbstractSyntax.sv index 95e9fa575..02c835179 100644 --- a/tutorials/dc/AbstractSyntax.sv +++ b/tutorials/dc/AbstractSyntax.sv @@ -28,28 +28,28 @@ r::Root ::= e::Expr nonterminal Expr with pp, value; -abstract production add +abstract production addOp sum::Expr ::= l::Expr r::Expr { sum.pp = "(" ++ l.pp ++ " + " ++ r.pp ++ ")"; sum.value = l.value + r.value ; } -abstract production sub +abstract production subOp dff::Expr ::= l::Expr r::Expr { dff.pp = "(" ++ l.pp ++ " - " ++ r.pp ++ ")"; dff.value = l.value - r.value ; } -abstract production mul +abstract production mulOp prd::Expr ::= l::Expr r::Expr { prd.pp = "(" ++ l.pp ++ " * " ++ r.pp ++ ")"; prd.value = l.value * r.value ; } -abstract production div +abstract production divOp quo::Expr ::= l::Expr r::Expr { quo.pp = "(" ++ l.pp ++ " / " ++ r.pp ++ ")"; diff --git a/tutorials/dc/BetterPP.sv b/tutorials/dc/BetterPP.sv index dc1c0d50e..218718e60 100644 --- a/tutorials/dc/BetterPP.sv +++ b/tutorials/dc/BetterPP.sv @@ -45,7 +45,7 @@ r::Root ::= e::Expr wrapping without considering associativity -} } -aspect production add +aspect production addOp sum::Expr ::= l::Expr r::Expr { sum.bpp = if wrapInParens ( sum.enclosingOpPrecedence, 1, @@ -59,7 +59,7 @@ sum::Expr ::= l::Expr r::Expr r.leftOrRight = "right" ; } -aspect production sub +aspect production subOp dff::Expr ::= l::Expr r::Expr { dff.bpp = if wrapInParens ( dff.enclosingOpPrecedence, 1, @@ -73,7 +73,7 @@ dff::Expr ::= l::Expr r::Expr r.leftOrRight = "right" ; } -aspect production mul +aspect production mulOp prd::Expr ::= l::Expr r::Expr { prd.bpp = if wrapInParens ( prd.enclosingOpPrecedence, 2, @@ -87,7 +87,7 @@ prd::Expr ::= l::Expr r::Expr r.leftOrRight = "right" ; } -aspect production div +aspect production divOp quo::Expr ::= l::Expr r::Expr { local attribute ourPrecedence :: Integer; diff --git a/tutorials/dc/ConcreteSyntax.sv b/tutorials/dc/ConcreteSyntax.sv index 33316ee1a..d46ea6378 100644 --- a/tutorials/dc/ConcreteSyntax.sv +++ b/tutorials/dc/ConcreteSyntax.sv @@ -35,14 +35,14 @@ concrete production add_c sum::Expr_c ::= e::Expr_c '+' t::Term_c { sum.pp = e.pp ++ " + " ++ t.pp ; - sum.ast_Expr = add(e.ast_Expr, t.ast_Expr ); + sum.ast_Expr = addOp(e.ast_Expr, t.ast_Expr ); } concrete production sub_c dff::Expr_c ::= e::Expr_c '-' t::Term_c { dff.pp = e.pp ++ " - " ++ t.pp ; - dff.ast_Expr = sub(e.ast_Expr, t.ast_Expr); + dff.ast_Expr = subOp(e.ast_Expr, t.ast_Expr); } concrete production exprTerm_c @@ -56,14 +56,14 @@ concrete production mul_c prd::Term_c ::= t::Term_c '*' f::Factor_c { prd.pp = t.pp ++ " * " ++ f.pp ; - prd.ast_Expr = mul(t.ast_Expr, f.ast_Expr); + prd.ast_Expr = mulOp(t.ast_Expr, f.ast_Expr); } concrete production div_c d::Term_c ::= t::Term_c '/' f::Factor_c { d.pp = t.pp ++ " / " ++ f.pp ; - d.ast_Expr = div(t.ast_Expr, f.ast_Expr); + d.ast_Expr = divOp(t.ast_Expr, f.ast_Expr); } concrete production termFactor_c diff --git a/tutorials/dc/Main.sv b/tutorials/dc/Main.sv index 28d00e095..cb2084c83 100644 --- a/tutorials/dc/Main.sv +++ b/tutorials/dc/Main.sv @@ -20,7 +20,7 @@ parser parse :: Root_c - world' and each value used only once. -} function main -IOVal ::= largs::[String] ioin::IO +IO ::= largs::[String] { local attribute args :: String; args = implode(" ", largs); @@ -34,7 +34,7 @@ IOVal ::= largs::[String] ioin::IO local attribute r_ast :: Root ; r_ast = r_cst.ast_Root ; - local attribute print_success :: IO; + local attribute print_success :: IO; print_success = print( "Command line expression: " ++ args ++ "\n\n" ++ @@ -45,15 +45,16 @@ IOVal ::= largs::[String] ioin::IO "AST better pretty print: " ++ r_ast.bpp ++ "\n\n" ++ "Value: " ++ toString(r_ast.value) ++ - "\n\n" - , ioin ); + "\n\n"); - local attribute print_failure :: IO; + local attribute print_failure :: IO; print_failure = - print("Encountered a parse error:\n" ++ result.parseErrors ++ "\n", ioin); + print("Encountered a parse error:\n" ++ result.parseErrors ++ "\n"); - return ioval(if result.parseSuccess then print_success else print_failure, - 0); + return do { + new(if result.parseSuccess then print_success else print_failure); + return 0; + }; } diff --git a/tutorials/dc/silver-compile b/tutorials/dc/silver-compile index 057e1633b..08b77cc84 100755 --- a/tutorials/dc/silver-compile +++ b/tutorials/dc/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/tutorials/hello/Hello.sv b/tutorials/hello/Hello.sv index cba2aee26..dc29080da 100644 --- a/tutorials/hello/Hello.sv +++ b/tutorials/hello/Hello.sv @@ -1,9 +1,10 @@ grammar hello; function main -IOVal ::= largs::[String] ioin::IO +IO ::= largs::[String] { - return ioval(print(" World!\n", - print("Hello", ioin)), - 0); + return do { + print("Hello, world!\n"); + return 0; + }; } diff --git a/tutorials/hello/silver-compile b/tutorials/hello/silver-compile index 5253b9563..515cc0846 100755 --- a/tutorials/hello/silver-compile +++ b/tutorials/hello/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/tutorials/lambda/Driver.sv b/tutorials/lambda/Driver.sv index 0575d9491..edbc04b25 100644 --- a/tutorials/lambda/Driver.sv +++ b/tutorials/lambda/Driver.sv @@ -6,17 +6,17 @@ imports silver:langutil:pp; function driver IOVal ::= args::[String] parse::(ParseResult ::= String String) - driverIO::IO + driverIO::IOToken { production filename::String = head(args) ; - local fileExists::IOVal = isFile(filename, driverIO); - local text::IOVal = readFile(filename,fileExists.io); + local fileExists::IOVal = isFileT(filename, driverIO); + local text::IOVal = readFileT(filename,fileExists.io); local result::ParseResult = parse(text.iovalue, filename); local r_cst::Root_c = result.parseTree ; production r_ast::Root = r_cst.ast ; - local print_failure::IO - = print("parse failed.\n" ++ result.parseErrors ++ "\n", text.io); + local print_failure::IOToken + = printT("parse failed.\n" ++ result.parseErrors ++ "\n", text.io); {- Display pp unless it is "turned on" by some aspect of the driver - production contributing the value 'true' to the collection @@ -43,10 +43,10 @@ IOVal ::= args::[String] return if null(args) - then ioval (print ("Command line arguments required, usage \"java -jar lambda.jar <>\"\n", driverIO), 1) + then ioval (printT ("Command line arguments required, usage \"java -jar lambda.jar <>\"\n", driverIO), 1) else if ! fileExists.iovalue - then ioval (print ("File \"" ++ filename ++ "\" not found.\n\n", + then ioval (printT ("File \"" ++ filename ++ "\" not found.\n\n", fileExists.io ) , 1 ) else if ! result.parseSuccess @@ -55,28 +55,28 @@ IOVal ::= args::[String] } nonterminal Task with tioIn, tioOut ; -inherited attribute tioIn :: IO ; -synthesized attribute tioOut :: IO ; +inherited attribute tioIn :: IOToken ; +synthesized attribute tioOut :: IOToken ; abstract production printPPTask t::Task ::= filename::String r_cst::Decorated Root_c -{ t.tioOut = print("Pretty print of program in \"" ++ filename ++ "\":\n" ++ +{ t.tioOut = printT("Pretty print of program in \"" ++ filename ++ "\":\n" ++ show(80,r_cst.pp) ++ "\n\n" ++ "CST:\n" ++ show(80,r_cst.ast.pp) ++ "\n\n", t.tioIn) ; } abstract production writePPTask t::Task ::= filename::String r_ast::Decorated Root -{ t.tioOut = writeFile(filenamePP, show(80,r_ast.pp), t.tioIn) ; +{ t.tioOut = writeFileT(filenamePP, show(80,r_ast.pp), t.tioIn) ; local filenamePP::String = substring(0, length(filename)-7, filename) ++ "_pp.lambda" ; } abstract production printErrorsTask t::Task ::= filename::String r_ast::Decorated Root -{ t.tioOut = print("Errors of program in \"" ++ filename ++ "\":\n" ++ +{ t.tioOut = printT("Errors of program in \"" ++ filename ++ "\":\n" ++ r_ast.errors ++ "\n\n", t.tioIn ) ; } abstract production writeErrorsTask t::Task ::= filename::String r_ast::Decorated Root -{ t.tioOut = writeFile(filenameErrors, +{ t.tioOut = writeFileT(filenameErrors, r_ast.errors ++ "\n\n", t.tioIn) ; local filenameErrors::String = substring(0, length(filename)-3, filename) ++ ".errors" ; diff --git a/tutorials/lambda/Main.sv b/tutorials/lambda/Main.sv index 67b06396c..66644f506 100644 --- a/tutorials/lambda/Main.sv +++ b/tutorials/lambda/Main.sv @@ -24,7 +24,7 @@ parser hostParse :: Root_c { } function main -IOVal ::= largs::[String] io_in::IO +IOVal ::= largs::[String] io_in::IOToken { return driver(largs, hostParse, io_in) ; } diff --git a/tutorials/lambda/silver-compile b/tutorials/lambda/silver-compile index fb2674b0c..312fafbb5 100755 --- a/tutorials/lambda/silver-compile +++ b/tutorials/lambda/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/tutorials/simple/arb_host/build.test b/tutorials/simple/arb_host/build.test new file mode 100644 index 000000000..15937bbb9 --- /dev/null +++ b/tutorials/simple/arb_host/build.test @@ -0,0 +1 @@ +run: ./silver-compile --clean diff --git a/tutorials/simple/arb_host/run_1.test b/tutorials/simple/arb_host/run_1.test new file mode 100644 index 000000000..12c0cff62 --- /dev/null +++ b/tutorials/simple/arb_host/run_1.test @@ -0,0 +1 @@ +run: java -jar simple.arb.jar 1 diff --git a/tutorials/simple/arb_host/run_5.test b/tutorials/simple/arb_host/run_5.test new file mode 100644 index 000000000..1ac9f3c6f --- /dev/null +++ b/tutorials/simple/arb_host/run_5.test @@ -0,0 +1 @@ +run: java -jar simple.arb.jar 5 diff --git a/tutorials/simple/arb_host/silver-compile b/tutorials/simple/arb_host/silver-compile new file mode 100755 index 000000000..1839e08ad --- /dev/null +++ b/tutorials/simple/arb_host/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../../support/bin/silver" "$@"; } + +SRC=../src +GRAMMAR=simple:arb + +silver -I $SRC $@ $GRAMMAR + diff --git a/tutorials/simple/arb_with_all/build.test b/tutorials/simple/arb_with_all/build.test new file mode 100644 index 000000000..15937bbb9 --- /dev/null +++ b/tutorials/simple/arb_with_all/build.test @@ -0,0 +1 @@ +run: ./silver-compile --clean diff --git a/tutorials/simple/arb_with_all/run_1.test b/tutorials/simple/arb_with_all/run_1.test new file mode 100644 index 000000000..facb9eb4f --- /dev/null +++ b/tutorials/simple/arb_with_all/run_1.test @@ -0,0 +1 @@ +run: java -jar simple.composed.simple_arb_all.jar 1 diff --git a/tutorials/simple/arb_with_all/run_5.test b/tutorials/simple/arb_with_all/run_5.test new file mode 100644 index 000000000..084ebc07b --- /dev/null +++ b/tutorials/simple/arb_with_all/run_5.test @@ -0,0 +1 @@ +run: java -jar simple.composed.simple_arb_all.jar 5 diff --git a/tutorials/simple/arb_with_all/silver-compile b/tutorials/simple/arb_with_all/silver-compile new file mode 100755 index 000000000..722d557d9 --- /dev/null +++ b/tutorials/simple/arb_with_all/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../../support/bin/silver" "$@"; } + +SRC=../src +GRAMMAR=simple:composed:simple_arb_all + +silver -I $SRC $@ $GRAMMAR + diff --git a/tutorials/simple/host/silver-compile b/tutorials/simple/host/silver-compile index 500e2732a..83043a8d2 100755 --- a/tutorials/simple/host/silver-compile +++ b/tutorials/simple/host/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../../support/bin/silver" "$@"; } diff --git a/tutorials/simple/src/simple/abstractsyntax/C-trans.sv b/tutorials/simple/src/simple/abstractsyntax/C-trans.sv index 722cde9ac..62f86a9f8 100644 --- a/tutorials/simple/src/simple/abstractsyntax/C-trans.sv +++ b/tutorials/simple/src/simple/abstractsyntax/C-trans.sv @@ -50,6 +50,7 @@ s::Stmt ::= e::Expr | floatType() -> "%f" | booleanType() -> "%d" | stringType() -> "%s" + | errorType() -> "%d" end; s.c_code = s"printf(\"${print_code}\", ${e.c_code}); \n"; } @@ -120,24 +121,24 @@ Note that we do not define the c_code attribute for those relational and logical operations that (umm that WHAT? hahahaha) -} aspect production intLit -e::Expr ::= s::String +e::Expr ::= i::Integer { - e.c_code = s; + e.c_code = toString(i); } aspect production floatLit -e::Expr ::= s::String +e::Expr ::= f::Float { - e.c_code = s; + e.c_code = toString(f); } aspect production boolLit -e::Expr ::= s::String +e::Expr ::= b::Boolean { - e.c_code = s; + e.c_code = if b then "1" else "0"; } aspect production stringLit e::Expr ::= s::String { - e.c_code = s; + e.c_code = s"\"${escapeString(s)}\""; } aspect production varRef @@ -146,33 +147,33 @@ e::Expr ::= id::Name e.c_code = id.name; } -aspect production add +aspect production addOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} + ${r.c_code})"; } -aspect production sub +aspect production subOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} - ${r.c_code})"; } -aspect production mul +aspect production mulOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} * ${r.c_code})"; } -aspect production div +aspect production divOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} / ${r.c_code})"; } -aspect production eq +aspect production eqOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} == ${r.c_code})"; } -aspect production lt +aspect production ltOp e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} < ${r.c_code})"; @@ -183,7 +184,7 @@ e::Expr ::= l::Expr r::Expr { e.c_code = s"(${l.c_code} && ${r.c_code})"; } -aspect production not +aspect production notOp e::Expr ::= ne::Expr { e.c_code = s"(!${ne.c_code})"; diff --git a/tutorials/simple/src/simple/abstractsyntax/Decl.sv b/tutorials/simple/src/simple/abstractsyntax/Decl.sv index 19450a566..9eb97e00c 100644 --- a/tutorials/simple/src/simple/abstractsyntax/Decl.sv +++ b/tutorials/simple/src/simple/abstractsyntax/Decl.sv @@ -8,7 +8,7 @@ nonterminal Decl with pp, env, defs, errors; abstract production decl d::Decl ::= t::TypeExpr id::Name { - d.pp = pp"${t.pp} ${id.pp};"; + d.pp = pp"${t} ${id};"; d.defs = [valueDef(id.name, t)]; d.errors := []; } diff --git a/tutorials/simple/src/simple/abstractsyntax/Env.sv b/tutorials/simple/src/simple/abstractsyntax/Env.sv index 124bb1bda..50c564880 100644 --- a/tutorials/simple/src/simple/abstractsyntax/Env.sv +++ b/tutorials/simple/src/simple/abstractsyntax/Env.sv @@ -1,6 +1,6 @@ grammar simple:abstractsyntax; -import silver:util:raw:treemap as tm; +import silver:util:treemap as tm; {- This is slightly overkill for simple, however it's an ideal way to set up the environment for larger projects and more realistic languages. -} @@ -66,7 +66,7 @@ synthesized attribute valueContribs :: [Pair] occurs aspect production emptyEnv_i top::Env ::= { - top.values = tm:empty(compareString); + top.values = tm:empty(); } aspect production addEnv_i top::Env ::= dlist::[Def] e::Decorated Env diff --git a/tutorials/simple/src/simple/abstractsyntax/Expr.sv b/tutorials/simple/src/simple/abstractsyntax/Expr.sv index 77a0eb6d1..14d184edb 100644 --- a/tutorials/simple/src/simple/abstractsyntax/Expr.sv +++ b/tutorials/simple/src/simple/abstractsyntax/Expr.sv @@ -1,5 +1,7 @@ grammar simple:abstractsyntax; +imports silver:util:random; + {-- - Names (identifiers) are useful to abstract as nonterminals, because we - frequently want to do similar things to them: look them up in the @@ -19,35 +21,43 @@ n::Name ::= s::String n.lookup = lookupValue(s, n.env); } +instance Arbitrary Name { + genArb = \ depth::Integer -> do { + chars :: [Integer] <- randomShuffle(stringToChars("abcd")); + loc :: Location <- genArb(depth); + return name(charsToString(chars), location=loc); + }; +} + nonterminal Expr with pp, env, errors; -- Constants ------------ abstract production intLit -e::Expr ::= s::String +e::Expr ::= i::Integer { - e.pp = text(s); + e.pp = text(toString(i)); e.errors := []; } abstract production floatLit -e::Expr ::= s::String +e::Expr ::= f::Float { - e.pp = text(s); + e.pp = text(toString(f)); e.errors := []; } abstract production boolLit -e::Expr ::= s::String +e::Expr ::= b::Boolean { - e.pp = text(s); + e.pp = if b then pp"True" else pp"False"; e.errors := []; } abstract production stringLit e::Expr ::= s::String { - e.pp = text(s); + e.pp = pp"\"${text(escapeString(s))}\""; e.errors := []; } @@ -85,28 +95,28 @@ e::Expr ::= id::Name ------------------------ -- Only name declaration errors are computed here, thus we simply collect -- the errors attributes from the children. -abstract production add +abstract production addOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} + ${r.pp})"; + e.pp = pp"(${l} + ${r})"; e.errors := l.errors ++ r.errors; } -abstract production sub +abstract production subOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} - ${r.pp})"; + e.pp = pp"(${l} - ${r})"; e.errors := l.errors ++ r.errors; } -abstract production mul +abstract production mulOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} * ${r.pp})"; + e.pp = pp"(${l} * ${r})"; e.errors := l.errors ++ r.errors; } -abstract production div +abstract production divOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} / ${r.pp})"; + e.pp = pp"(${l} / ${r})"; e.errors := l.errors ++ r.errors; } @@ -127,46 +137,46 @@ e::Expr ::= l::Expr r::Expr lt. The rest (neq, lte, gt, gte) can be handled by forwarding. -} -abstract production eq +abstract production eqOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} == ${r.pp})"; + e.pp = pp"(${l} == ${r})"; e.errors := l.errors ++ r.errors; } -abstract production lt +abstract production ltOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} < ${r.pp})"; + e.pp = pp"(${l} < ${r})"; e.errors := l.errors ++ r.errors; } -abstract production neq +abstract production neqOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} != ${r.pp})"; - forwards to not (eq(l,r)); + e.pp = pp"(${l} != ${r})"; + forwards to notOp(eqOp(l,r)); -- e.errors is copied from the forwarded-to tree -- Similarly, type checking attributes defined TypeChecking.sv are -- automatically copied, as are other yet-to-be defined attributes. } -abstract production lte +abstract production lteOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} <= ${r.pp})"; - forwards to or( lt(l,r), eq(l,r) ); + e.pp = pp"(${l} <= ${r})"; + forwards to or( ltOp(l,r), eqOp(l,r) ); } -abstract production gt +abstract production gtOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} > ${r.pp})"; - forwards to not(lte(l,r)); + e.pp = pp"(${l} > ${r})"; + forwards to notOp(lteOp(l,r)); } -abstract production gte +abstract production gteOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} >= ${r.pp})"; - forwards to not(lt(l,r)); + e.pp = pp"(${l} >= ${r})"; + forwards to notOp(ltOp(l,r)); } @@ -175,13 +185,13 @@ e::Expr ::= l::Expr r::Expr abstract production and e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} && ${r.pp})"; + e.pp = pp"(${l} && ${r})"; e.errors := l.errors ++ r.errors; } -abstract production not +abstract production notOp e::Expr ::= ne::Expr { - e.pp = pp"!(${ne.pp})"; + e.pp = pp"!(${ne})"; e.errors := ne.errors; } @@ -189,8 +199,8 @@ e::Expr ::= ne::Expr abstract production or e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} || ${r.pp})"; - forwards to not( and(not(l), not(r)) ); + e.pp = pp"(${l} || ${r})"; + forwards to notOp( and(notOp(l), notOp(r)) ); } diff --git a/tutorials/simple/src/simple/abstractsyntax/For.sv b/tutorials/simple/src/simple/abstractsyntax/For.sv index 53c2b81ab..ca2ce034b 100644 --- a/tutorials/simple/src/simple/abstractsyntax/For.sv +++ b/tutorials/simple/src/simple/abstractsyntax/For.sv @@ -4,7 +4,7 @@ abstract production for s::Stmt ::= i::Name lower::Expr upper::Expr body::Stmt { -- provide a nicer pretty printing, but nothing else. - s.pp = pp"for(${i.pp} = ${lower.pp} to ${upper.pp})${ppblock(body)}"; + s.pp = pp"for(${i} = ${lower} to ${upper})${ppblock(body)}"; forwards to {- i = lower ; @@ -15,8 +15,8 @@ s::Stmt ::= i::Name lower::Expr upper::Expr body::Stmt -} seq( assignment(i, lower), - while(lte(varRef(i), upper), + while(lteOp(varRef(i), upper), block( seq(body, - assignment(i, add(varRef(i), intLit("1"))))))); + assignment(i, addOp(varRef(i), intLit(1))))))); } diff --git a/tutorials/simple/src/simple/abstractsyntax/Stmt.sv b/tutorials/simple/src/simple/abstractsyntax/Stmt.sv index 4d808039c..72d45ced5 100644 --- a/tutorials/simple/src/simple/abstractsyntax/Stmt.sv +++ b/tutorials/simple/src/simple/abstractsyntax/Stmt.sv @@ -4,10 +4,10 @@ nonterminal Stmt with pp, env, defs, errors; -- Decides how best to pretty print a statement following if/while/etc function ppblock -Document ::= s::Stmt +Document ::= s::Decorated Stmt { return case s of - | block(_) -> pp" ${s.pp}" -- the block will do it itself. + | block(_) -> pp" ${s} " -- the block will do it itself. | _ -> nestlines(3, s.pp) end; } @@ -31,7 +31,7 @@ s::Stmt ::= body::Stmt abstract production seq s::Stmt ::= s1::Stmt s2::Stmt { - s.pp = pp"${s1.pp}${line()}${s2.pp}"; + s.pp = pp"${s1}${line()}${s2}"; s.defs = s1.defs ++ s2.defs; s.errors := s1.errors ++ s2.errors; @@ -41,7 +41,7 @@ s::Stmt ::= s1::Stmt s2::Stmt abstract production printStmt s::Stmt ::= e::Expr { - s.pp = pp"print(${e.pp});"; + s.pp = pp"print(${e});"; s.defs = []; s.errors := e.errors; } @@ -57,7 +57,7 @@ s::Stmt ::= abstract production while s::Stmt ::= c::Expr b::Stmt { - s.pp = pp"while(${c.pp})${ppblock(b)}"; + s.pp = pp"while(${c})${ppblock(b)}"; s.defs = []; s.errors := c.errors ++ b.errors; } @@ -65,7 +65,7 @@ s::Stmt ::= c::Expr b::Stmt abstract production ifthen s::Stmt ::= c::Expr t::Stmt { - s.pp = pp"if(${c.pp})${ppblock(t)}"; + s.pp = pp"if(${c})${ppblock(t)}"; s.defs = []; s.errors := c.errors ++ t.errors; } @@ -73,7 +73,7 @@ s::Stmt ::= c::Expr t::Stmt abstract production ifelse s::Stmt ::= c::Expr t::Stmt e::Stmt { - s.pp = pp"if(${c.pp})${ppblock(t)}else${ppblock(e)}"; + s.pp = pp"if(${c})${ppblock(t)}else${ppblock(e)}"; s.defs = []; s.errors := c.errors ++ t.errors ++ e.errors; } @@ -81,7 +81,7 @@ s::Stmt ::= c::Expr t::Stmt e::Stmt abstract production assignment s::Stmt ::= id::Name e::Expr { - s.pp = pp"${id.pp} = ${e.pp};"; + s.pp = pp"${id} = ${e};"; s.defs = []; s.errors := case id.lookup of diff --git a/tutorials/simple/src/simple/abstractsyntax/TypeChecking.sv b/tutorials/simple/src/simple/abstractsyntax/TypeChecking.sv index 27013128d..c80ed3899 100644 --- a/tutorials/simple/src/simple/abstractsyntax/TypeChecking.sv +++ b/tutorials/simple/src/simple/abstractsyntax/TypeChecking.sv @@ -23,25 +23,25 @@ attribute type occurs on Expr; attribute directly. -} aspect production intLit -e::Expr ::= s::String +e::Expr ::= _ { e.type = integerType(); } aspect production floatLit -e::Expr ::= s::String +e::Expr ::= _ { e.type = floatType(); } aspect production boolLit -e::Expr ::= s::String +e::Expr ::= _ { e.type = booleanType(); } aspect production stringLit -e::Expr ::= s::String +e::Expr ::= _ { e.type = stringType(); } @@ -78,7 +78,7 @@ e::Expr ::= id::Name a generic numeric binary operation. But we have not done that on this simple language. -} -aspect production add +aspect production addOp e::Expr ::= l::Expr r::Expr { e.type = resolveNumericTypes (l.type, r.type); @@ -89,7 +89,7 @@ e::Expr ::= l::Expr r::Expr else [err(locUnknown(), "Expression \"" ++ show(100,r.pp) ++ "\" must be of type Integer or Float.\n")]); } -aspect production sub +aspect production subOp e::Expr ::= l::Expr r::Expr { e.type = resolveNumericTypes (l.type, r.type); @@ -100,7 +100,7 @@ e::Expr ::= l::Expr r::Expr else [err(locUnknown(), "Expression \"" ++ show(100,r.pp) ++ "\" must be of type Integer or Float.\n")]); } -aspect production mul +aspect production mulOp e::Expr ::= l::Expr r::Expr { e.type = resolveNumericTypes (l.type, r.type); @@ -111,7 +111,7 @@ e::Expr ::= l::Expr r::Expr else [err(locUnknown(), "Expression \"" ++ show(100,r.pp) ++ "\" must be of type Integer or Float.\n")]); } -aspect production div +aspect production divOp e::Expr ::= l::Expr r::Expr { e.type = resolveNumericTypes (l.type, r.type); @@ -150,9 +150,9 @@ Boolean ::= t::Type -- Relational and Logical Operations ------------------------------------ {- Because of forwarding in Expr.sv, we do not need to write aspect - productions for "or", "neq", "lte", "gt", or "gte", only these below. + productions for "or", "neqOp", "lteOp", "gtOp", or "gteOp", only these below. -} -aspect production eq +aspect production eqOp e::Expr ::= l::Expr r::Expr { e.type = booleanType(); @@ -163,7 +163,7 @@ e::Expr ::= l::Expr r::Expr else [err(locUnknown(), "Expression \"" ++ show(100,r.pp) ++ "\" must be of type Integer or Float.\n")]); } -aspect production lt +aspect production ltOp e::Expr ::= l::Expr r::Expr { e.type = booleanType(); @@ -187,7 +187,7 @@ e::Expr ::= l::Expr r::Expr "\" must be of type Boolean.\n")]); } -aspect production not +aspect production notOp e::Expr ::= ne::Expr { e.type = booleanType(); diff --git a/tutorials/simple/src/simple/arb/Main.sv b/tutorials/simple/src/simple/arb/Main.sv new file mode 100644 index 000000000..1541e8253 --- /dev/null +++ b/tutorials/simple/src/simple/arb/Main.sv @@ -0,0 +1,14 @@ +grammar simple:arb; + +imports simple:host; +imports simple:arb:driver; + +generator generate :: simple:concretesyntax:Root { + simple:host; +} + +function main +IOVal ::= args::[String] io_in::IOToken +{ + return ioval(arbDriver(args, io_in, generate), 0); +} diff --git a/tutorials/simple/src/simple/arb/driver/Driver.sv b/tutorials/simple/src/simple/arb/driver/Driver.sv new file mode 100644 index 000000000..9db9e6963 --- /dev/null +++ b/tutorials/simple/src/simple/arb/driver/Driver.sv @@ -0,0 +1,41 @@ +grammar simple:arb:driver; + +import silver:langutil; +import silver:langutil:pp as pp; +import simple:abstractsyntax as ast; +import simple:concretesyntax as cst; +import silver:reflect; +import silver:util:random; + +function arbDriver +IOToken ::= args::[String] + io_in::IOToken + generator::(RandomGen ::= Integer Integer) +{ + local depth :: Integer = toInteger(head(args)); + + local r_cst :: IOVal = runRandomGenT(generator(3, depth), io_in); + + --local r_ast :: IOVal = runRandomGenT(generator(3, depth), io_in); + local r_ast :: ast:Root = r_cst.iovalue.ast; + + local print_success :: IOToken = + printT( "AST: \n" ++ pp:show(100, reflect(new(r_ast))) ++ + "\n\n" ++ + "AST pretty print: \n" ++ pp:show(100, r_ast) ++ + "\n\n" ++ + "Errors: " ++ + (if null(r_ast.errors) then " No semantic errors!\n" + else "\n" ++ + messagesToString(r_ast.errors) ++ "\n" + ) + , r_cst.io ); + + local write_success :: IOToken = + writeFileT("output.c", r_ast.ast:c_code, print_success); + + return if null(args) || length(head(args)) == 0 || !isDigit(head(args)) + then error("Expected a depth to generate\n") + else write_success; +} + diff --git a/tutorials/simple/src/simple/composed/simple_all/Main.sv b/tutorials/simple/src/simple/composed/simple_all/Main.sv index 697095317..8e572f0fa 100644 --- a/tutorials/simple/src/simple/composed/simple_all/Main.sv +++ b/tutorials/simple/src/simple/composed/simple_all/Main.sv @@ -22,7 +22,7 @@ parser parse :: cst:Root { } function main -IOVal ::= args::[String] io_in::IO +IOVal ::= args::[String] io_in::IOToken { return ioval(driver(args, io_in, parse), 0); } diff --git a/tutorials/simple/src/simple/composed/simple_arb_all/Main.sv b/tutorials/simple/src/simple/composed/simple_arb_all/Main.sv new file mode 100644 index 000000000..7fce29eb3 --- /dev/null +++ b/tutorials/simple/src/simple/composed/simple_arb_all/Main.sv @@ -0,0 +1,17 @@ +grammar simple:composed:simple_arb_all; + +imports simple:host; +imports simple:arb:driver; + +generator generate :: simple:concretesyntax:Root { + simple:host; + simple:extensions:do_while; + simple:extensions:repeat_until; + simple:extensions:implication; +} + +function main +IOVal ::= args::[String] io_in::IOToken +{ + return ioval(arbDriver(args, io_in, generate), 0); +} diff --git a/tutorials/simple/src/simple/composed/simple_do_while/Main.sv b/tutorials/simple/src/simple/composed/simple_do_while/Main.sv index 6c6e9d99f..c75b09191 100644 --- a/tutorials/simple/src/simple/composed/simple_do_while/Main.sv +++ b/tutorials/simple/src/simple/composed/simple_do_while/Main.sv @@ -23,7 +23,7 @@ parser parse :: cst:Root { } function main -IOVal ::= args::[String] io_in::IO +IOVal ::= args::[String] io_in::IOToken { return ioval(driver(args, io_in, parse), 0); } diff --git a/tutorials/simple/src/simple/composed/simple_implication/Main.sv b/tutorials/simple/src/simple/composed/simple_implication/Main.sv index 08e4b55b9..85458a247 100644 --- a/tutorials/simple/src/simple/composed/simple_implication/Main.sv +++ b/tutorials/simple/src/simple/composed/simple_implication/Main.sv @@ -19,7 +19,7 @@ parser parse :: cst:Root { } function main -IOVal ::= args::[String] io_in::IO +IOVal ::= args::[String] io_in::IOToken { return ioval(driver(args, io_in, parse), 0); } diff --git a/tutorials/simple/src/simple/composed/simple_repeat_until/Main.sv b/tutorials/simple/src/simple/composed/simple_repeat_until/Main.sv index 02a76d5b8..44ef0f3c7 100644 --- a/tutorials/simple/src/simple/composed/simple_repeat_until/Main.sv +++ b/tutorials/simple/src/simple/composed/simple_repeat_until/Main.sv @@ -19,7 +19,7 @@ parser parse :: cst:Root { } function main -IOVal ::= args::[String] io_in::IO +IOVal ::= args::[String] io_in::IOToken { return ioval(driver(args, io_in, parse), 0); } diff --git a/tutorials/simple/src/simple/concretesyntax/Expr.sv b/tutorials/simple/src/simple/concretesyntax/Expr.sv index 67fec315d..cc736c0c2 100644 --- a/tutorials/simple/src/simple/concretesyntax/Expr.sv +++ b/tutorials/simple/src/simple/concretesyntax/Expr.sv @@ -31,7 +31,7 @@ concrete productions e::Expr | l::Expr '||' r::Expr { e.unparse = "(" ++ l.unparse ++ " || " ++ r.unparse ++ ")"; e.ast = ast:or(l.ast, r.ast); } | '!' ne::Expr { e.unparse = "( !" ++ ne.unparse ++ ")"; - e.ast = ast:not(ne.ast); } + e.ast = ast:notOp(ne.ast); } -- Relational Operations ------------------------ @@ -40,17 +40,17 @@ concrete productions e::Expr its lexeme and thus make all assignments to unparse be the same. -} | l::Expr op::'==' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:eq(l.ast, r.ast); } + e.ast = ast:eqOp(l.ast, r.ast); } | l::Expr op::'!=' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:neq(l.ast, r.ast); } + e.ast = ast:neqOp(l.ast, r.ast); } | l::Expr op::'<' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:lt(l.ast, r.ast); } + e.ast = ast:ltOp(l.ast, r.ast); } | l::Expr op::'<=' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:lte(l.ast, r.ast); } + e.ast = ast:lteOp(l.ast, r.ast); } | l::Expr op::'>' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:gt(l.ast, r.ast); } + e.ast = ast:gtOp(l.ast, r.ast); } | l::Expr op::'>=' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:gte(l.ast, r.ast); } + e.ast = ast:gteOp(l.ast, r.ast); } -- Arithmetic Operations ------------------------ @@ -71,13 +71,13 @@ concrete productions e::Expr | l::Expr op::'+' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:add(l.ast, r.ast); } + e.ast = ast:addOp(l.ast, r.ast); } | l::Expr op::'-' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:sub(l.ast, r.ast); } + e.ast = ast:subOp(l.ast, r.ast); } | l::Expr op::'*' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:mul(l.ast, r.ast); } + e.ast = ast:mulOp(l.ast, r.ast); } | l::Expr op::'/' r::Expr { e.unparse = "(" ++ l.unparse ++ " " ++ op.lexeme ++ " " ++ r.unparse ++ ")"; - e.ast = ast:div(l.ast, r.ast); } + e.ast = ast:divOp(l.ast, r.ast); } -- Variable reference @@ -88,11 +88,11 @@ concrete productions e::Expr -- Literals | l::IntegerLiteral { e.unparse = l.lexeme; - e.ast = ast:intLit(l.lexeme); } + e.ast = ast:intLit(toInteger(l.lexeme)); } | l::FloatLiteral { e.unparse = l.lexeme; - e.ast = ast:floatLit(l.lexeme); } + e.ast = ast:floatLit(toFloat(l.lexeme)); } | l::BooleanLiteral { e.unparse = l.lexeme; - e.ast = ast:boolLit(l.lexeme); } + e.ast = ast:boolLit(l.lexeme == "True"); } | l::StringLiteral { e.unparse = l.lexeme; - e.ast = ast:stringLit(l.lexeme); } + e.ast = ast:stringLit(unescapeString(substring(1, length(l.lexeme) - 1, l.lexeme))); } diff --git a/tutorials/simple/src/simple/extensions/do_while/DoWhile.sv b/tutorials/simple/src/simple/extensions/do_while/DoWhile.sv index 22a041591..e7666297c 100644 --- a/tutorials/simple/src/simple/extensions/do_while/DoWhile.sv +++ b/tutorials/simple/src/simple/extensions/do_while/DoWhile.sv @@ -8,6 +8,7 @@ grammar simple:extensions:do_while; extended language. -} imports silver:langutil; +imports silver:langutil:pp; imports simple:concretesyntax as cst; imports simple:abstractsyntax; imports simple:extensions:repeat_until; @@ -22,12 +23,12 @@ concrete productions s::cst:StmtMatched abstract production dowhile s::Stmt ::= body::Stmt cond::Expr { - -- s.pp = "do \n" ++ body.pp ++ "\n" ++ "while " ++ cond.pp ++ "; \n"; + s.pp = pp"do ${ppblock(body)}while (${cond});"; forwards to {- repeat body until (! cond); -} - repeatStmt(body, not(cond)); + repeatStmt(body, notOp(cond)); } diff --git a/tutorials/simple/src/simple/extensions/implication/Implication.sv b/tutorials/simple/src/simple/extensions/implication/Implication.sv index 6f38d6818..5ae9eef97 100644 --- a/tutorials/simple/src/simple/extensions/implication/Implication.sv +++ b/tutorials/simple/src/simple/extensions/implication/Implication.sv @@ -10,12 +10,12 @@ terminal Implies '=>' precedence = 6; concrete productions e::cst:Expr | l::cst:Expr '=>' r::cst:Expr { e.unparse = s"(${l.unparse} => ${r.unparse})"; - e.ast = implies(l.ast, r.ast); } + e.ast = impliesOp(l.ast, r.ast); } -abstract production implies +abstract production impliesOp e::Expr ::= l::Expr r::Expr { - e.pp = pp"(${l.pp} => ${r.pp})"; + e.pp = pp"(${l} => ${r})"; -- l => r is equivalent to !l || r - forwards to or(not(l), r); + forwards to or(notOp(l), r); } diff --git a/tutorials/simple/src/simple/extensions/repeat_until/RepeatUntil.sv b/tutorials/simple/src/simple/extensions/repeat_until/RepeatUntil.sv index 11afcbd08..938e64d6b 100644 --- a/tutorials/simple/src/simple/extensions/repeat_until/RepeatUntil.sv +++ b/tutorials/simple/src/simple/extensions/repeat_until/RepeatUntil.sv @@ -1,6 +1,7 @@ grammar simple:extensions:repeat_until; imports silver:langutil; +imports silver:langutil:pp; imports simple:concretesyntax as cst; imports simple:abstractsyntax; @@ -15,14 +16,14 @@ concrete productions s::cst:StmtMatched abstract production repeatStmt s::Stmt ::= body::Stmt cond::Expr { - -- s.pp = "repeat \n" ++ body.pp ++ "\n" ++ "until " ++ cond.pp ++ "; \n"; + s.pp = pp"repeat ${ppblock(body)}until ${cond};"; forwards to {- body while (! cond) { body } -} seq( body, - while(not(cond), block(body)) + while(notOp(cond), block(body)) ); } diff --git a/tutorials/simple/src/simple/host/Main.sv b/tutorials/simple/src/simple/host/Main.sv index f7193c555..07e51d08f 100644 --- a/tutorials/simple/src/simple/host/Main.sv +++ b/tutorials/simple/src/simple/host/Main.sv @@ -24,7 +24,7 @@ parser parse :: simple:concretesyntax:Root { } function main -IOVal ::= args::[String] io_in::IO +IOVal ::= args::[String] io_in::IOToken { return ioval(driver(args, io_in, parse), 0); } diff --git a/tutorials/simple/src/simple/host/driver/Driver.sv b/tutorials/simple/src/simple/host/driver/Driver.sv index ab4ff0360..ffcc1ba00 100644 --- a/tutorials/simple/src/simple/host/driver/Driver.sv +++ b/tutorials/simple/src/simple/host/driver/Driver.sv @@ -6,15 +6,15 @@ import simple:concretesyntax as cst; import simple:abstractsyntax as ast; function driver -IO ::= args::[String] - io_in::IO +IOToken ::= args::[String] + io_in::IOToken the_parser::(ParseResult ::= String String) { local filename :: String = if null(args) then "" else head(args); - local isF :: IOVal = isFile(filename, io_in); + local isF :: IOVal = isFileT(filename, io_in); - local text :: IOVal = readFile(filename, isF.io); + local text :: IOVal = readFileT(filename, isF.io); local result :: ParseResult = the_parser(text.iovalue, filename); @@ -22,8 +22,9 @@ IO ::= args::[String] local r_ast :: ast:Root = r_cst.ast; - local print_success :: IO = - print( "Command line arguments: " ++ filename ++ + local print_success :: IOToken = + printObjectPairForOriginsViz(r_cst, r_ast, + printT( "Command line arguments: " ++ filename ++ "\n\n" ++ "CST pretty print: \n" ++ r_cst.unparse ++ "\n\n" ++ @@ -34,13 +35,13 @@ IO ::= args::[String] else "\n" ++ messagesToString(r_ast.errors) ++ "\n" ) - , text.io ); + , text.io )); - local write_success :: IO = - writeFile("output.c", r_ast.ast:c_code, print_success); + local write_success :: IOToken = + writeFileT("output.c", r_ast.ast:c_code, print_success); - local print_failure :: IO = - print("Encountered a parse error:\n" ++ result.parseErrors ++ "\n", text.io); + local print_failure :: IOToken = + printT("Encountered a parse error:\n" ++ result.parseErrors ++ "\n", text.io); return if !isF.iovalue then error("\n\nFile \"" ++ filename ++ "\" not found.\n") diff --git a/tutorials/simple/src/simple/terminals/Terminals.sv b/tutorials/simple/src/simple/terminals/Terminals.sv index 42dfc3128..aea9e7f9f 100644 --- a/tutorials/simple/src/simple/terminals/Terminals.sv +++ b/tutorials/simple/src/simple/terminals/Terminals.sv @@ -71,10 +71,8 @@ terminal Id /[a-zA-Z][a-zA-Z0-9_]*/ submits to { KEYWORDS }; -- Literals -terminal IntegerLiteral /[0-9]+/; -terminal FloatLiteral /[0-9]+\.[0-9]+/; +terminal IntegerLiteral /[0-9]+/ repeatProb = 0.5; +terminal FloatLiteral /[0-9]+\.[0-9]+/ repeatProb = 0.5; terminal BooleanLiteral /(True)|(False)/ lexer classes { KEYWORDS }; -terminal StringLiteral /"([^"\n\\]|\\"|\\\\|\\n|\\r|\\t)*"/; - - +terminal StringLiteral /"([^"\n\\]|\\"|\\\\|\\n|\\r|\\t)*"/ repeatProb=0.95; diff --git a/tutorials/simple/with_all/silver-compile b/tutorials/simple/with_all/silver-compile index cf24ae043..926f84607 100755 --- a/tutorials/simple/with_all/silver-compile +++ b/tutorials/simple/with_all/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../../support/bin/silver" "$@"; } diff --git a/tutorials/simple/with_do_while/silver-compile b/tutorials/simple/with_do_while/silver-compile index 4c222f7cd..45f2bb203 100755 --- a/tutorials/simple/with_do_while/silver-compile +++ b/tutorials/simple/with_do_while/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../../support/bin/silver" "$@"; } diff --git a/tutorials/simple/with_implication/silver-compile b/tutorials/simple/with_implication/silver-compile index 6ecedc7d8..3d88b5b00 100755 --- a/tutorials/simple/with_implication/silver-compile +++ b/tutorials/simple/with_implication/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../../support/bin/silver" "$@"; } diff --git a/tutorials/simple/with_repeat_until/silver-compile b/tutorials/simple/with_repeat_until/silver-compile index dbb91cba2..dc59a2a41 100755 --- a/tutorials/simple/with_repeat_until/silver-compile +++ b/tutorials/simple/with_repeat_until/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../../support/bin/silver" "$@"; } diff --git a/tutorials/stlc/Abstract.sv b/tutorials/stlc/Abstract.sv new file mode 100644 index 000000000..d1a90638a --- /dev/null +++ b/tutorials/stlc/Abstract.sv @@ -0,0 +1,290 @@ +grammar stlc; + +restricted inherited attribute gamma::[Pair]; +implicit synthesized attribute type::Either; +synthesized attribute errors::[String]; + +restricted inherited attribute substV::String; +restricted inherited attribute substE::Expression; +restricted synthesized attribute substed::Expression; +restricted synthesized attribute isvalue::Boolean; +implicit synthesized attribute nextStep::Maybe; + +synthesized attribute pp::String; + + + +function lookupType +Maybe ::= name::String gamma::[Pair] +{ + return if null(gamma) + then nothing() + else if head(gamma).fst == name + then just(head(gamma).snd) + else lookupType(name, tail(gamma)); +} + + +synthesized attribute singleSteps::[Expression]; +nonterminal Root with pp, type, errors, nextStep, singleSteps; + +abstract production root +top::Root ::= e::Expression +{ + e.gamma = []; + top.type = e.type; + top.errors = e.errors; + + top.pp = e.pp; + + top.nextStep = e.nextStep; + + top.singleSteps = case e.nextStep of + | just(x) -> e::root(x).singleSteps + | nothing() -> [e] + end; +} + + + +nonterminal Expression with + gamma, type, errors, + substV, substE, substed, isvalue, nextStep, + pp; + +abstract production var +top::Expression ::= name::String +{ + top.type = case lookupType(name, top.gamma) of + | just(x) -> x + | nothing() -> left("Unknown variable " ++ name) + end; + top.errors = case top.type of + | left(s) -> [s] + | _ -> [] + end; + + top.isvalue = false; + + top.substed = if top.substV == name + then top.substE + else top; + + implicit top.nextStep = ; + + top.pp = name; +} + + +abstract production abs +top::Expression ::= name::String ty::Type body::Expression +{ + body.gamma = [pair(name, ty)] ++ top.gamma; + top.type = arrow(ty, body.type); + top.errors = case top.type, body.type of + | left(s), right(_) -> [s] ++ body.errors + | _, _ -> body.errors + end; + + top.isvalue = true; + + body.substV = top.substV; + body.substE = top.substE; + top.substed = if top.substV == name + then top + else abs(name, ty, body.substed); + + implicit top.nextStep = ; + + top.pp = "lambda " ++ name ++ ":" ++ ty.pp ++ ". " ++ body.pp; +} + + +abstract production app +top::Expression ::= t1::Expression t2::Expression +{ + t1.gamma = top.gamma; + t2.gamma = top.gamma; + top.type = case t1.type of + | arrow(ty1, ty2) when tyEq(ty1, t2.type) -> ty2 + | arrow(_, _) -> left("Application type mismatch") + | _ -> left("Non-function applied") + end; + top.errors = case top.type, t1.type, t2.type of + | left(s), right(ty1), right(ty2) -> [s] ++ t1.errors ++ t2.errors + | _, _, _ -> t1.errors ++ t2.errors + end; + + top.isvalue = false; + + t1.substV = top.substV; + t2.substV = top.substV; + t1.substE = top.substE; + t2.substE = top.substE; + top.substed = app(t1.substed, t2.substed); + + top.nextStep = case t1, t2 of + | abs(n, t, b), v when v.isvalue -> + decorate b with {substV=n; substE=v;}.substed + | v1, _ when !v1.isvalue -> app(t1.nextStep, t2) + | _, _ -> app(t1, t2.nextStep) + end; + + top.pp = "(" ++ t1.pp ++ ") (" ++ t2.pp ++ ")"; +} + + +abstract production or +top::Expression ::= t1::Expression t2::Expression +{ + t1.gamma = top.gamma; + t2.gamma = top.gamma; + top.type = case t1.type, t2.type of + | bool(), bool() -> bool() + | _, _ -> left("Both disjuncts must be of type Bool") + end; + top.errors = case top.type, t1.type, t2.type of + | left(s), right(_), right(_) -> [s] ++ t1.errors ++ t2.errors + | _, _, _ -> t1.errors ++ t2.errors + end; + + top.isvalue = false; + + t1.substV = top.substV; + t2.substV = top.substV; + t1.substE = top.substE; + t2.substE = top.substE; + top.substed = or(t1.substed, t2.substed); + + top.nextStep = case t1, t2 of + | tru_a(), _ -> tru_a() + | fals_a(), _ -> t2 + | _, _ -> or(t1.nextStep, t2) + end; + + top.pp = "(" ++ t1.pp ++ ") || (" ++ t2.pp ++ ")"; +} + + +abstract production and +top::Expression ::= t1::Expression t2::Expression +{ + t1.gamma = top.gamma; + t2.gamma = top.gamma; + top.type = case t1.type, t2.type of + | bool(), bool() -> bool() + | _, _ -> left("Both conjuncts must be of type Bool") + end; + top.errors = case top.type, t1.type, t2.type of + | left(s), right(ty1), right(ty2) -> [s] ++ t1.errors ++ t2.errors + | _, _, _ -> t1.errors ++ t2.errors + end; + + top.isvalue = false; + + t1.substV = top.substV; + t2.substV = top.substV; + t1.substE = top.substE; + t2.substE = top.substE; + top.substed = and(t1.substed, t2.substed); + + top.nextStep = case t1, t2 of + | tru_a(), _ -> t2 + | fals_a(), _ -> fals_a() + | _, _ -> and(t1.nextStep, t2) + end; + + top.pp = "(" ++ t1.pp ++ ") && (" ++ t2.pp ++ ")"; +} + + +abstract production tru_a +top::Expression ::= +{ + top.type = bool(); + top.errors = []; + + top.isvalue = true; + + top.substed = top; + + implicit top.nextStep = ; + + top.pp = "true"; +} + + +abstract production fals_a +top::Expression ::= +{ + top.type = bool(); + top.errors = []; + + top.isvalue = true; + + top.substed = top; + + implicit top.nextStep = ; + + top.pp = "false"; +} + + +abstract production notOp +top::Expression ::= e::Expression +{ + e.gamma = top.gamma; + top.type = case e.type of + | bool() -> bool() + | _ -> left("Not requires an argument of type Bool") + end; + top.errors = case top.type, e.type of + | left(s), right(_) -> [s] ++ e.errors + | _, _ -> e.errors + end; + + top.isvalue = false; + + e.substV = top.substV; + e.substE = top.substE; + top.substed = notOp(e.substed); + + top.nextStep = case e of + | tru_a() -> fals_a() + | fals_a() -> tru_a() + | _ -> notOp(e.nextStep) + end; + + top.pp = "!(" ++ e.pp ++ ")"; +} + + + + +nonterminal Type with pp; + +abstract production arrow +top::Type ::= t1::Type t2::Type +{ + top.pp = "(" ++ t1.pp ++ ") -> " ++ t2.pp; +} + + +abstract production bool +top::Type ::= +{ + top.pp = "Bool"; +} + + + +function tyEq +Boolean ::= t1::Type t2::Type +{ + return case t1, t2 of + | bool(), bool() -> true + | arrow(t11, t12), arrow(t21, t22) -> + tyEq(t11, t21) && tyEq(t12, t22) + | _, _ -> false + end; +} diff --git a/tutorials/stlc/Concrete.sv b/tutorials/stlc/Concrete.sv new file mode 100644 index 000000000..de4cf9194 --- /dev/null +++ b/tutorials/stlc/Concrete.sv @@ -0,0 +1,155 @@ +grammar stlc; + + +terminal ID_t /[a-zA-Z][a-zA-Z0-9]*/; +terminal True_t 'true' dominates {ID_t}; +terminal False_t 'false' dominates {ID_t}; + +terminal Abs_t 'lambda' dominates {ID_t}; +terminal Colon_t ':'; +terminal Dot_t '.'; +terminal Or_t '||'; +terminal And_t '&&'; +terminal Not_t '!'; + +terminal TyBool_t 'Bool' dominates {ID_t} ; +terminal TyArr_t '->' association = right; + +terminal LParen_t '('; +terminal RParen_t ')'; + +ignore terminal WhiteSpace_t /[\t\r\n\ ]+/; + + + +synthesized attribute ast::a; + + +-- Root +closed nonterminal Root_c with ast; + +concrete production root_c +r::Root_c ::= t::Abs_c +{ + r.ast = root(t.ast); +} + + +-- Terms +closed nonterminal Abs_c with ast; +closed nonterminal Term_c with ast; +closed nonterminal And_c with ast; +closed nonterminal App_c with ast; +closed nonterminal Expr_c with ast; + + +-- Abs_c +concrete production abs_c +top::Abs_c ::= 'lambda' n::ID_t ':' t::Type_c '.' body::Abs_c +{ + top.ast = abs(n.lexeme, t.ast, body.ast); +} + +concrete production abs_term_c +top::Abs_c ::= t::Term_c +{ + top.ast = t.ast; +} + + +-- Term_c +concrete production or_c +top::Term_c ::= t1::And_c '||' t2::Term_c +{ + top.ast = or(t1.ast, t2.ast); +} + +concrete production term_and_c +top::Term_c ::= a::And_c +{ + top.ast = a.ast; +} + + +-- And_c +concrete production and_c +top::And_c ::= t1::App_c '&&' t2::And_c +{ + top.ast = and(t1.ast, t2.ast); +} + +concrete production and_app_c +top::And_c ::= a::App_c +{ + top.ast = a.ast; +} + + +-- App_c +concrete production app_c +top::App_c ::= t1::App_c t2::Expr_c +{ + top.ast = app(t1.ast, t2.ast); +} + +concrete production app_expr_c +top::App_c ::= e::Expr_c +{ + top.ast = e.ast; +} + + +-- Expr_c +concrete production var_c +top::Expr_c ::= n::ID_t +{ + top.ast = var(n.lexeme); +} + +concrete production tru_c +top::Expr_c ::= 'true' +{ + top.ast = tru_a(); +} + +concrete production fals_c +top::Expr_c ::= 'false' +{ + top.ast = fals_a(); +} + +concrete production not_c +top::Expr_c ::= '!' e::Expr_c +{ + top.ast = notOp(e.ast); +} + +concrete production parens_c +top::Expr_c ::= '(' t::Abs_c ')' +{ + top.ast = t.ast; +} + + +-- Types +closed nonterminal Type_c with ast; + +concrete production boolTy_c +top::Type_c ::= 'Bool' +{ + top.ast = bool(); +} + +concrete production arrTy_c +top::Type_c ::= t1::Type_c '->' t2::Type_c +{ + top.ast = arrow(t1.ast, t2.ast); +} + +concrete production parenTy_c +top::Type_c ::= '(' t::Type_c ')' +{ + top.ast = t.ast; +} + + diff --git a/tutorials/stlc/Main.sv b/tutorials/stlc/Main.sv new file mode 100644 index 000000000..a09c5644b --- /dev/null +++ b/tutorials/stlc/Main.sv @@ -0,0 +1,72 @@ +grammar stlc; + + + +parser hostparse :: Root_c +{ + stlc; +} + +function main +IO ::= largs::[String] +{ + local attribute args::String; + args = implode(" ", largs); + + local attribute result :: ParseResult; + result = hostparse(args, "<>"); + + local attribute r_cst::Root_c; + r_cst = result.parseTree; + + local attribute r::Root = r_cst.ast; + + local attribute print_success :: IO; + print_success = + print("Expression: " ++ r.pp ++ "\n" ++ + "Type: " ++ typeToString(r.type) ++ "\n" ++ + "Errors: " ++ errorsToString(r.errors) ++ "\n" ++ + "SingleSteps Attribute (Evaluation Trace):\n " ++ listToString_Expression(r.singleSteps) ++ "\n"); + + local attribute print_failure :: IO; + print_failure = print("Encountered a parse error:\n" ++ + result.parseErrors ++ "\n"); + + return do { new(if result.parseSuccess then print_success else print_failure); return 0; }; +} + + +function typeToString +String ::= e::Either +{ + return case e of + | left(s) -> s + | right(t) -> t.pp + end; +} + +function listToString_Expression +String ::= l::[Expression] +{ + return foldl(\x::String t::Expression -> + x ++ ", " ++ t.pp, "[", l) ++ "]"; +} + + +function errorsToString +String ::= l::[String] +{ + return foldl(\x::String t::String -> + x ++ ", " ++ t, "", l); +} + + +function stepsAttrString +String ::= l::[[Expression]] +{ + return case l of + | [] -> "" + | h::t -> " " ++ listToString_Expression(h) ++ "\n" ++ stepsAttrString(t) + end; +} + diff --git a/tutorials/stlc/README.md b/tutorials/stlc/README.md new file mode 100644 index 000000000..e69f4715b --- /dev/null +++ b/tutorials/stlc/README.md @@ -0,0 +1,39 @@ + +The grammar in this directory implements typing and evaluation for the +simply-typed lambda calculus augmented with Boolean operations. This +is done by taking advantage of implicit monads, which allow us to use +monadic expressions as if they were not monadic in attribute +equations. + + +To compile the grammar, run +``` +./silver-compile +``` +This will produce `stlc.jar`. + + +There are two ways to run examples: +- Examples can be run with + ``` + java -jar stlc.jar + ``` + replacing `` with the expression one wishes to type + and evaluate. +- Examples may also be run with + ``` + ./run + ``` + which will enter a REPL. Expressions may be typed at the prompt. + To exit, enter a blank line. + + +The following example expressions show the syntax of the language: +- `lambda f:Bool -> Bool. f` : a function taking an argument of type + `Bool -> Bool` named `f` +- `true && false` : conjunction of the constants `true` and `false` +- `true || true` : disjunction of the constant `true` with itself +- `(lambda x:Bool. x) true` : a function applied to the constant + `true` +Associativity and precedence are as expected. + diff --git a/tutorials/stlc/run b/tutorials/stlc/run new file mode 100755 index 000000000..4e2c8765b --- /dev/null +++ b/tutorials/stlc/run @@ -0,0 +1,25 @@ +#!/usr/bin/env bash + +JARFILE=stlc.jar + + +if [ ! -f $JARFILE ]; then + echo -e "File $JARFILE not found; building it" + if ! ./silver-compile; then #exit on Silver compiler error + exit 1 + fi + echo "" +fi + +echo -e -n "Enter an expression: " +read entered + +while [ -n "$entered" ]; do + java -jar $JARFILE "$entered" + echo "" + echo "" + echo -e -n "Enter an expression: " + read entered +done + +echo "" diff --git a/tutorials/stlc/silver-compile b/tutorials/stlc/silver-compile new file mode 100755 index 000000000..7a91ae56c --- /dev/null +++ b/tutorials/stlc/silver-compile @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +set -eu +silver() { "../../support/bin/silver" "$@"; } + +SRC=.. # cheating a bit. Current directory should be 'stlc' +GRAMMAR=stlc + +silver -I $SRC $@ $GRAMMAR + diff --git a/tutorials/turing/silver-compile b/tutorials/turing/silver-compile index 7c183f79e..99aaffdb4 100755 --- a/tutorials/turing/silver-compile +++ b/tutorials/turing/silver-compile @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu silver() { "../../support/bin/silver" "$@"; } diff --git a/tutorials/turing/src/turing/bin/Main.sv b/tutorials/turing/src/turing/bin/Main.sv index 248b39836..e5bdb0058 100644 --- a/tutorials/turing/src/turing/bin/Main.sv +++ b/tutorials/turing/src/turing/bin/Main.sv @@ -7,13 +7,13 @@ parser parse::Dcls { } function main -IOVal ::= largs::[String] i::IO +IOVal ::= largs::[String] i::IOToken { local attribute args :: String; args = implode(" ", largs); local attribute file :: IOVal; - file = readFile(args, i); + file = readFileT(args, i); local attribute result :: Dcls; result = parse(file.iovalue, args).parseTree; -- If we run into a parse error, it won't be pretty! @@ -33,18 +33,18 @@ IOVal ::= largs::[String] i::IO allMachines, result.tape); - local attribute doneio::IO; + local attribute doneio::IOToken; doneio = - print(t.pp ++ "\n-------------------------------------------------------------\n", - print(result.tape.pp ++ "\n-------------------------------------------------------------\n", - print("Results for " ++ args ++ ":\n-------------------------------------------------------------\n", + printT(t.pp ++ "\n-------------------------------------------------------------\n", + printT(result.tape.pp ++ "\n-------------------------------------------------------------\n", + printT("Results for " ++ args ++ ":\n-------------------------------------------------------------\n", extras.ioOut))); return ioval(doneio, 0); } -inherited attribute ioIn :: IO; -synthesized attribute ioOut :: IO; +inherited attribute ioIn :: IOToken; +synthesized attribute ioOut :: IOToken; nonterminal IOAMachineList with ioIn, ioOut, machines; function getImports @@ -57,7 +57,7 @@ abstract production getImportsHelp top::IOAMachineList ::= seen::[String] need::[String] { local attribute text :: IOVal; - text = readFile(head(need), top.ioIn); + text = readFileT(head(need), top.ioIn); local attribute result :: Dcls; result = parse(text.iovalue, head(need)).parseTree; diff --git a/update b/update index 63c9dc87c..a468760e2 100755 --- a/update +++ b/update @@ -1,4 +1,4 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu diff --git a/upload-override-jars b/upload-override-jars new file mode 100755 index 000000000..56ae5e836 --- /dev/null +++ b/upload-override-jars @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +set -eu + +cd "$(dirname "$(realpath "${BASH_SOURCE[0]}")")" + +# if [[ -n "$(git status --porcelain)" ]]; then +# echo "You have uncommitted changes! Refusing to run." +# exit 1 +# fi + +branch_name="$(git rev-parse --abbrev-ref HEAD)" +encoded_branch_name="$(curl -Gso /dev/null -w %{url_effective} --data-urlencode $branch_name "" | cut -c 3-)" +commit_hash="$(git rev-parse HEAD)" + +# If your username on the your development machine doesn't match your username +# on foundry, put a block in ~/.ssh/config like the following: +# +# Host foundry.remexre.xyz +# User nathan +rsync -Pa jars/ foundry.remexre.xyz:/melt/jenkins/export-scratch/melt-jenkins/${commit_hash}-jars/ + +echo "Now go to https://foundry.remexre.xyz/jenkins/job/melt-umn/job/silver/job/$encoded_branch_name/build and" +echo "pass /export/scratch/melt-jenkins/${commit_hash}-jars as OVERRIDE_JARS"