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