Compare commits

..

91 Commits

Author SHA1 Message Date
John J. Camilleri
c058457337 Change Data.Text to String as a test, seemingly makes no difference. 2021-03-10 16:50:26 +01:00
John J. Camilleri
8f5033e4ce Add notes on profiling 2021-03-09 08:36:35 +01:00
John J. Camilleri
126b61ea03 Merge branch 'master' into lpgf 2021-03-08 13:52:34 +01:00
John J. Camilleri
99abb9b2a5 Add Phrasebook benchmark snippet to LPGF README 2021-03-08 13:37:02 +01:00
John J. Camilleri
3e9d12854a Switch to 10000-tree Phrasebook treebank. All errors to do with missing functions, plus variants in German. 2021-03-08 11:19:06 +01:00
John J. Camilleri
fd07946a50 Remove commented line 2021-03-08 10:42:16 +01:00
John J. Camilleri
c76efcf916 Use C runtime in mkTreebank script 2021-03-08 10:17:03 +01:00
John J. Camilleri
785d6069e2 Fix lin2string and pass all unittests and Phrasebook 2021-03-08 09:53:10 +01:00
John J. Camilleri
0f4b349b0b Remove old commented code 2021-03-05 16:51:59 +01:00
John J. Camilleri
dbf369aae5 Make removal of record fields recursive. Latest results with Phrasebook:
Bul ✓
Cat ✗
Chi ✓
Dan ✓
Dut ✓
Eng ✓
Est ✓
Fin ✓
Fre ✗
Ger ✓
Hin ✓
Ita ✗
Jpn ✓
Lav ✓
Nor ✓
Pol ✓
Ron ✓
Snd ✗
Spa ✓
Swe ✓
Tha ✓
Urd ✓

Passed 18 | Failed 4 | Total 22
2021-03-05 16:48:05 +01:00
John J. Camilleri
0d4659fe8c Add workaround for missing param defs. Add links to gf-core issues in workaround comments. 2021-03-05 13:23:00 +01:00
John J. Camilleri
575a746a3e Add LPGF function for catching errors. Manual fixes to Phrasebook treebank. 2021-03-05 12:05:25 +01:00
John J. Camilleri
70581c2d8c Improve base case in table handling, cleanup. Add run-phrasebook script, current output:
Bul ✗
Cat ✗
Chi ✓
Dan ✓
Dut ✓
Eng ✓
Est ✓
Fin ✗
Fre ✗
Ger ✓
Hin ✓
Ita ✗
Jpn ✓
Lav ✓
Nor ✓
Pol ✓
Ron ✓
Snd ✗
Spa ✗
Swe ✓
Tha ✓
Urd ✓

Passed 15 | Failed 7 | Total 22
2021-03-04 17:09:35 +01:00
John J. Camilleri
bca1e2286d New handling of tables, works for all tests but Phrasebook still fails 2021-03-04 16:42:56 +01:00
John J. Camilleri
94f76b9e36 Add more tests to Params5 which cause it to fail again
Originally found in PhrasebookFre
2021-03-04 13:38:55 +01:00
John J. Camilleri
f5886bf447 Add more complex param/table unit tests and pass them. Still fails on Phrasebook though. 2021-03-04 12:37:12 +01:00
John J. Camilleri
0ba0438dc7 Add a little colour to benchmark output 2021-03-04 10:20:57 +01:00
John J. Camilleri
30b016032d Also store Pre prefixes in token map. Introduce IntMapBuilder data structure.
Storing of prefixes uses show/read, which isn't a great solution but avoids having yet another token map.
2021-03-04 09:58:17 +01:00
John J. Camilleri
4082c006c3 Extract token strings and put them in map which linfuns refer to by index, to reduce LPGF sizes. 2021-03-04 00:16:12 +01:00
John J. Camilleri
adc162b374 Pass all unit tests and Foods again, with new strategy. Cleanup. 2021-03-03 15:21:32 +01:00
John J. Camilleri
3beed2c49e Replace list comprehension lookups with maps. Halfway through transitioning to new strategy for tables/params, see testsuite/lpgf/README.md. 2021-03-03 13:26:03 +01:00
John J. Camilleri
a8e3dc8855 Improve mkTreebank script. Add 100-tree Phrasebook treebank. Improve output in testsuite. 2021-03-03 11:01:31 +01:00
John J. Camilleri
997d7c1694 Use ErrorMonad instead of IOE
It probably ends up being the same thing, but the code is a little cleaner for it.
2021-03-03 09:36:48 +01:00
John J. Camilleri
4c09e4a340 Remove LF prefix from constructors. Pass all unit tests and Foods again, but improvements/cleanup still necessary. 2021-03-03 09:19:52 +01:00
John J. Camilleri
33e0e98aec Add 1-tree treebank for Phrasebook in a few languages 2021-02-28 00:34:46 +01:00
John J. Camilleri
83bc3c9c6e More work on params: pass all tests except params1 (!) 2021-02-27 23:13:02 +01:00
John J. Camilleri
f42b5ec9ef More work on params, but Foods fails now 2021-02-26 20:25:05 +01:00
John J. Camilleri
4771d9c356 WIP params 2021-02-26 17:18:21 +01:00
John J. Camilleri
9785f8351d Reduce Params2 further 2021-02-26 11:52:12 +01:00
John J. Camilleri
6a5d735904 Reduce Params2 unittest (still fails) 2021-02-26 10:26:11 +01:00
John J. Camilleri
8324ad8801 Add pretty-printing of LPGF grammars, to help debugging 2021-02-26 10:13:33 +01:00
John J. Camilleri
20290be616 Add Params2 unit test, from problem uncovered in PhrasebookGer 2021-02-22 10:52:37 +01:00
John J. Camilleri
b4a393ac09 Pass missing unit test 2021-02-21 14:22:46 +01:00
John J. Camilleri
9942908df9 Add unit test for missing lins 2021-02-21 14:05:31 +01:00
John J. Camilleri
dca2ebaf72 Add Phrasebook to testsuite. Move grammars into subfolders. Add run-bench script. 2021-02-20 13:22:29 +01:00
John J. Camilleri
5ad5789b31 Filter out record fields which don't exist in lintype
This is to work around an inconsistency in the canonical representation
2021-02-19 15:19:40 +01:00
John J. Camilleri
9f3f4139b1 Grammar and languages to run in testsuite can be specified by command line options, see README 2021-02-19 11:14:55 +01:00
John J. Camilleri
505c12c528 Rename run.hs to test.hs 2021-02-19 09:33:35 +01:00
John J. Camilleri
023b50557e Write LPGF dump to file when DEBUG is set, rather than console 2021-02-19 09:31:26 +01:00
John J. Camilleri
2b0493eece Tweak memory reporting and strictness in benchmark 2021-02-19 09:18:01 +01:00
John J. Camilleri
51e543878b Add support for wildcards when specifying modules names in benchmark compilation 2021-02-18 21:34:23 +01:00
John J. Camilleri
625386a14f Force evaluation in benchmark linearisation
BangPatterns only does WHNF which is not sufficient, previous benchmark results are thus wrong
2021-02-18 21:01:30 +01:00
John J. Camilleri
5240749fad Make grammar and trees files command line arguments into benchmark script 2021-02-18 15:27:25 +01:00
John J. Camilleri
e6079523f1 Remove ParamAliasDefs by inlining their definitions 2021-02-18 14:45:10 +01:00
John J. Camilleri
866a2101e1 When projecting a non-existent field, return Prelude.False
This seems to be GF's own behaviour, as exhibited by the canonical version of PhrasebookTha:

    NNumeral Numeral_0 = {s = Numeral_0.s; hasC = <>.hasC};
2021-02-18 14:42:39 +01:00
John J. Camilleri
d8557e8433 Enable debug output to files with envvar DEBUG=1 2021-02-18 14:40:03 +01:00
John J. Camilleri
7a5bc2dab3 Separate compile/run in benchmark 2021-02-17 16:57:06 +01:00
John J. Camilleri
9a263450f5 Add PFG2 linearisation to benchmark 2021-02-17 15:30:11 +01:00
John J. Camilleri
8e1fa4981f Add memory stats to benchmark 2021-02-17 15:02:39 +01:00
John J. Camilleri
b4fce5db59 Use envvars in benchmark for controlling PGF/LPGF. Add readme. 2021-02-17 11:44:00 +01:00
John J. Camilleri
6a7ead0f84 Add benchmark for comparing PGF and LPGF 2021-02-17 10:04:36 +01:00
John J. Camilleri
d3988f93d5 writePGF et al. functions return path[s] of written files 2021-02-17 10:03:52 +01:00
John J. Camilleri
236dbdbba3 Minor tidying 2021-02-17 00:15:44 +01:00
John J. Camilleri
768c3d9b2d Include return types for params, records, pre 2021-02-17 00:04:37 +01:00
John J. Camilleri
29114ce606 Improve binary format, reducing Foods.lpgf from 300 to 73KB (4x smaller!) 2021-02-16 23:30:21 +01:00
John J. Camilleri
5be21dba1c Add and pass FoodsJpn 2021-02-16 22:49:37 +01:00
John J. Camilleri
d5cf00f711 Add and pass all Foods languages, except Jpn 2021-02-16 22:41:28 +01:00
John J. Camilleri
312cfeb69d Add Afr, Amh, Cat, Cze, Dut, Ger foods grammars to testsuite 2021-02-16 22:33:26 +01:00
John J. Camilleri
2d03b9ee0c Finish type passing in val2lin, generalise projection case and pass FoodsFre testsuite. 2021-02-16 21:07:24 +01:00
John J. Camilleri
4c06c3f825 Add case for when pre is not followed by anything 2021-02-16 21:01:01 +01:00
John J. Camilleri
7227ede24b WIP return type from val2lin for use in projection case 2021-02-16 17:18:01 +01:00
John J. Camilleri
398b294734 Use Data.Text instead of String. Rename Abstr to Abstract, Concr to Concrete. 2021-02-16 16:04:40 +01:00
John J. Camilleri
d394cacddf Add support for CAPIT and ALL_CAPIT 2021-02-16 15:17:54 +01:00
John J. Camilleri
21f14c2aa1 Add support for SOFT_SPACE 2021-02-16 14:57:33 +01:00
John J. Camilleri
23e49cddb7 Add support for SOFT_BIND (which PGF runtime doesn't support) 2021-02-16 14:51:29 +01:00
John J. Camilleri
4d1217b06d Add support for pre 2021-02-15 21:57:05 +01:00
John J. Camilleri
4f0abe5540 Add FoodsFre, fails because pre is not implemented
Also an unhandled Projection case
2021-02-15 01:14:34 +01:00
John J. Camilleri
109822675b Pass test with FoodsFin, by forcibly resorting record fields to make s first 2021-02-15 00:43:53 +01:00
John J. Camilleri
d563abb928 Minors 2021-02-13 00:59:15 +01:00
John J. Camilleri
a58a6c8a59 Add FoodsFin to testsuite (fails) 2021-02-13 00:16:03 +01:00
John J. Camilleri
98f6136ebd Add support for BIND 2021-02-13 00:14:35 +01:00
John J. Camilleri
8cfaa69b6e Handle record tables, pass FoodSwe in testsuite 2021-02-12 23:51:16 +01:00
John J. Camilleri
a12f58e7b0 Add test case for selection using records (fails) 2021-02-10 13:55:38 +01:00
John J. Camilleri
d5f68970b9 Add FoodsSwe (fails) 2021-02-09 10:54:51 +01:00
John J. Camilleri
9c2d8eb0b2 Add FoodsChi, FoodsHeb to LPGF testsuite 2021-02-09 10:14:40 +01:00
John J. Camilleri
34f0fc0ba7 Fix bug in dynamic parameter handling, compile FoodsBul successfully 2021-02-03 15:41:27 +01:00
John J. Camilleri
42b9e7036e Support dynamic param values 2021-02-03 13:16:10 +01:00
John J. Camilleri
132f693713 Minor cleanup 2021-02-03 09:44:15 +01:00
John J. Camilleri
153bffdad7 Support nested parameters, but fails with non-static values (see FoodsBull, ASg kind.g). 2021-02-03 00:11:22 +01:00
John J. Camilleri
d09838e97e Separate .trees and .treebank, and add a script for making the latter from the former 2021-02-02 21:46:38 +01:00
John J. Camilleri
c94bffe435 Generalise testsuite script to use treebank files, add FoodEng 2021-02-02 21:22:36 +01:00
John J. Camilleri
2a5850023b Correctly handle projection, but only in limited cases 2021-02-01 13:08:39 +01:00
John J. Camilleri
fe15aa0c00 Use canonical GF in LPGF compiler
Still contains some hardcoded values, missing cases.

I notice now that LPGF and Canonical GF are almost identical, so maybe we don't need a new LPGF format,
just a linearization-only runtime which works on canonical grammars.
The argument for keeping LGPF is that it would be optimized for size and speed.
2021-02-01 12:28:06 +01:00
John J. Camilleri
cead0cc4c1 Add selection and projection cases but not working 2021-01-26 09:55:07 +01:00
John J. Camilleri
6f622b496b Rename Zero grammar to Walking 2021-01-26 09:35:21 +01:00
John J. Camilleri
270e7f021f Add binary instances 2021-01-25 14:42:00 +01:00
John J. Camilleri
32b0860925 Make LPGF testsuite work (but still fails)
stack test :lpgf
2021-01-25 13:41:33 +01:00
John J. Camilleri
f24c50339b Strip down format. More early work on compiler. Add testsuite (doesn't work yet). 2021-01-25 12:10:30 +01:00
John J. Camilleri
cd5881d83a Early work on LPGF compiler 2021-01-22 15:17:36 +01:00
John J. Camilleri
93b81b9f13 Add first version of LPGF datatype, with linearization function and some hardcoded examples 2021-01-22 14:07:41 +01:00
John J. Camilleri
8ad9cf1e09 Add flag and stubs for compiling to LPGF format 2021-01-19 17:21:13 +01:00
633 changed files with 378524 additions and 25717 deletions

View File

@@ -14,7 +14,7 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
cabal: ["latest"]
cabal: ["3.2"]
ghc:
- "8.6.5"
- "8.8.3"
@@ -33,7 +33,7 @@ jobs:
- uses: actions/checkout@v2
if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'
- uses: haskell/actions/setup@v1
- uses: actions/setup-haskell@v1.1.4
id: setup-haskell-cabal
name: Setup Haskell
with:
@@ -65,7 +65,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
stack: ["latest"]
stack: ["2.3.3"]
ghc: ["7.10.3","8.0.2", "8.2.2", "8.4.4", "8.6.5", "8.8.4"]
# ghc: ["8.8.3"]
@@ -73,12 +73,11 @@ jobs:
- uses: actions/checkout@v2
if: github.event.action == 'opened' || github.event.action == 'synchronize' || github.event.ref == 'refs/heads/master'
- uses: haskell/actions/setup@v1
- uses: actions/setup-haskell@v1.1.4
name: Setup Haskell Stack
with:
ghc-version: ${{ matrix.ghc }}
stack-version: 'latest'
enable-stack: true
# ghc-version: ${{ matrix.ghc }}
stack-version: ${{ matrix.stack }}
- uses: actions/cache@v1
name: Cache ~/.stack
@@ -91,6 +90,6 @@ jobs:
stack build --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml
# stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks
- name: Test
run: |
stack test --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml
# - name: Test
# run: |
# stack test --system-ghc

View File

@@ -2,8 +2,7 @@ name: Build Binary Packages
on:
workflow_dispatch:
release:
types: ["created"]
release:
jobs:
@@ -11,13 +10,11 @@ jobs:
ubuntu:
name: Build Ubuntu package
strategy:
matrix:
os:
- ubuntu-18.04
- ubuntu-20.04
runs-on: ${{ matrix.os }}
runs-on: ubuntu-18.04
# strategy:
# matrix:
# ghc: ["8.6.5"]
# cabal: ["2.4"]
steps:
- uses: actions/checkout@v2
@@ -56,33 +53,19 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: gf-${{ github.event.release.tag_name }}-${{ matrix.os }}.deb
name: gf-${{ github.sha }}-ubuntu
path: dist/gf_*.deb
if-no-files-found: error
- name: Rename package for specific ubuntu version
run: |
mv dist/gf_*.deb dist/gf-${{ github.event.release.tag_name }}-${{ matrix.os }}.deb
- uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: dist/gf-${{ github.event.release.tag_name }}-${{ matrix.os }}.deb
asset_name: gf-${{ github.event.release.tag_name }}-${{ matrix.os }}.deb
asset_content_type: application/octet-stream
# ---
macos:
name: Build macOS package
runs-on: macos-10.15
strategy:
matrix:
ghc: ["8.6.5"]
cabal: ["2.4"]
os: ["macos-10.15"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
@@ -109,33 +92,19 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: gf-${{ github.event.release.tag_name }}-macos
name: gf-${{ github.sha }}-macos
path: dist/gf-*.pkg
if-no-files-found: error
- name: Rename package
run: |
mv dist/gf-*.pkg dist/gf-${{ github.event.release.tag_name }}-macos.pkg
- uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: dist/gf-${{ github.event.release.tag_name }}-macos.pkg
asset_name: gf-${{ github.event.release.tag_name }}-macos.pkg
asset_content_type: application/octet-stream
# ---
windows:
name: Build Windows package
runs-on: windows-2019
strategy:
matrix:
ghc: ["8.6.5"]
cabal: ["2.4"]
os: ["windows-2019"]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
@@ -167,18 +136,16 @@ jobs:
cp /mingw64/bin/libpgf-0.dll /c/tmp-dist/c
cp /mingw64/bin/libgu-0.dll /c/tmp-dist/c
# JAVA_HOME_8_X64 = C:\hostedtoolcache\windows\Java_Adopt_jdk\8.0.292-10\x64
- name: Build Java bindings
shell: msys2 {0}
run: |
export JDKPATH=/c/hostedtoolcache/windows/Java_Adopt_jdk/8.0.292-10/x64
export PATH="${PATH}:${JDKPATH}/bin"
export PATH="${PATH}:/c/Program Files/Java/jdk8u275-b01/bin"
cd src/runtime/java
make \
JNI_INCLUDES="-I \"${JDKPATH}/include\" -I \"${JDKPATH}/include/win32\" -I \"/mingw64/include\" -D__int64=int64_t" \
JNI_INCLUDES="-I \"/c/Program Files/Java/jdk8u275-b01/include\" -I \"/c/Program Files/Java/jdk8u275-b01/include/win32\" -I \"/mingw64/include\" -D__int64=int64_t" \
WINDOWS_LDFLAGS="-L\"/mingw64/lib\" -no-undefined"
make install
cp .libs/msys-jpgf-0.dll /c/tmp-dist/java/jpgf.dll
cp .libs//msys-jpgf-0.dll /c/tmp-dist/java/jpgf.dll
cp jpgf.jar /c/tmp-dist/java
- name: Build Python bindings
@@ -190,7 +157,7 @@ jobs:
cd src/runtime/python
python setup.py build
python setup.py install
cp /usr/lib/python3.9/site-packages/pgf* /c/tmp-dist/python
cp /usr/lib/python3.8/site-packages/pgf* /c/tmp-dist/python
- name: Setup Haskell
uses: actions/setup-haskell@v1
@@ -213,18 +180,6 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v2
with:
name: gf-${{ github.event.release.tag_name }}-windows
name: gf-${{ github.sha }}-windows
path: C:\tmp-dist\*
if-no-files-found: error
- name: Create archive
run: |
Compress-Archive C:\tmp-dist C:\gf-${{ github.event.release.tag_name }}-windows.zip
- uses: actions/upload-release-asset@v1.0.2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: C:\gf-${{ github.event.release.tag_name }}-windows.zip
asset_name: gf-${{ github.event.release.tag_name }}-windows.zip
asset_content_type: application/zip

View File

@@ -1,162 +0,0 @@
name: Build majestic runtime
on: push
env:
LD_LIBRARY_PATH: /usr/local/lib
jobs:
ubuntu-runtime:
name: Runtime (Ubuntu)
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Build runtime
working-directory: ./src/runtime/c
run: |
autoreconf -i
./configure
make
sudo make install
- name: Upload artifact
uses: actions/upload-artifact@master
with:
name: libpgf-ubuntu
path: |
/usr/local/lib/libpgf*
/usr/local/include/pgf
ubuntu-haskell:
name: Haskell (Ubuntu)
runs-on: ubuntu-20.04
needs: ubuntu-runtime
steps:
- uses: actions/checkout@v2
- name: Download artifact
uses: actions/download-artifact@master
with:
name: libpgf-ubuntu
- run: |
sudo mv lib/* /usr/local/lib/
sudo mv include/* /usr/local/include/
- name: Setup Haskell
uses: haskell/actions/setup@v1
- name: Build & run testsuite
working-directory: ./src/runtime/haskell
run: |
cabal test --extra-lib-dirs=/usr/local/lib
ubuntu-python:
name: Python (Ubuntu)
runs-on: ubuntu-20.04
needs: ubuntu-runtime
steps:
- uses: actions/checkout@v2
- name: Download artifact
uses: actions/download-artifact@master
with:
name: libpgf-ubuntu
- run: |
sudo mv lib/* /usr/local/lib/
sudo mv include/* /usr/local/include/
- name: Install bindings
working-directory: ./src/runtime/python
run: |
python setup.py build
sudo python setup.py install
- name: Run testsuite
working-directory: ./src/runtime/python
run: |
pip install pytest
pytest
ubuntu-javascript:
name: JavaScript (Ubuntu)
runs-on: ubuntu-20.04
needs: ubuntu-runtime
steps:
- uses: actions/checkout@v2
- name: Download artifact
uses: actions/download-artifact@master
with:
name: libpgf-ubuntu
- run: |
sudo mv lib/* /usr/local/lib/
sudo mv include/* /usr/local/include/
- name: Install dependencies
working-directory: ./src/runtime/javascript
run: |
npm ci
- name: Run testsuite
working-directory: ./src/runtime/javascript
run: |
npm run test
# ----------------------------------------------------------------------------
macos-runtime:
name: Runtime (macOS)
runs-on: macOS-11
steps:
- uses: actions/checkout@v2
- name: Install build tools
run: |
brew install \
autoconf \
automake \
libtool \
- name: Build runtime
working-directory: ./src/runtime/c
run: |
glibtoolize
autoreconf -i
./configure
make
sudo make install
- name: Upload artifact
uses: actions/upload-artifact@master
with:
name: libpgf-macos
path: |
/usr/local/lib/libpgf*
/usr/local/include/pgf
macos-haskell:
name: Haskell (macOS)
runs-on: macOS-11
needs: macos-runtime
steps:
- uses: actions/checkout@v2
- name: Download artifact
uses: actions/download-artifact@master
with:
name: libpgf-macos
- run: |
sudo mv lib/* /usr/local/lib/
sudo mv include/* /usr/local/include/
- name: Setup Haskell
uses: haskell/actions/setup@v1
- name: Build & run testsuite
working-directory: ./src/runtime/haskell
run: |
cabal test --extra-lib-dirs=/usr/local/lib

View File

@@ -25,7 +25,7 @@ jobs:
- name: Install cibuildwheel
run: |
python -m pip install git+https://github.com/joerick/cibuildwheel.git@main
python -m pip install git+https://github.com/joerick/cibuildwheel.git@master
- name: Install build tools for OSX
if: startsWith(matrix.os, 'macos')

8
.gitignore vendored
View File

@@ -5,7 +5,7 @@
*.jar
*.gfo
*.pgf
*.ngf
*.lpgf
debian/.debhelper
debian/debhelper-build-stamp
debian/gf
@@ -47,8 +47,6 @@ src/runtime/c/sg/.dirstamp
src/runtime/c/stamp-h1
src/runtime/java/.libs/
src/runtime/python/build/
src/runtime/python/**/__pycache__/
src/runtime/python/**/.pytest_cache/
.cabal-sandbox
cabal.sandbox.config
.stack-work
@@ -56,10 +54,6 @@ DATA_DIR
stack*.yaml.lock
# Output files for test suite
*.out
gf-tests.html
# Generated documentation (not exhaustive)
demos/index-numbers.html
demos/resourcegrammars.html

14
.travis.yml Normal file
View File

@@ -0,0 +1,14 @@
sudo: required
language: c
services:
- docker
before_install:
- docker pull odanoburu/gf-src:3.9
script:
- |
docker run --mount src="$(pwd)",target=/home/gfer,type=bind odanoburu/gf-src:3.9 /bin/bash -c "cd /home/gfer/src/runtime/c &&
autoreconf -i && ./configure && make && make install ; cd /home/gfer ; cabal install -fserver -fc-runtime --extra-lib-dirs='/usr/local/lib'"

View File

@@ -1,11 +0,0 @@
### New since 3.11 (WIP)
- Added a changelog!
### 3.11
See <https://www.grammaticalframework.org/download/release-3.11.html>
### 3.10
See <https://www.grammaticalframework.org/download/release-3.10.html>

View File

@@ -1,48 +1,31 @@
.PHONY: all build install doc clean html deb pkg bintar sdist
.PHONY: all build install doc clean gf html deb pkg bintar sdist
# This gets the numeric part of the version from the cabal file
VERSION=$(shell sed -ne "s/^version: *\([0-9.]*\).*/\1/p" gf.cabal)
# Check if stack is installed
STACK=$(shell if hash stack 2>/dev/null; then echo "1"; else echo "0"; fi)
# Check if cabal >= 2.4 is installed (with v1- and v2- commands)
CABAL_NEW=$(shell if cabal v1-repl --help >/dev/null 2>&1 ; then echo "1"; else echo "0"; fi)
ifeq ($(STACK),1)
CMD=stack
else
CMD=cabal
ifeq ($(CABAL_NEW),1)
CMD_PFX=v1-
endif
endif
all: build
dist/setup-config: gf.cabal Setup.hs WebSetup.hs
ifneq ($(STACK),1)
cabal ${CMD_PFX}configure
endif
cabal configure
build: dist/setup-config
${CMD} ${CMD_PFX}build
cabal build
install:
ifeq ($(STACK),1)
stack install
else
cabal ${CMD_PFX}copy
cabal ${CMD_PFX}register
endif
cabal copy
cabal register
doc:
${CMD} ${CMD_PFX}haddock
cabal haddock
clean:
${CMD} ${CMD_PFX}clean
cabal clean
bash bin/clean_html
gf:
cabal build rgl-none
strip dist/build/gf/gf
html::
bash bin/update_html
@@ -52,7 +35,7 @@ html::
deb:
dpkg-buildpackage -b -uc
# Make a macOS installer package
# Make an OS X Installer package
pkg:
FMT=pkg bash bin/build-binary-dist.sh
@@ -65,6 +48,6 @@ bintar:
# Make a source tar.gz distribution using git to make sure that everything is included.
# We put the distribution in dist/ so it is removed on `make clean`
# sdist:
# test -d dist || mkdir dist
# git archive --format=tar.gz --output=dist/gf-${VERSION}.tar.gz HEAD
sdist:
test -d dist || mkdir dist
git archive --format=tar.gz --output=dist/gf-${VERSION}.tar.gz HEAD

View File

@@ -1,4 +1,4 @@
![GF Logo](https://www.grammaticalframework.org/doc/Logos/gf1.svg)
![GF Logo](doc/Logos/gf1.svg)
# Grammatical Framework (GF)
@@ -30,16 +30,13 @@ GF particularly addresses four aspects of grammars:
## Compilation and installation
The simplest way of installing GF from source is with the command:
The simplest way of installing GF is with the command:
```
cabal install
```
or:
```
stack install
```
For more information, including links to precompiled binaries, see the [download page](https://www.grammaticalframework.org/download/index.html).
For more details, see the [download page](http://www.grammaticalframework.org/download/index.html)
and [developers manual](http://www.grammaticalframework.org/doc/gf-developers.html).
## About this repository

View File

@@ -45,16 +45,11 @@ but the generated _artifacts_ must be manually attached to the release as _asset
### 4. Upload to Hackage
In order to do this you will need to be added the [GF maintainers](https://hackage.haskell.org/package/gf/maintainers/) on Hackage.
1. Run `stack sdist --test-tarball` and address any issues.
1. Run `make sdist`
2. Upload the package, either:
1. **Manually**: visit <https://hackage.haskell.org/upload> and upload the file generated by the previous command.
2. **via Stack**: `stack upload . --candidate`
3. After testing the candidate, publish it:
1. **Manually**: visit <https://hackage.haskell.org/package/gf-X.Y.Z/candidate/publish>
1. **via Stack**: `stack upload .`
4. If the documentation-building fails on the Hackage server, do:
1. **Manually**: visit <https://hackage.haskell.org/upload> and upload the file `dist/gf-X.Y.tar.gz`
2. **via Cabal (≥2.4)**: `cabal upload dist/gf-X.Y.tar.gz`
3. If the documentation-building fails on the Hackage server, do:
```
cabal v2-haddock --builddir=dist/docs --haddock-for-hackage --enable-doc
cabal upload --documentation dist/docs/*-docs.tar.gz

View File

@@ -26,14 +26,6 @@ import Distribution.PackageDescription(PackageDescription(..))
so users won't see this message unless they check the log.)
-}
-- | Notice about contrib grammars
noContribMsg :: IO ()
noContribMsg = putStr $ unlines
[ "Example grammars are no longer included in the main GF repository, but have moved to gf-contrib."
, "If you want them to be built, clone the following repository in the same directory as gf-core:"
, "https://github.com/GrammaticalFramework/gf-contrib.git"
]
example_grammars :: [(String, String, [String])] -- [(pgf, subdir, source modules)]
example_grammars =
[("Letter.pgf","letter",letterSrc)
@@ -58,8 +50,11 @@ buildWeb gf flags (pkg,lbi) = do
contrib_exists <- doesDirectoryExist contrib_dir
if contrib_exists
then mapM_ build_pgf example_grammars
-- else noContribMsg
else return ()
else putStr $ unlines
[ "Example grammars are no longer included in the main GF repository, but have moved to gf-contrib."
, "If you want these example grammars to be built, clone this repository in the same top-level directory as GF:"
, "https://github.com/GrammaticalFramework/gf-contrib.git"
]
where
gfo_dir = buildDir lbi </> "examples"

6
debian/changelog vendored
View File

@@ -1,9 +1,3 @@
gf (3.11) bionic focal; urgency=low
* GF 3.11
-- Inari Listenmaa <inari@digitalgrammars.com> Sun, 25 Jul 2021 10:27:40 +0800
gf (3.10.4-1) xenial bionic cosmic; urgency=low
* GF 3.10.4

10
debian/rules vendored
View File

@@ -16,9 +16,9 @@ override_dh_shlibdeps:
override_dh_auto_configure:
cd src/runtime/c && bash setup.sh configure --prefix=/usr
cd src/runtime/c && bash setup.sh build
cabal v1-update
cabal v1-install --only-dependencies
cabal v1-configure --prefix=/usr -fserver -fc-runtime --extra-lib-dirs=$(CURDIR)/src/runtime/c/.libs --extra-include-dirs=$(CURDIR)/src/runtime/c
cabal update
cabal install --only-dependencies
cabal configure --prefix=/usr -fserver -fc-runtime --extra-lib-dirs=$(CURDIR)/src/runtime/c/.libs --extra-include-dirs=$(CURDIR)/src/runtime/c
SET_LDL=LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:$(CURDIR)/src/runtime/c/.libs
@@ -26,10 +26,10 @@ override_dh_auto_build:
cd src/runtime/python && EXTRA_INCLUDE_DIRS=$(CURDIR)/src/runtime/c EXTRA_LIB_DIRS=$(CURDIR)/src/runtime/c/.libs python setup.py build
cd src/runtime/java && make CFLAGS="-I$(CURDIR)/src/runtime/c -L$(CURDIR)/src/runtime/c/.libs" INSTALL_PATH=/usr
echo $(SET_LDL)
-$(SET_LDL) cabal v1-build
-$(SET_LDL) cabal build
override_dh_auto_install:
$(SET_LDL) cabal v1-copy --destdir=$(CURDIR)/debian/gf
$(SET_LDL) cabal copy --destdir=$(CURDIR)/debian/gf
cd src/runtime/c && bash setup.sh copy prefix=$(CURDIR)/debian/gf/usr
cd src/runtime/python && python setup.py install --prefix=$(CURDIR)/debian/gf/usr
cd src/runtime/java && make INSTALL_PATH=$(CURDIR)/debian/gf/usr install

View File

@@ -1,201 +0,0 @@
GF Developer's Guide: Old installation instructions with Cabal
This page contains the old installation instructions from the [Developer's Guide ../doc/gf-developers.html].
We recommend Stack as a primary installation method, because it's easier for a Haskell beginner, and we want to keep the main instructions short.
But if you are an experienced Haskeller and want to keep using Cabal, here are the old instructions using ``cabal install``.
Note that some of these instructions may be outdated. Other parts may still be useful.
== Compilation from source with Cabal ==
The build system of GF is based on //Cabal//, which is part of the
Haskell Platform, so no extra steps are needed to install it. In the simplest
case, all you need to do to compile and install GF, after downloading the
source code as described above, is
```
$ cabal install
```
This will automatically download any additional Haskell libraries needed to
build GF. If this is the first time you use Cabal, you might need to run
``cabal update`` first, to update the list of available libraries.
If you want more control, the process can also be split up into the usual
//configure//, //build// and //install// steps.
=== Configure ===
During the configuration phase Cabal will check that you have all
necessary tools and libraries needed for GF. The configuration is
started by the command:
```
$ cabal configure
```
If you don't see any error message from the above command then you
have everything that is needed for GF. You can also add the option
``-v`` to see more details about the configuration.
You can use ``cabal configure --help`` to get a list of configuration options.
=== Build ===
The build phase does two things. First it builds the GF compiler from
the Haskell source code and after that it builds the GF Resource Grammar
Library using the already build compiler. The simplest command is:
```
$ cabal build
```
Again you can add the option ``-v`` if you want to see more details.
==== Parallel builds ====
If you have Cabal>=1.20 you can enable parallel compilation by using
```
$ cabal build -j
```
or by putting a line
```
jobs: $ncpus
```
in your ``.cabal/config`` file. Cabal
will pass this option to GHC when building the GF compiler, if you
have GHC>=7.8.
Cabal also passes ``-j`` to GF to enable parallel compilation of the
Resource Grammar Library. This is done unconditionally to avoid
causing problems for developers with Cabal<1.20. You can disable this
by editing the last few lines in ``WebSetup.hs``.
=== Install ===
After you have compiled GF you need to install the executable and libraries
to make the system usable.
```
$ cabal copy
$ cabal register
```
This command installs the GF compiler for a single user, in the standard
place used by Cabal.
On Linux and Mac this could be ``$HOME/.cabal/bin``.
On Mac it could also be ``$HOME/Library/Haskell/bin``.
On Windows this is ``C:\Program Files\Haskell\bin``.
The compiled GF Resource Grammar Library will be installed
under the same prefix, e.g. in
``$HOME/.cabal/share/gf-3.3.3/lib`` on Linux and
in ``C:\Program Files\Haskell\gf-3.3.3\lib`` on Windows.
If you want to install in some other place then use the ``--prefix``
option during the configuration phase.
=== Clean ===
Sometimes you want to clean up the compilation and start again from clean
sources. Use the clean command for this purpose:
```
$ cabal clean
```
%=== SDist ===
%
%You can use the command:
%
%% This does *NOT* include everything that is needed // TH 2012-08-06
%```
%$ cabal sdist
%```
%
%to prepare archive with all source codes needed to compile GF.
=== Known problems with Cabal ===
Some versions of Cabal (at least version 1.16) seem to have a bug that can
cause the following error:
```
Configuring gf-3.x...
setup: Distribution/Simple/PackageIndex.hs:124:8-13: Assertion failed
```
The exact cause of this problem is unclear, but it seems to happen
during the configure phase if the same version of GF is already installed,
so a workaround is to remove the existing installation with
```
ghc-pkg unregister gf
```
You can check with ``ghc-pkg list gf`` that it is gone.
== Compilation with make ==
If you feel more comfortable with Makefiles then there is a thin Makefile
wrapper arround Cabal for you. If you just type:
```
$ make
```
the configuration phase will be run automatically if needed and after that
the sources will be compiled.
%% cabal build rgl-none does not work with recent versions of Cabal
%If you don't want to compile the resource library
%every time then you can use:
%```
%$ make gf
%```
For installation use:
```
$ make install
```
For cleaning:
```
$ make clean
```
%and to build source distribution archive run:
%```
%$ make sdist
%```
== Partial builds of RGL ==
**NOTE**: The following doesn't work with recent versions of ``cabal``. //(This comment was left in 2015, so make your own conclusions.)//
%% // TH 2015-06-22
%Sometimes you just want to work on the GF compiler and don't want to
%recompile the resource library after each change. In this case use
%this extended command:
%```
%$ cabal build rgl-none
%```
The resource grammar library can be compiled in two modes: with present
tense only and with all tenses. By default it is compiled with all
tenses. If you want to use the library with only present tense you can
compile it in this special mode with the command:
```
$ cabal build present
```
You could also control which languages you want to be recompiled by
adding the option ``langs=list``. For example the following command
will compile only the English and the Swedish language:
```
$ cabal build langs=Eng,Swe
```

View File

@@ -1,6 +1,6 @@
GF Developers Guide
2021-07-15
2018-07-26
%!options(html): --toc
@@ -15,287 +15,388 @@ you are a GF user who just wants to download and install GF
== Setting up your system for building GF ==
To build GF from source you need to install some tools on your
system: the Haskell build tool //Stack//, the version control software //Git// and the //Haskeline// library.
system: the //Haskell Platform//, //Git// and the //Haskeline library//.
%**On Linux** the best option is to install the tools via the standard
%software distribution channels, i.e. by using the //Software Center//
%in Ubuntu or the corresponding tool in other popular Linux distributions.
**On Linux** the best option is to install the tools via the standard
software distribution channels, i.e. by using the //Software Center//
in Ubuntu or the corresponding tool in other popular Linux distributions.
Or, from a Terminal window, the following command should be enough:
%**On Mac OS and Windows**, the tools can be downloaded from their respective
%web sites, as described below.
=== Stack ===
The primary installation method is via //Stack//.
(You can also use Cabal, but we recommend Stack to those who are new to Haskell.)
To install Stack:
- **On Linux and Mac OS**, do either
``$ curl -sSL https://get.haskellstack.org/ | sh``
or
``$ wget -qO- https://get.haskellstack.org/ | sh``
- On Ubuntu: ``sudo apt-get install haskell-platform git libghc6-haskeline-dev``
- On Fedora: ``sudo dnf install haskell-platform git ghc-haskeline-devel``
- **On other operating systems**, see the [installation guide https://docs.haskellstack.org/en/stable/install_and_upgrade].
**On Mac OS and Windows**, the tools can be downloaded from their respective
web sites, as described below.
=== The Haskell Platform ===
%If you already have Stack installed, upgrade it to the latest version by running: ``stack upgrade``
GF is written in Haskell, so first of all you need
the //Haskell Platform//, e.g. version 8.0.2 or 7.10.3. Downloads
and installation instructions are available from here:
http://hackage.haskell.org/platform/
Once you have installed the Haskell Platform, open a terminal
(Command Prompt on Windows) and try to execute the following command:
```
$ ghc --version
```
This command should show you which version of GHC you have. If the installation
of the Haskell Platform was successful you should see a message like:
```
The Glorious Glasgow Haskell Compilation System, version 8.0.2
```
Other required tools included in the Haskell Platform are
[Cabal http://www.haskell.org/cabal/],
[Alex http://www.haskell.org/alex/]
and
[Happy http://www.haskell.org/happy/].
=== Git ===
To get the GF source code, you also need //Git//, a distributed version control system.
To get the GF source code, you also need //Git//.
//Git// is a distributed version control system, see
https://git-scm.com/downloads for more information.
- **On Linux**, the best option is to install the tools via the standard
software distribution channels:
- On Ubuntu: ``sudo apt-get install git-all``
- On Fedora: ``sudo dnf install git-all``
- **On other operating systems**, see
https://git-scm.com/book/en/v2/Getting-Started-Installing-Git for installation.
=== Haskeline ===
=== The haskeline library ===
GF uses //haskeline// to enable command line editing in the GF shell.
This should work automatically on Mac OS and Windows, but on Linux one
extra step is needed to make sure the C libraries (terminfo)
required by //haskeline// are installed. Here is one way to do this:
- **On Mac OS and Windows**, this should work automatically.
- **On Linux**, an extra step is needed to make sure the C libraries (terminfo)
required by //haskeline// are installed:
- On Ubuntu: ``sudo apt-get install libghc-haskeline-dev``
- On Fedora: ``sudo dnf install ghc-haskeline-devel``
- On Ubuntu: ``sudo apt-get install libghc-haskeline-dev``
- On Fedora: ``sudo dnf install ghc-haskeline-devel``
== Getting the source ==[getting-source]
== Getting the source ==
Once you have all tools in place you can get the GF source code from
[GitHub https://github.com/GrammaticalFramework/]:
Once you have all tools in place you can get the GF source code. If you
just want to compile and use GF then it is enough to have read-only
access. It is also possible to make changes in the source code but if you
want these changes to be applied back to the main source repository you will
have to send the changes to us. If you plan to work continuously on
GF then you should consider getting read-write access.
- https://github.com/GrammaticalFramework/gf-core for the GF compiler
- https://github.com/GrammaticalFramework/gf-rgl for the Resource Grammar Library
=== Read-only access ===
==== Getting a fresh copy for read-only access ====
=== Read-only access: clone the main repository ===
If you only want to compile and use GF, you can just clone the repositories as follows:
Anyone can get the latest development version of GF by running:
```
$ git clone https://github.com/GrammaticalFramework/gf-core.git
$ git clone https://github.com/GrammaticalFramework/gf-rgl.git
$ git clone https://github.com/GrammaticalFramework/gf-core.git
$ git clone https://github.com/GrammaticalFramework/gf-rgl.git
```
To get new updates, run the following anywhere in your local copy of the repository:
This will create directories ``gf-core`` and ``gf-rgl`` in the current directory.
==== Updating your copy ====
To get all new patches from each repo:
```
$ git pull
```
This can be done anywhere in your local repository.
==== Recording local changes ====[record]
Since every copy is a repository, you can have local version control
of your changes.
If you have added files, you first need to tell your local repository to
keep them under revision control:
```
$ git pull
$ git add file1 file2 ...
```
=== Contribute your changes: fork the main repository ===
If you want the possibility to contribute your changes,
you should create your own fork, do your changes there,
and then send a pull request to the main repository.
+ **Creating and cloning a fork —**
See GitHub documentation for instructions how to [create your own fork https://docs.github.com/en/get-started/quickstart/fork-a-repo]
of the repository. Once you've done it, clone the fork to your local computer.
To record changes, use:
```
$ git clone https://github.com/<YOUR_USERNAME>/gf-core.git
$ git commit file1 file2 ...
```
+ **Updating your copy —**
Once you have cloned your fork, you need to set up the main repository as a remote:
This creates a patch against the previous version and stores it in your
local repository. You can record any number of changes before
pushing them to the main repo. In fact, you don't have to push them at
all if you want to keep the changes only in your local repo.
Instead of enumerating all modified files on the command line,
you can use the flag ``-a`` to automatically record //all// modified
files. You still need to use ``git add`` to add new files.
=== Read-write access ===
If you are a member of the GF project on GitHub, you can push your
changes directly to the GF git repository on GitHub.
```
$ git remote add upstream https://github.com/GrammaticalFramework/gf-core.git
$ git push
```
Then you can get the latest updates by running the following:
It is also possible for anyone else to contribute by
```
$ git pull upstream master
```
+ **Recording local changes —**
See Git tutorial on how to [record and push your changes https://git-scm.com/book/en/v2/Git-Basics-Recording-Changes-to-the-Repository] to your fork.
+ **Pull request —**
When you want to contribute your changes to the main gf-core repository,
[create a pull request https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request]
from your fork.
- creating a fork of the GF repository on GitHub,
- working with local clone of the fork (obtained with ``git clone``),
- pushing changes to the fork,
- and finally sending a pull request.
If you want to contribute to the RGL as well, do the same process for the RGL repository.
== Compilation from source with Cabal ==
== Compilation from source ==
By now you should have installed Stack and Haskeline, and cloned the Git repository on your own computer, in a directory called ``gf-core``.
=== Primary recommendation: use Stack ===
Open a terminal, go to the top directory (``gf-core``), and type the following command.
```
$ stack install
```
It will install GF and all necessary tools and libraries to do that.
=== Alternative: use Cabal ===
You can also install GF using Cabal, if you prefer Cabal to Stack. In that case, you may need to install some prerequisites yourself.
The actual installation process is similar to Stack: open a terminal, go to the top directory (``gf-core``), and type the following command.
The build system of GF is based on //Cabal//, which is part of the
Haskell Platform, so no extra steps are needed to install it. In the simplest
case, all you need to do to compile and install GF, after downloading the
source code as described above, is
```
$ cabal install
```
//The old (potentially outdated) instructions for Cabal are moved to a [separate page ../doc/gf-developers-old-cabal.html]. If you run into trouble with ``cabal install``, you may want to take a look.//
This will automatically download any additional Haskell libraries needed to
build GF. If this is the first time you use Cabal, you might need to run
``cabal update`` first, to update the list of available libraries.
== Compiling GF with C runtime system support ==
If you want more control, the process can also be split up into the usual
//configure//, //build// and //install// steps.
The C runtime system is a separate implementation of the PGF runtime services.
=== Configure ===
During the configuration phase Cabal will check that you have all
necessary tools and libraries needed for GF. The configuration is
started by the command:
```
$ cabal configure
```
If you don't see any error message from the above command then you
have everything that is needed for GF. You can also add the option
``-v`` to see more details about the configuration.
You can use ``cabal configure --help`` to get a list of configuration options.
=== Build ===
The build phase does two things. First it builds the GF compiler from
the Haskell source code and after that it builds the GF Resource Grammar
Library using the already build compiler. The simplest command is:
```
$ cabal build
```
Again you can add the option ``-v`` if you want to see more details.
==== Parallel builds ====
If you have Cabal>=1.20 you can enable parallel compilation by using
```
$ cabal build -j
```
or by putting a line
```
jobs: $ncpus
```
in your ``.cabal/config`` file. Cabal
will pass this option to GHC when building the GF compiler, if you
have GHC>=7.8.
Cabal also passes ``-j`` to GF to enable parallel compilation of the
Resource Grammar Library. This is done unconditionally to avoid
causing problems for developers with Cabal<1.20. You can disable this
by editing the last few lines in ``WebSetup.hs``.
==== Partial builds ====
**NOTE**: The following doesn't work with recent versions of ``cabal``.
%% // TH 2015-06-22
Sometimes you just want to work on the GF compiler and don't want to
recompile the resource library after each change. In this case use
this extended command:
```
$ cabal build rgl-none
```
The resource library could also be compiled in two modes: with present
tense only and with all tenses. By default it is compiled with all
tenses. If you want to use the library with only present tense you can
compile it in this special mode with the command:
```
$ cabal build present
```
You could also control which languages you want to be recompiled by
adding the option ``langs=list``. For example the following command
will compile only the English and the Swedish language:
```
$ cabal build langs=Eng,Swe
```
=== Install ===
After you have compiled GF you need to install the executable and libraries
to make the system usable.
```
$ cabal copy
$ cabal register
```
This command installs the GF compiler for a single user, in the standard
place used by Cabal.
On Linux and Mac this could be ``$HOME/.cabal/bin``.
On Mac it could also be ``$HOME/Library/Haskell/bin``.
On Windows this is ``C:\Program Files\Haskell\bin``.
The compiled GF Resource Grammar Library will be installed
under the same prefix, e.g. in
``$HOME/.cabal/share/gf-3.3.3/lib`` on Linux and
in ``C:\Program Files\Haskell\gf-3.3.3\lib`` on Windows.
If you want to install in some other place then use the ``--prefix``
option during the configuration phase.
=== Clean ===
Sometimes you want to clean up the compilation and start again from clean
sources. Use the clean command for this purpose:
```
$ cabal clean
```
%=== SDist ===
%
%You can use the command:
%
%% This does *NOT* include everything that is needed // TH 2012-08-06
%```
%$ cabal sdist
%```
%
%to prepare archive with all source codes needed to compile GF.
=== Known problems with Cabal ===
Some versions of Cabal (at least version 1.16) seem to have a bug that can
cause the following error:
```
Configuring gf-3.x...
setup: Distribution/Simple/PackageIndex.hs:124:8-13: Assertion failed
```
The exact cause of this problem is unclear, but it seems to happen
during the configure phase if the same version of GF is already installed,
so a workaround is to remove the existing installation with
```
ghc-pkg unregister gf
```
You can check with ``ghc-pkg list gf`` that it is gone.
== Compilation with make ==
If you feel more comfortable with Makefiles then there is a thin Makefile
wrapper arround Cabal for you. If you just type:
```
$ make
```
the configuration phase will be run automatically if needed and after that
the sources will be compiled.
%% cabal build rgl-none does not work with recent versions of Cabal
%If you don't want to compile the resource library
%every time then you can use:
%```
%$ make gf
%```
For installation use:
```
$ make install
```
For cleaning:
```
$ make clean
```
%and to build source distribution archive run:
%```
%$ make sdist
%```
== Compiling GF with C run-time system support ==
The C run-time system is a separate implementation of the PGF run-time services.
It makes it possible to work with very large, ambiguous grammars, using
probabilistic models to obtain probable parses. The C runtime system might
also be easier to use than the Haskell runtime system on certain platforms,
probabilistic models to obtain probable parses. The C run-time system might
also be easier to use than the Haskell run-time system on certain platforms,
e.g. Android and iOS.
To install the C runtime system, go to the ``src/runtime/c`` directory.
To install the C run-time system, go to the ``src/runtime/c`` directory
%and follow the instructions in the ``INSTALL`` file.
and use the ``install.sh`` script:
```
bash setup.sh configure
bash setup.sh build
bash setup.sh install
```
This will install
the C header files and libraries need to write C programs that use PGF grammars.
Some example C programs are included in the ``utils`` subdirectory, e.g.
``pgf-translate.c``.
- **On Linux and Mac OS —**
You should have autoconf, automake, libtool and make.
If you are missing some of them, follow the
instructions in the [INSTALL https://github.com/GrammaticalFramework/gf-core/blob/master/src/runtime/c/INSTALL] file.
Once you have the required libraries, the easiest way to install the C runtime is to use the ``install.sh`` script. Just type
``$ bash install.sh``
This will install the C header files and libraries need to write C programs
that use PGF grammars.
% If this doesn't work for you, follow the manual instructions in the [INSTALL https://github.com/GrammaticalFramework/gf-core/blob/master/src/runtime/c/INSTALL] file under your operating system.
- **On other operating systems —** Follow the instructions in the
[INSTALL https://github.com/GrammaticalFramework/gf-core/blob/master/src/runtime/c/INSTALL] file under your operating system.
Depending on what you want to do with the C runtime, you can follow one or more of the following steps.
=== Use the C runtime from another programming language ===[bindings]
% **If you just want to use the C runtime from Python, Java, or Haskell, you don't need to change your GF installation.**
- **What —**
This is the most common use case for the C runtime: compile
your GF grammars into PGF with the standard GF executable,
and manipulate the PGFs from another programming language,
using the bindings to the C runtime.
- **How —**
The Python, Java and Haskell bindings are found in the
``src/runtime/{python,java,haskell-bind}`` directories,
respecively. Compile them by following the instructions
in the ``INSTALL`` or ``README`` files in those directories.
The Python library can also be installed from PyPI using ``pip install pgf``.
//If you are on Mac and get an error about ``clang`` version, you can try some of [these solutions https://stackoverflow.com/questions/63972113/big-sur-clang-invalid-version-error-due-to-macosx-deployment-target]—but be careful before removing any existing installations.//
=== Use GF shell with C runtime support ===
- **What —**
If you want to use the GF shell with C runtime functionalities, then you need to (re)compile GF with special flags.
The GF shell can be started with ``gf -cshell`` or ``gf -crun`` to use
the C run-time system instead of the Haskell run-time system.
Only limited functionality is available when running the shell in these
modes (use the ``help`` command in the shell for details).
(Re)compiling your GF with these flags will also give you
Haskell bindings to the C runtime, as a library called ``PGF2``,
but if you want Python or Java bindings, you need to do [the previous step #bindings].
% ``PGF2``: a module to import in Haskell programs, providing a binding to the C run-time system.
- **How —**
If you use cabal, run the following command:
When the C run-time system is installed, you can install GF with C run-time
support by doing
```
cabal install -fc-runtime
cabal install -fserver -fc-runtime
```
from the top directory. This give you three new things:
from the top directory (``gf-core``).
- ``PGF2``: a module to import in Haskell programs, providing a binding to
the C run-time system.
If you use stack, uncomment the following lines in the ``stack.yaml`` file:
- The GF shell can be started with ``gf -cshell`` or ``gf -crun`` to use
the C run-time system instead of the Haskell run-time system.
Only limited functionality is available when running the shell in these
modes (use the ``help`` command in the shell for details).
```
flags:
gf:
c-runtime: true
extra-lib-dirs:
- /usr/local/lib
```
and then run ``stack install`` from the top directory (``gf-core``).
- ``gf -server`` mode is extended with new requests to call the C run-time
system, e.g. ``c-parse``, ``c-linearize`` and ``c-translate``.
//If you get an "``error while loading shared libraries``" when trying to run GF with C runtime, remember to declare your ``LD_LIBRARY_PATH``.//
//Add ``export LD_LIBRARY_PATH="/usr/local/lib"`` to either your ``.bashrc`` or ``.profile``. You should now be able to start GF with C runtime.//
=== Use GF server mode with C runtime ===
- **What —**
With this feature, ``gf -server`` mode is extended with new requests to call the C run-time
system, e.g. ``c-parse``, ``c-linearize`` and ``c-translate``.
- **How —**
If you use cabal, run the following command:
```
cabal install -fc-runtime -fserver
```
from the top directory.
If you use stack, add the following lines in the ``stack.yaml`` file:
```
flags:
gf:
c-runtime: true
server: true
extra-lib-dirs:
- /usr/local/lib
```
and then run ``stack install``, also from the top directory.
=== Python and Java bindings ===
The C run-time system can also be used from Python and Java. Python and Java
bindings are found in the ``src/runtime/python`` and ``src/runtime/java``
directories, respecively. Compile them by following the instructions in
the ``INSTALL`` files in those directories.
The Python library can also be installed from PyPI using `pip install pgf`.
== Compilation of RGL ==
As of 2018-07-26, the RGL is distributed separately from the GF compiler and runtimes.
To get the source, follow the previous instructions on [how to clone a repository with Git #getting-source].
After cloning the RGL, you should have a directory named ``gf-rgl`` on your computer.
=== Simple ===
To install the RGL, you can use the following commands from within the ``gf-rgl`` repository:
```
@@ -317,68 +418,103 @@ If you do not have Haskell installed, you can use the simple build script ``Setu
== Creating binary distribution packages ==
The binaries are generated with Github Actions. More details can be viewed here:
=== Creating .deb packages for Ubuntu ===
https://github.com/GrammaticalFramework/gf-core/actions/workflows/build-binary-packages.yml
This was tested on Ubuntu 14.04 for the release of GF 3.6, and the
resulting ``.deb`` packages appears to work on Ubuntu 12.04, 13.10 and 14.04.
For the release of GF 3.7, we generated ``.deb`` packages on Ubuntu 15.04 and
tested them on Ubuntu 12.04 and 14.04.
Under Ubuntu, Haskell executables are statically linked against other Haskell
libraries, so the .deb packages are fairly self-contained.
== Running the test suite ==
The GF test suite is run with one of the following commands from the top directory:
==== Preparations ====
```
$ cabal test
sudo apt-get install dpkg-dev debhelper
```
or
==== Creating the package ====
Make sure the ``debian/changelog`` starts with an entry that describes the
version you are building. Then run
```
$ stack test
make deb
```
If get error messages about missing dependencies
(e.g. ``autoconf``, ``automake``, ``libtool-bin``, ``python-dev``,
``java-sdk``, ``txt2tags``)
use ``apt-get intall`` to install them, then try again.
=== Creating OS X Installer packages ===
Run
```
make pkg
```
=== Creating binary tar distributions ===
Run
```
make bintar
```
=== Creating .rpm packages for Fedora ===
This is possible, but the procedure has not been automated.
It involves using the cabal-rpm tool,
```
sudo dnf install cabal-rpm
```
and following the Fedora guide
[How to create an RPM package http://fedoraproject.org/wiki/How_to_create_an_RPM_package].
Under Fedora, Haskell executables are dynamically linked against other Haskell
libraries, so ``.rpm`` packages for all Haskell libraries that GF depends on
are required. Most of them are already available in the Fedora distribution,
but a few of them might have to be built and distributed along with
the GF ``.rpm`` package.
When building ``.rpm`` packages for GF 3.4, we also had to build ``.rpm``s for
``fst`` and ``httpd-shed``.
== Running the testsuite ==
**NOTE:** The test suite has not been maintained recently, so expect many
tests to fail.
%% // TH 2012-08-06
GF has testsuite. It is run with the following command:
```
$ cabal test
```
The testsuite architecture for GF is very simple but still very flexible.
GF by itself is an interpreter and could execute commands in batch mode.
This is everything that we need to organize a testsuite. The root of the
testsuite is the ``testsuite/`` directory. It contains subdirectories
which themselves contain GF batch files (with extension ``.gfs``).
The above command searches the subdirectories of the ``testsuite/`` directory
for files with extension ``.gfs`` and when it finds one, it is executed with
the GF interpreter. The output of the script is stored in file with extension ``.out``
and is compared with the content of the corresponding file with extension ``.gold``, if there is one.
testsuite is the testsuite/ directory. It contains subdirectories which
themself contain GF batch files (with extension .gfs). The above command
searches the subdirectories of the testsuite/ directory for files with extension
.gfs and when it finds one it is executed with the GF interpreter.
The output of the script is stored in file with extension .out and is compared
with the content of the corresponding file with extension .gold, if there is one.
If the contents are identical the command reports that the test was passed successfully.
Otherwise the test had failed.
Every time when you make some changes to GF that have to be tested,
instead of writing the commands by hand in the GF shell, add them to one ``.gfs``
file in the testsuite subdirectory where its ``.gf`` file resides and run the test.
In this way you can use the same test later and we will be sure that we will not
accidentally break your code later.
**Test Outcome - Passed:** If the contents of the files with the ``.out`` extension
are identical to their correspondingly-named files with the extension ``.gold``,
the command will report that the tests passed successfully, e.g.
Every time when you make some changes to GF that have to be tested, instead of
writing the commands by hand in the GF shell, add them to one .gfs file in the testsuite
and run the test. In this way you can use the same test later and we will be sure
that we will not incidentaly break your code later.
If you don't want to run the whole testsuite you can write the path to the subdirectory
in which you are interested. For example:
```
Running 1 test suites...
Test suite gf-tests: RUNNING...
Test suite gf-tests: PASS
1 of 1 test suites (1 of 1 test cases) passed.
$ cabal test testsuite/compiler
```
**Test Outcome - Failed:** If there is a contents mismatch between the files
with the ``.out`` extension and their corresponding files with the extension ``.gold``,
the test diagnostics will show a fail and the areas that failed. e.g.
```
testsuite/compiler/compute/Records.gfs: OK
testsuite/compiler/compute/Variants.gfs: FAIL
testsuite/compiler/params/params.gfs: OK
Test suite gf-tests: FAIL
0 of 1 test suites (0 of 1 test cases) passed.
```
The fail results overview is available in gf-tests.html which shows 4 columns:
+ __Results__ - only areas that fail will appear. (Note: There are 3 failures in the gf-tests.html which are labelled as (expected). These failures should be ignored.)
+ __Input__ - which is the test written in the .gfs file
+ __Gold__ - the expected output from running the test set out in the .gfs file. This column refers to the contents from the .gold extension files.
+ __Output__ - This column refers to the contents from the .out extension files which are generated as test output.
After fixing the areas which fail, rerun the test command. Repeat the entire process of fix-and-test until the test suite passes before submitting a pull request to include your changes.
will run only the testsuite for the compiler.

View File

@@ -15,12 +15,6 @@ instructions inside.
==Atom==
[language-gf https://atom.io/packages/language-gf], by John J. Camilleri
==Visual Studio Code==
[Grammatical Framework Language Server https://marketplace.visualstudio.com/items?itemName=anka-213.gf-vscode] by Andreas Källberg.
This provides syntax highlighting and a client for the Grammatical Framework language server. Follow the installation instructions in the link.
==Eclipse==
[GF Eclipse Plugin https://github.com/GrammaticalFramework/gf-eclipse-plugin/], by John J. Camilleri

View File

@@ -7,6 +7,7 @@ title: "Grammatical Framework: Authors and Acknowledgements"
The current maintainers of GF are
[Krasimir Angelov](http://www.chalmers.se/cse/EN/organization/divisions/computing-science/people/angelov-krasimir),
[Thomas Hallgren](http://www.cse.chalmers.se/~hallgren/),
[Aarne Ranta](http://www.cse.chalmers.se/~aarne/),
[John J. Camilleri](http://johnjcamilleri.com), and
[Inari Listenmaa](https://inariksit.github.io/).
@@ -21,7 +22,6 @@ and
The following people have contributed code to some of the versions:
- [Thomas Hallgren](http://www.cse.chalmers.se/~hallgren/) (University of Gothenburg)
- Grégoire Détrez (University of Gothenburg)
- Ramona Enache (University of Gothenburg)
- [Björn Bringert](http://www.cse.chalmers.se/alumni/bringert) (University of Gothenburg)

View File

@@ -1,11 +0,0 @@
# Compilation
The GF language is designed to be easy for the programmers to use but be able to run it efficiently we need to reduce it to a more low-level language. The goal of this chapter is to give an overview of the different steps in the compilation. The program transformation goes throught the following phases:
- renaming - here all identifiers in the grammar are made explicitly qualified. For example, if you had used the identifier PredVP somewhere, the compiler will search for a definition of that identifier in either the current module or in any of the modules imported from the current one. If a definition is found in, say in a module called Sentence, then the unqualified name PredVP will be replaced with the explicit qualification Sentence.PredVP. On the other hand, if the source program is already using an explicit qualification like Sentence.PredVP, then the compiler will check whether PredVP is indeed defined in the module Sentence.
- type checking - here the compiler will check whether all functions and variables are used correctly with respect to their types. For each term that the compiler checks it will also generate a new version of the term after the type checking. The input and output terms may not need to be the same. For example, the compiler may insert explicit type information. It might fill-in implicit arguments, or it may instantiate meta variables.
- partial evaluation - here is where the real compilation starts. The compiler will fully evaluate the term for each linearization to a normal. In the process, all uses of operations will be inlined. This is part of reducing the GF language to a simpler language which does not support operations.
- PMCFG generation - the language that the GF runtime understands is an extension of the PMCFG formalism. Not all features permitted in the GF language are allowed on that level. Most of the uses for that extra features have been eliminated via partial evaluation. If there are any left, then the compilation will abort. The main purpose of the PMCFG generation is to get rid of most of the parameter types in the source grammar. That is possible by generating several specialized linearization rules from a single linearization rule in the source.

View File

@@ -1,51 +0,0 @@
This is an experiment to develop **a majestic new GF runtime**.
The reason is that there are several features that we want to have and they all require a major rewrite of the existing C runtime.
Instead of beating the old code until it starts doing what we want, it is time to start from scratch.
# New Features
The features that we want are:
- We want to support **even bigger grammars that don't fit in the main memory** anymore. Instead, they should reside on the disc and parts will be loaded on demand.
The current design is that all memory allocated for the grammars should be from memory-mapped files. In this way the only limit for the grammar size will
be the size of the virtual memory, i.e. 2^64 bytes. The swap file is completely circumvented, while all of the available RAM can be used as a cache for loading parts
of the grammar.
- We want to be able to **update grammars dynamically**. This is a highly desired feature since recompiling large grammars takes hours.
Instead, dynamic updates should happen instantly.
- We want to be able to **store additional information in the PGF**. For example that could be application specific semantic data.
Another example is to store the source code of the different grammar rules, to allow the compiler to recompile individual rules.
- We want to **allow a single file to contain slightly different versions of the grammar**. This will be a kind of a version control system,
which will allow different users to store their own grammar extensions while still using the same core content.
- We want to **avoid the exponential explosion in the size of PMCFG** for some grammars. This happens because PMCFG as a formalism is too low-level.
By enriching it with light-weight variables, we can make it more powerful and hopefully avoid the exponential explosion.
- We want to finally **ditch the old Haskell runtime** which has long outlived its time.
There are also two bugs in the old C runtime whose fixes will require a lot of changes, so instead of fixing the old runtime we do it here:
- **Integer literals in the C runtime** are implemented as 32-bit integers, while the Haskell runtime used unlimited integers.
Python supports unlimited integers too, so it would be nice to support them in the new runtime as well.
- The old C runtime assumed that **String literals are terminated with the NULL character**. None of the modern languages (Haskell, Python, Java, etc) make
that assumption, so we should drop it too.
# Consequences
The desired features will have the following implementation cosequences.
- The switch from memory-based to disc-based runtime requires one big change. Before it was easy to just keep a pointer from one object to another.
Unfortunately this doesn't work with memory-mapped files, since every time when you map a file into memory it may end up at a different virtual address.
Instead we must use file offsets. In order to make programming simpler, the new runtime will be **implemented in C++ instead of C**. This allows us to overload
the arrow operator (`->`) which will dynamically convert file offsets to in-memory pointers.
- The choice of C++ also allows us to ditch the old `libgu` library and **use STL** instead.
- The content of the memory mapped files is platform-specific. For that reason there will be two grammar representations:
- **Native Grammar Format** (`.ngf`) - which will be instantly loadable by just mapping it to memory, but will be platform-dependent.
- **Portable Grammar Format** (`.pgf`) - which will take longer to load but will be more compact and platform independent.
The runtime will be able to load `.pgf` files and convert them to `.ngf`. Conversely `.pgf` can be exported from the current `.ngf`.

View File

@@ -1,20 +0,0 @@
# The Hacker's Guide to GF
This is the hacker's guide to GF, for the guide to the galaxy, see the full edition [here](https://en.wikipedia.org/wiki/The_Hitchhiker%27s_Guide_to_the_Galaxy).
Here we will limit outselves to the vastly narrower domain of the [GF](https://www.grammaticalframework.org) runtime. This means that we will not meet
any [Vogons](https://en.wikipedia.org/wiki/Vogon), but we will touch upon topics like memory management, databases, transactions, compilers,
functional programming, theorem proving and sometimes even languages. Subjects that no doubt would interest any curious hacker.
So, **Don't Panic!** and keep reading. This is a live document and will develop together with the runtime itself.
**TABLE OF CONTENTS**
1. Compilation
1. [Overview](CompilationOverview.md)
1. [Lambda Calculus](LambdaCalculus.md)
2. [Parallel Multiple Context-Free Grammars](PMCFG.md)
2. Runtime
1. [Desiderata](DESIDERATA.md)
2. [Memory Model](memory_model.md)
3. [Abstract Expressions](abstract_expressions.md)
4. [Transactions](transactions.md)

View File

@@ -1,192 +0,0 @@
# Data Marshalling Strategies
The runtime is designed to be used from a high-level programming language, which means that there are frequent foreign calls between the host language and C. This also implies that all the data must be frequently marshalled between the binary representations of the two languages. This is usually trivial and well supported for primitive types like numbers and strings but for complex data structures we need to design our own strategy.
The most central data structure in GF is of course the abstract syntax expression. The other two secondary but closely related structures are types and literals. These are complex structures and no high-level programming language will let us to manipulate them directly unless if they are in the format that the runtime of the language understands. There are three main strategies to deal with complex data accross a language boundry:
1. Keep the data in the C world and provide only an opaque handle to the host language. This means that all operations over the data must be done in C via foreign calls.
2. Design a native host-language representation. For each foreign call the data is copied from the host language to the C representation and vice versa. Copying is obviously bad, but not too bad if the data is small. The added benefit is that now both languages have first-class access to the data. As a bonus, the garbage collector of the host language now understands the data and can immediately release it if part of it becomes unreachable.
3. Keep the data in the host language. The C code has only an indirect access via opaque handles and calls back to the host language. The program in the host language has first-class access and the garbage collector can work with the data. No copying is needed.
The old C runtime used option 1. Obviously, this means that abstract expressions cannot be manipulated directly, but this is not the only problem. When the application constructs abstract expressions from different pieces, a whole a lot of overhead is added. First, the design was such that data in C must always be allocated from a memory pool. This means that even if we want to make a simple function application, we first must allocate a pool which adds memory overhead. In addition, the host language must allocate an object which wraps arround the C structure. The net effect is that while the plain abstract function application requires the allocation of only two pointers, the actually allocated data may be several times bigger if the application builds the expression piece by piece. The situation is better if the expression is entirely created from the runtime and the application just needs to keep a reference to it.
Another problem is that when the runtime has to create a whole bunch of expressions, for instance as a result from parsing or random and exhaustive generation, then all the expressions are allocated in the same memory pool. The application gets separate handles to each of the produced expressions, but the memory pool is released only after all of the handles become unreachable. Obviously the problem here is that different expressions share the same pool. Unfortunately this is hard to avoid since although the expressions are different, they usually share common subexpression. Identifying the shared parts would be expensive and at the end it might mean that each expression node must be allocated in its own pool.
The path taken in the new runtime is a combination of strategies 2 and 3. The abstract expressions are stored in the heap of the host language and use a native for that language representation.
# Abstract Expressions in Different Languages
In Haskell, abstract expressions are represented with an algebraic data type:
```Haskell
data Expr =
EAbs BindType Var Expr
| EApp Expr Expr
| ELit Literal
| EMeta MetaId
| EFun Fun
| EVar Int
| ETyped Expr Type
| EImplArg Expr
```
while in Python and all other object-oriented languages an expression is represented with objects of different classes:
```Python
class Expr: pass
class ExprAbs(Expr): pass
class ExprApp(Expr): pass
class ExprLit(Expr): pass
class ExprMeta(Expr): pass
class ExprFun(Expr): pass
class ExprVar(Expr): pass
class ExprTyped(Expr): pass
class ExprImplArg(Expr): pass
```
The runtime needs its own representation as well but only when an expression is stored in a .ngf file. This happens for instance with all types in the abstract syntax of the grammar. Since the type system allows dependent types, some type signature might contain expressions too. Another appearance for abstract expressions is in function definitions, i.e. in the def rules.
Expressions in the runtime are represented with C structures which on the other hand may contain tagged references to other structures. The lowest four bits of each reference encode the type of structure that it points to, while the rest contain the file offsets in the memory mapped file. For example, function application is represented as:
```C++
struct PgfExprApp {
static const uint8_t tag = 1;
PgfExpr fun;
PgfExpr arg;
};
```
Here the constant `tag` says that any reference to a PgfExprApp structure must contain the value 1 in its lowest four bits. The fields `fun` and `arg` refer to the function and the argument for that application. The type PgfExpr is defined as:
```C++
typedef uintptr_t object;
typedef object PgfExpr;
```
In order to dereference an expression, we first neeed to pattern match and then obtain a `ref<>` object:
```C++
switch (ref<PgfExpr>::get_tag(e)) {
...
case PgfExprApp::tag: {
auto eapp = ref<PgfExprApp>::untagged(e);
// do something with eapp->fun and eapp->arg
...
break;
}
...
}
```
The representation in the runtime is internal and should never be exposed to the host language. Moreover, these structures live in the memory mapped file and as we discussed in Section "[Memory Model](memory_model.md)" accessing them requires special care. This also means that occasionally the runtime must make a copy from the native representation to the host representation and vice versa. For example, function:
```Haskell
functionType :: PGF -> Fun -> Maybe Type
```
must look up the type of an abstract syntax function in the .ngf file and return its type. The type, however, is in the native representation and it must first be copied in the host representation. The converse also happens. When the compiler wants to add a new abstract function to the grammar, it creates its type in the Haskell heap, which the runtime later copies to the native representation in the .ngf file. This is not much different from any other database. The database file usually uses a different data representation than what the host language has.
In most other runtime operations, copying is not necessary. The only thing that the runtime needs to know is how to create new expressions in the heap of the host and how to pattern match on them. For that it calls back to code implemented differently for each host language. For example in:
```Haskell
readExpr :: String -> Maybe Expr
```
the runtime knows how to read an abstract syntax expression, while for the construction of the actual value it calls back to Haskell. Similarly:
```Haskell
showExpr :: [Var] -> Expr -> String
```
uses code implemented in Haskell to pattern match on the different algebraic constructors, while the text generation itself happens inside the runtime.
# Marshaller and Unmarshaller
The marshaller and the unmarshaller are the two key data structures which bridge together the different representation realms for abstract expressions and types. The structures have two equivalent definitions, one in C++:
```C++
struct PgfMarshaller {
virtual object match_lit(PgfUnmarshaller *u, PgfLiteral lit)=0;
virtual object match_expr(PgfUnmarshaller *u, PgfExpr expr)=0;
virtual object match_type(PgfUnmarshaller *u, PgfType ty)=0;
};
struct PgfUnmarshaller {
virtual PgfExpr eabs(PgfBindType btype, PgfText *name, PgfExpr body)=0;
virtual PgfExpr eapp(PgfExpr fun, PgfExpr arg)=0;
virtual PgfExpr elit(PgfLiteral lit)=0;
virtual PgfExpr emeta(PgfMetaId meta)=0;
virtual PgfExpr efun(PgfText *name)=0;
virtual PgfExpr evar(int index)=0;
virtual PgfExpr etyped(PgfExpr expr, PgfType typ)=0;
virtual PgfExpr eimplarg(PgfExpr expr)=0;
virtual PgfLiteral lint(size_t size, uintmax_t *v)=0;
virtual PgfLiteral lflt(double v)=0;
virtual PgfLiteral lstr(PgfText *v)=0;
virtual PgfType dtyp(int n_hypos, PgfTypeHypo *hypos,
PgfText *cat,
int n_exprs, PgfExpr *exprs)=0;
virtual void free_ref(object x)=0;
};
```
and one in C:
```C
typedef struct PgfMarshaller PgfMarshaller;
typedef struct PgfMarshallerVtbl PgfMarshallerVtbl;
struct PgfMarshallerVtbl {
object (*match_lit)(PgfUnmarshaller *u, PgfLiteral lit);
object (*match_expr)(PgfUnmarshaller *u, PgfExpr expr);
object (*match_type)(PgfUnmarshaller *u, PgfType ty);
};
struct PgfMarshaller {
PgfMarshallerVtbl *vtbl;
};
typedef struct PgfUnmarshaller PgfUnmarshaller;
typedef struct PgfUnmarshallerVtbl PgfUnmarshallerVtbl;
struct PgfUnmarshallerVtbl {
PgfExpr (*eabs)(PgfUnmarshaller *this, PgfBindType btype, PgfText *name, PgfExpr body);
PgfExpr (*eapp)(PgfUnmarshaller *this, PgfExpr fun, PgfExpr arg);
PgfExpr (*elit)(PgfUnmarshaller *this, PgfLiteral lit);
PgfExpr (*emeta)(PgfUnmarshaller *this, PgfMetaId meta);
PgfExpr (*efun)(PgfUnmarshaller *this, PgfText *name);
PgfExpr (*evar)(PgfUnmarshaller *this, int index);
PgfExpr (*etyped)(PgfUnmarshaller *this, PgfExpr expr, PgfType typ);
PgfExpr (*eimplarg)(PgfUnmarshaller *this, PgfExpr expr);
PgfLiteral (*lint)(PgfUnmarshaller *this, size_t size, uintmax_t *v);
PgfLiteral (*lflt)(PgfUnmarshaller *this, double v);
PgfLiteral (*lstr)(PgfUnmarshaller *this, PgfText *v);
PgfType (*dtyp)(PgfUnmarshaller *this,
int n_hypos, PgfTypeHypo *hypos,
PgfText *cat,
int n_exprs, PgfExpr *exprs);
void (*free_ref)(PgfUnmarshaller *this, object x);
};
struct PgfUnmarshaller {
PgfUnmarshallerVtbl *vtbl;
};
```
Which one you will get, depends on whether you import `pgf/pgf.h` from C or C++.
As we can see, most of the arguments for the different methods are of type `PgfExpr`, `PgfType` or `PgfLiteral`. These are all just type synonyms for the type `object`, which on the other hand is nothing else but a number with enough bits to hold an address if necessary. The interpretation of the number depends on the realm in which the object lives. The following table shows the interpretations for four languages as well as the one used internally in the .ngf files:
| | PgfExpr | PgfLiteral | PgfType |
|----------|----------------|-------------------|----------------|
| Haskell | StablePtr Expr | StablePtr Literal | StablePtr Type |
| Python | ExprObject * | PyObject * | TypeObject * |
| Java | jobject | jobject | jobject |
| .NET | GCHandle | GCHandle | GCHandle |
| internal | file offset | file offset | file offset |
The marshaller is the structure that lets the runtime to pattern match on an expression. When one of the match methods is executed, it checks the kind of expr, literal or type and calls the corresponding method from the unmarshaller which it gets as an argument. The method on the other hand gets as arguments the corresponding sub-expressions and attributes.
Generally the role of an unmarshaller is to construct things. For example, the variable `unmarshaller` in `PGF2.FFI` is an object which can construct new expressions in the Haskell heap from the already created children. Function `readExpr`, for instance, passes that one to the runtime to instruct it that the result must be in the Haskell realm.
Constructing objects is not the only use of an unmarshaller. The implementation of `showExpr` passes to `pgf_print_expr` an abstract expression in Haskell and the `marshaller` defined in PGF2.FFI. That marshaller knows how to pattern match on Haskell expressions and calls the right methods from whatever unmarhaller is given to it. What it will get in that particular case is a special unmarshaller which does not produce new representations of abstract expressions, but generates a string.
# Literals
Finally, we should have a few remarks about how values of the literal types `String`, `Int` and `Float` are represented in the runtime.
`String` is represented as the structure:
```C
typedef struct {
size_t size;
char text[];
} PgfText;
```
Here the first field is the size of the string in number of bytes. The second field is the string itself, encoded in UTF-8. Just like in most modern languages, the string may contain the zero character and that is not an indication for end of string. This means that functions like `strlen` and `strcat` should never be used when working with PgfText. Despite that the text is not zero terminated, the runtime always allocates one more last byte for the text content and sets it to zero. That last byte is not included when calculating the field `size`. The purpose is that with that last zero byte the GDB debugger knows how to show the string properly. Most of the time, this doesn't incur any memory overhead either since `malloc` always allocates memory in size divisible by the size of two machine words. The consequence is that usually there are some byte left unused at the end of every string anyway.
`Int` is like the integers in Haskell and Python and can have arbitrarily many digits. In the runtime, the value is represented as an array of `uintmax_t` values. Each of these values contains as many decimal digits as it is possible to fit in `uintmax_t`. For example on a 64-bit machine,
the maximal value that fits is 18446744073709551616. However, the left-most digit here is at most 1, this means that if we want to represend an arbitrary sequence of digits, the maximal length of the sequence must be at most 19. Similarly on a 32-bit machine each value in the array will store 9 decimal digits. Finally the sign of the number is stored as the sign of the first number in the array which is always threated as `intmax_t`.
Just to have an example, the number `-774763251095801167872` is represented as the array `{-77, 4763251095801167872}`. Note that this representation is not at all suitable for implementing arithmetics with integers, but is very simple to use for us since the runtime only needs to to parse and linearize numbers.
`Float` is trivial and is just represented as the type `double` in C/C++. This can also be seen in the type of the method `lflt` in the unmarshaller.

View File

@@ -1,136 +0,0 @@
# The different storage files
The purpose of the `.ngf` files is to be used as on-disk databases that store grammars. Their format is platform-dependent and they should not be copied from
one platform to another. In contrast the `.pgf` files are platform-independent and can be moved around. The runtime can import a `.pgf` file and create an `.ngf` file.
Conversely a `.pgf` file can be exported from an already existing `.ngf` file.
The internal relation between the two files is more interesting. The runtime uses its own memory allocator which always allocates memory from a memory mapped file.
The file may be explicit or an anonymous one. The `.ngf` is simply a memory image saved in a file. This means that loading the file is always immediate.
You just create a new mapping and the kernel will load memory pages on demand.
On the other hand a `.pgf` file is a version of the grammar serialized in a platform-independent format. This means that loading this type of file is always slower.
Fortunately, you can always create an `.ngf` file from it to speed up later reloads.
The runtime has three ways to load a grammar:
#### 1. Loading a `.pgf`
```Haskell
readPGF :: FilePath -> IO PGF
```
This loads the `.pgf` into an anonymous memory-mapped file. In practice, this means that instead of allocating memory from an explicit file, the runtime will still
use the normal swap file.
#### 2. Loading a `.pgf` and booting a new `.ngf`
```Haskell
bootPGF :: FilePath -> FilePath -> IO PGF
```
The grammar is loaded from a `.pgf` (the first argument) and the memory is mapped to an explicit `.ngf` (second argument). The `.ngf` file is created by the function
and a file with the same name should not exist before the call.
#### 3. Loading an existing memory image
```Haskell
readNGF :: FilePath -> IO PGF
```
Once an `.ngf` file exists, it can be mapped back to memory by using this function. This call is always guaranteed to be fast. The same function can also
create new empty `.ngf` files. If the file does not exist, then a new one will be created which contains an empty grammar. The grammar could then be extended
by dynamically adding functions and categories.
# The content of an `.ngf` file
The `.ngf` file is a memory image but this is not the end of the story. The problem is that there is no way to control at which address the memory image would be
mapped. On Posix systems, `mmap` takes as hint the mapping address but the kernel may choose to ignore it. There is also the flag `MAP_FIXED`, which makes the hint
into a constraint, but then the kernel may fail to satisfy the constraint. For example that address may already be used for something else. Furthermore, if the
same file is mapped from several processes (if they all load the same grammar), it would be difficult to find an address which is free in all of them.
Last but not least using `MAP_FIXED` is considered a security risk.
Since the start address of the mapping can change, using traditional memory pointers withing the mapped area is not possible. The only option is to use offsets
relative to the beginning of the area. In other words, if normally we would have written `p->x`, now we have the offset `o` which we must use like this:
```C++
((A*) (current_base+o))->x
```
Writing the explicit pointer arithmetics and typecasts, each time when we dereference a pointer, is not better than Vogon poetry and it
becomes worse when using a chain of arrow operators. The solution is to use the operator overloading in C++.
There is the type `ref<A>` which wraps around a file offset to a data item of type `A`. The operators `->` and `*`
are overloaded for the type and they do the necessary pointer arithmetics and type casts.
This solves the problem with code readability but creates another problem. How do `->` and `*` know the address of the memory mapped area? Obviously,
`current_base` must be a global variable and there must be a way to initialize it. More specifically it must be thread-local to allow different threads to
work without collisions.
A database (a memory-mapped file) in the runtime is represented by the type `DB`. Before any of the data in the database is accessed, the database must
be brought into scope. Bringing into scope means that `current_base` is initialized to point to the mapping area for that database. After that any dereferencing
of a reference will be done relative to the corresponding database. This is how scopes are defined:
```C++
{
DB_scope scope(db, READER_SCOPE);
...
}
```
Here `DB_scope` is a helper type and `db` is a pointer to the database that you want to bring into scope. The constructor for `DB_scope` saves the old value
for `current_base` and then sets it to point to the area of the given database. Conversely, the destructor restores the previous value.
The use of `DB_scope` is reentrant, i.e. you can do this:
```C++
{
DB_scope scope(db1, READER_SCOPE);
...
{
DB_scope scope(db2, READER_SCOPE);
...
}
...
}
```
What you can't do is to have more than one database in scope simultaneously. Fortunately, that is not needed. All API functions start a scope
and the internals of the runtime always work with the current database in scope.
Note the flag `READER_SCOPE`. You can use either `READER_SCOPE` or `WRITER_SCOPE`. In addition to selecting the database, the `DB_scope` also enforces
the single writer/multiple readers policy. The main problem is that a writer may have to enlarge the current file, which consequently may mean
that the kernel should relocate the mapping area to a new address. If there are readers at the same time, they may break since they expect that the mapped
area is at a particular location.
# Developing writers
There is one important complication when developing procedures modifying the database. Every call to `DB::malloc` may potentially have to enlarge the mapped area
which sometimes leads to changing `current_base`. That would not have been a problem if GCC was not sometimes caching variables in registers. Look at the following code:
```C++
p->r = foo();
```
Here `p` is a reference which is used to access another reference `r`. On the other hand, `foo()` is a procedure which directly or indirectly calls `DB::malloc`.
GCC compiles assignments by first computing the address to modify, and then it evaluates the right hand side. This means that while `foo()` is being evaluated the address computed on the left-hand side is saved in a register or somewhere in the stack. But now, if it happens that the allocation in `foo()` has changed
`current_base`, then the saved address is no longer valid.
That first problem is solved by overloading the assignment operator for `ref<A>`:
```C++
ref<A>& operator= (const ref<A>& r) {
offset = r.offset;
return *this;
}
```
On first sight, nothing special happens here and it looks like the overloading is redundant. However, now the assignments are compiled in a very different way.
The overloaded operator is inlined, so there is no real method call and we don't get any overhead. The real difference is that now, whatever is on the left-hand side of the assignment becomes the value of the `this` pointer, and `this` is always the last thing to be evaluated in a method call. This solves the problem.
`foo()` is evaluated first and if it changes `current_base`, the change will be taken into account when computing the left-hand side of the assignment.
Unfortunately, this is not the only problem. A similar thing happens when the arguments of a function are calls to other functions. See this:
```C++
foo(p->r,bar(),q->r)
```
Where now `bar()` is the function that performs allocation. The compiler is free to keep in a register the value of `current_base` that it needs for the evaluation of
`p->r`, while it evaluates `bar()`. But if `current_base` has changed, then the saved value would be invalid while computing `q->r`. There doesn't seem to be
a work around for this. The only solution is to:
**Never call a function that allocates as an argument to another function**
Instead we call allocating functions on a separate line and we save the result in a temporary variable.
# Thread-local variables
A final remark is the compilation of thread-local variables. When a thread-local variable is compiled in a position-dependent code, i.e. in executables, it is
compiled efficiently by using the `fs` register which points to the thread-local segment. Unfortunately, that is not the case by default for shared
libraries like our runtime. In that case, GCC applies the global-dynamic model which means that access to a thread local variable is internally implemented
with a call to the function `__tls_get_addr`. Since `current_base` is used all the time, this adds overhead.
The solution is to define the variable with the attribute `__attribute__((tls_model("initial-exec")))` which says that it should be treated as if it is defined
in an executable. This removes the overhead, but adds the limitation that the runtime should not be loaded with `dlopen`.

View File

@@ -1,131 +0,0 @@
# Transactions
The `.ngf` files that the runtime creates are actual databases which are used to get quick access to the grammars. Like in any database, we also make it possible to dynamically change the data. In our case this means that we can add and remove functions and categories at any time. Moreover, any changes happen in transactions which ensure that changes are not visible until the transaction is commited. The rest of the document describes how the transactions are implemented.
# Databases and Functional Languages
The database model of the runtime is specifically designed to be friendly towards pure functional languages like Haskell. In a usual database, updates happen constantly and therefore executing one and the same query at different times would yield different results. In our grammar databases, queries correspond to operations like parsing, linearization and generation. This means that if we had used the usual database model, all these operations would have to be bound to the IO monad. Consider this example:
```Haskell
main = do
gr <- readNGF "Example.ngf"
functionType gr "f" >>= print
-- modify the grammar gr
functionType gr "f" >>= print
```
Here we ask for the type of a function before and after an arbitrary update in the grammar `gr`. Obviously if we allow that then `functionType` would have to be in the IO monad, e.g.:
```Haskell
functionType :: PGF -> Fun -> IO Type
```
Although this is a possible way to go, it would mean that the programmer would have to do all grammar related work in the IO. This is not nice and against the spirit of functional programming. Moreover, all previous implementations of the runtime have assumed that most operations are pure. If we go along that path then this will cause a major breaking change.
Fortunately there is an alternative. Read-only operations remain pure functions, but any update should create a new revision of the database rather than modifying the existing one. Compare this example with the previous:
```Haskell
main = do
gr <- readNGF "Example.ngf"
print (functionType gr "f")
gr2 <- modifyPGF gr $ do
-- do all updates here
print (functionType gr2 "f")
```
Here `modifyPGF` allows us to do updates but the updates are performed on a freshly created clone of the grammar `gr`. The original grammar is never ever modified. After the changes the variable `gr2` is a reference to the new revision. While the transaction is in progress we cannot see the currently changing revision, and therefore all read-only operations can remain pure. Only after the transaction is complete do we get to use `gr2`, which will not change anymore.
Note also that above `functionType` is used with its usual pure type:
```Haskell
functionType :: PGF -> Fun -> Type
```
This is safe since the API never exposes database revisions which are not complete. Furthermore, the programmer is free to keep several revisions of the same database simultaneously. In this example:
```Haskell
main = do
gr <- readNGF "Example.ngf"
gr2 <- modifyPGF gr $ do
-- do all updates here
print (functionType gr "f", functionType gr2 "f")
```
The last line prints the type of function `"f"` in both the old and the new revision. Both are still available.
The API as described so far would have been complete if all updates were happening in a single thread. In reality we can expect that there might be several threads or processes modifying the database. The database ensures a multiple readers/single writer exclusion but this doesn't mean that another process/thread cannot modify the database while the current one is reading an old revision. In a parallel setting, `modifyPGF` first merges the revision which the process is using with the latest revision in the database. On top of that the specified updates are performed. The final revision after the updates is returned as a result.
**TODO: Interprocess synhronization is still not implemented**
**TODO: Merges are still not implemented.**
The process can also ask for the latest revision by calling `checkoutPGF`, see bellow.
# Databases and Imperative Languages
In imperative languages, the state of the program constantly changes and the considerations in the last section do not apply. All read-only operations always work with the latest revision. Bellow is the previous example translated to Python:
```Python
gr = readNGF("Example.ngf")
print(functionType(gr,"f"))
with gr.transaction() as t:
# do all updates here by using t
print(functionType(gr,"f"))
```
Here the first call to `functionType` returns the old type of "f", while the second call retrives the type after the updates. The transaction itself is initiated by the `with` statement. Inside the with statement `gr` will still refer to the old revision since the new one is not complete yet. If the `with` statement is finished without exceptions then `gr` is updated to point to the new one. If an exception occurs then the new revision is discarded, which corresponds to a transaction rollback. Inside the `with` block, the object `t` of type `Transaction` provides methods for modifying the data.
# Branches
Since the database already supports revisions, it is a simple step to support branches as well. A branch is just a revision with a name. When you open a database with `readNGF`, the runtime looks up and returns the revision (branch) with name `master`. There might be other branches as well. You can retrieve a specific branch by calling:
```Haskell
checkoutPGF :: PGF -> String -> IO (Maybe PGF)
```
Here the string is the branch name. New branches can be created by using:
```Haskell
branchPGF :: PGF -> String -> Transaction a -> IO PGF
```
Here we start with an existing revision, apply a transaction and store the result in a new branch with the given name.
# Implementation
The low-level API for transactions consists of only four functions:
```C
PgfRevision pgf_clone_revision(PgfDB *db, PgfRevision revision,
PgfText *name,
PgfExn *err);
void pgf_free_revision(PgfDB *pgf, PgfRevision revision);
void pgf_commit_revision(PgfDB *db, PgfRevision revision,
PgfExn *err);
PgfRevision pgf_checkout_revision(PgfDB *db, PgfText *name,
PgfExn *err);
```
Here `pgf_clone_revision` makes a copy of an existing revision and — if `name` is not `NULL` — changes its name. The new revision is transient and exists only until it is released with `pgf_free_revision`. Transient revisions can be updated with the API for adding functions and categories. To make a revision persistent, call `pgf_commit_revision`. After the revision is made persistent it will stay in the database even after you call `pgf_free_revision`. Moreover, it will replace the last persistent revision with the same name. The old revision will then become transient and will exist only until all clients call `pgf_free_revision` for it.
Persistent revisions can never be updated. Instead you clone it to create a new transient revision. That one is updated and finally it replaces the existing persistent revision.
This design for transactions may sound unusual but it is just another way to present the copy-on-write strategy. There instead of transaction logs, each change to the data is written in a new place and the result is made available only after all changes are in place. This is for instance what the [LMDB](http://www.lmdb.tech/doc/) (Lightning Memory-Mapped Database) does and it has also served as an inspiration for us.
## Functional Data Structures
From an imperative point of view, it may sound wasteful that a new copy of the grammar is created for each transaction. Functional programmers on the other hand know that with a functional data structure, you can make a copy which shares as much of the data with the original as possible. Each new version copies only those bits that are different from the old one. For example the main data structure that we use to represent the abstract syntax of a grammar is a size-balanced binary tree as described by:
- Stephen Adams, "Efficient sets: a balancing act", Journal of Functional Programming 3(4):553-562, October 1993, http://www.swiss.ai.mit.edu/~adams/BB/.
- J. Nievergelt and E.M. Reingold, "Binary search trees of bounded balance", SIAM journal of computing 2(1), March 1973.
## Garbage Collection
We use reference counting to keep track of which objects should be kept alive. For instance, `pgf_free_revision` knows that a transient revision should be removed only when its reference count reaches zero. This means that there is no process or thread using it. The function also checks whether the revision is persistent. Persistent revisions are never removed since they can always be retrieved with `checkoutPGF`.
Clients are supposed to correctly use `pgf_free_revision` to indicate that they don't need a revision any more. Unfortunately, this is not always possible to guarantee. For example many languages with garbage collection will call `pgf_free_revision` from a finalizer method. In some languages, however, the finalizer is not guaranteed to be executed if the process terminates before the garbage collection is done. Haskell is one of those languages. Even in languages with reference counting like Python, the process may get killed by the operating system and then the finalizer may still not be executed.
The solution is that we count on the database clients to correctly report when a revision is not needed. However, on a fresh database restart we explictly clean all left over transient revisions. This means that even if a client is killed or if it does not correctly release its revisions, the worst that can happen is a memory leak until the next restart.
## Atomicity
The transactions serve two goals. First they make it possible to isolate readers from seeing unfinished changes from writers. Second, they ensure atomicity. A database change should be either completely done or not done at all. The use of transient revisions ensures the isolation but the atomicity is only partly taken care of.
Think about what happens when a writer starts updating a transient revision. All the data is allocated in a memory mapped file. From the point of view of the runtime, all changes happen in memory. When all is done, the runtime calls `msync` which tells the kernel to flush all dirty pages to disk. The problem is that the kernel is also free to flush pages at any time. For instance, if there is not enough memory, it may decide to swap out pages earlier and reuse the released physical space to swap in other virtual pages. This would be fine if the transaction eventually succeeds. However, if this doesn't happen then the image in the file is already changed.
We can avoid the situation by calling [mlock](https://man7.org/linux/man-pages/man2/mlock.2.html) and telling the kernel that certain pages should not be swapped out. The question is which pages to lock. We can lock them all, but this is too much. That would mean that as soon as a page is touched it will never leave the physical memory. Instead, it would have been nice to tell the kernel -- feel free to swap out clean pages but, as soon as they get dirty, keep them in memory until further notice. Unfortunately there is no way to do that directly.
The work around is to first use [mprotect](https://man7.org/linux/man-pages/man2/mprotect.2.html) and keep all pages as read-only. Any attempt to change a page will cause segmentation fault which we can capture. If the change happens during a transaction then we can immediate lock the page and add it to the list of modified pages. When a transaction is successful we sync all modified pages. If an attempt to change a page happens outside of a transaction, then this is either a bug in the runtime or the client is trying to change an address which it should not change. In any case this prevents unintended changes in the data.
**TODO: atomicity is not implemented yet**

View File

@@ -1,9 +1,8 @@
---
title: Grammatical Framework Download and Installation
date: 25 July 2021
---
...
**GF 3.11** was released on 25 July 2021.
**GF 3.11** was released on ... December 2020.
What's new? See the [release notes](release-3.11.html).
@@ -25,25 +24,22 @@ Binary packages are available for Debian/Ubuntu, macOS, and Windows and include:
Unlike in previous versions, the binaries **do not** include the RGL.
[Binary packages on GitHub](https://github.com/GrammaticalFramework/gf-core/releases/tag/3.11)
[Binary packages on GitHub](https://github.com/GrammaticalFramework/gf-core/releases/tag/RELEASE-3.11)
#### Debian/Ubuntu
There are two versions: `gf-3.11-ubuntu-18.04.deb` for Ubuntu 18.04 (Cosmic), and `gf-3.11-ubuntu-20.04.deb` for Ubuntu 20.04 (Focal).
To install the package use:
```
sudo apt-get install ./gf-3.11-ubuntu-*.deb
sudo dpkg -i gf_3.11.deb
```
<!-- The Ubuntu `.deb` packages should work on Ubuntu 16.04, 18.04 and similar Linux distributions. -->
The Ubuntu `.deb` packages should work on Ubuntu 16.04, 18.04 and similar Linux distributions.
#### macOS
To install the package, just double-click it and follow the installer instructions.
The packages should work on at least Catalina and Big Sur.
The packages should work on at least 10.13 (High Sierra) and 10.14 (Mojave).
#### Windows
@@ -53,39 +49,24 @@ You will probably need to update the `PATH` environment variable to include your
For more information, see [Using GF on Windows](https://www.grammaticalframework.org/~inari/gf-windows.html) (latest updated for Windows 10).
## Installing from Hackage
_Instructions applicable for macOS, Linux, and WSL2 on Windows._
## Installing the latest release from source
[GF is on Hackage](http://hackage.haskell.org/package/gf), so under
normal circumstances the procedure is fairly simple:
```
cabal update
cabal install gf-3.11
```
1. Install a recent version of the [Haskell Platform](http://hackage.haskell.org/platform) (see note below)
2. `cabal update`
3. On Linux: install some C libraries from your Linux distribution (see note below)
4. `cabal install gf`
You can also download the source code release from [GitHub](https://github.com/GrammaticalFramework/gf-core/releases),
and follow the instructions below under **Installing from the latest developer source code**.
### Notes
**GHC version**
The GF source code is known to be compilable with GHC versions 7.10 through to 8.10.
**Obtaining Haskell**
There are various ways of obtaining Haskell, including:
- ghcup
1. Install from https://www.haskell.org/ghcup/
2. `ghcup install ghc 8.10.4`
3. `ghcup set ghc 8.10.4`
- Haskell Platform https://www.haskell.org/platform/
- Stack https://haskellstack.org/
**Installation location**
The above steps install GF for a single user.
The above steps installs GF for a single user.
The executables are put in `$HOME/.cabal/bin` (or on macOS in `$HOME/Library/Haskell/bin`),
so you might want to add this directory to your path (in `.bash_profile` or similar):
@@ -93,38 +74,47 @@ so you might want to add this directory to your path (in `.bash_profile` or simi
PATH=$HOME/.cabal/bin:$PATH
```
**Build tools**
In order to compile GF you need the build tools **Alex** and **Happy**.
These can be installed via Cabal, e.g.:
```
cabal install alex happy
```
or obtained by other means, depending on your OS.
**Haskeline**
GF uses [`haskeline`](http://hackage.haskell.org/package/haskeline), which
on Linux depends on some non-Haskell libraries that won't be installed
automatically by Cabal, and therefore need to be installed manually.
automatically by cabal, and therefore need to be installed manually.
Here is one way to do this:
- On Ubuntu: `sudo apt-get install libghc-haskeline-dev`
- On Fedora: `sudo dnf install ghc-haskeline-devel`
## Installing from source code
**GHC version**
**Obtaining**
The GF source code has been updated to compile with GHC versions 7.10 through to 8.8.
To obtain the source code for the **release**,
download it from [GitHub](https://github.com/GrammaticalFramework/gf-core/releases).
## Installing from the latest developer source code
Alternatively, to obtain the **latest version** of the source code:
If you haven't already, clone the repository with:
1. If you haven't already, clone the repository with:
```
git clone https://github.com/GrammaticalFramework/gf-core.git
```
2. If you've already cloned the repository previously, update with:
If you've already cloned the repository previously, update with:
```
git pull
```
Then install with:
**Installing**
You can then install with:
```
cabal install
```
@@ -135,7 +125,7 @@ or, if you're a Stack user:
stack install
```
<!--The above notes for installing from source apply also in these cases.-->
The above notes for installing from source apply also in these cases.
For more info on working with the GF source code, see the
[GF Developers Guide](../doc/gf-developers.html).

View File

@@ -1,8 +1,8 @@
<html>
<head>
<meta http-equiv="refresh" content="0; URL=/download/index-3.11.html" />
<meta http-equiv="refresh" content="0; URL=/download/index-3.10.html" />
</head>
<body>
You are being redirected to <a href="index-3.11.html">the current version</a> of this page.
You are being redirected to <a href="index-3.10.html">the current version</a> of this page.
</body>
</html>

View File

@@ -1,7 +1,7 @@
---
title: GF 3.11 Release Notes
date: 25 July 2021
---
date: ... December 2020
...
## Installation
@@ -12,27 +12,24 @@ See the [download page](index-3.11.html).
From this release, the binary GF core packages do not contain the RGL.
The RGL's release cycle is now completely separate from GF's. See [RGL releases](https://github.com/GrammaticalFramework/gf-rgl/releases).
Over 500 changes have been pushed to GF core
Over 400 changes have been pushed to GF core
since the release of GF 3.10 in December 2018.
## General
- Make the test suite work again.
- Compatibility with new versions of GHC, including multiple Stack files for the different versions.
- Support for newer version of Ubuntu 20.04 in the precompiled binaries.
- Updates to build scripts and CI workflows.
- Bug fixes and code cleanup.
- Updates to build scripts and CI.
- Bug fixes.
## GF compiler and run-time library
- Huge improvements in time & space requirements for grammar compilation (pending [#87](https://github.com/GrammaticalFramework/gf-core/pull/87)).
- Add CoNLL output to `visualize_tree` shell command.
- Add canonical GF as output format in the compiler.
- Add PGF JSON as output format in the compiler.
- Deprecate JavaScript runtime in favour of updated [TypeScript runtime](https://github.com/GrammaticalFramework/gf-typescript).
- Improvements in time & space requirements when compiling certain grammars.
- Improvements to Haskell export.
- Improvements to the GF shell.
- Improvements to canonical GF compilation.
- Improvements to the C runtime.
- Improvements to `gf -server` mode.
- Clearer compiler error messages.

633
gf.cabal
View File

@@ -1,19 +1,19 @@
name: gf
version: 3.11.0-git
version: 3.10.4-git
cabal-version: 1.22
cabal-version: >= 1.22
build-type: Custom
license: OtherLicense
license-file: LICENSE
category: Natural Language Processing, Compiler
synopsis: Grammatical Framework
description: GF, Grammatical Framework, is a programming language for multilingual grammar applications
homepage: https://www.grammaticalframework.org/
homepage: http://www.grammaticalframework.org/
bug-reports: https://github.com/GrammaticalFramework/gf-core/issues
tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.10.4
maintainer: Thomas Hallgren
tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.2.2, GHC==8.4.3
data-dir: src
extra-source-files: WebSetup.hs
data-files:
www/*.html
www/*.css
@@ -41,34 +41,39 @@ data-files:
custom-setup
setup-depends:
base >= 4.9.1 && < 4.15,
Cabal >= 1.22.0.0,
directory >= 1.3.0 && < 1.4,
filepath >= 1.4.1 && < 1.5,
process >= 1.0.1.1 && < 1.7
base,
Cabal >=1.22.0.0,
directory,
filepath,
process >=1.0.1.1
source-repository head
type: git
type: git
location: https://github.com/GrammaticalFramework/gf-core.git
flag interrupt
Description: Enable Ctrl+Break in the shell
Default: True
Default: True
flag server
Description: Include --server mode
Default: True
Default: True
flag network-uri
description: Get Network.URI from the network-uri package
default: True
executable gf
hs-source-dirs: src/programs, src/compiler
main-is: gf-main.hs
--flag new-comp
-- Description: Make -new-comp the default
-- Default: True
flag c-runtime
Description: Include functionality from the C run-time library (which must be installed already)
Default: False
Library
default-language: Haskell2010
build-depends: pgf2,
base >= 4.6 && <5,
build-depends: base >= 4.6 && <5,
array,
containers,
bytestring,
@@ -77,18 +82,85 @@ executable gf
pretty,
mtl,
exceptions,
fail,
-- For compatability with ghc < 8
-- We need transformers-compat >= 0.6.3, but that is only in newer snapshots where it is redundant.
transformers-compat,
ghc-prim,
filepath, directory>=1.2, time,
process, haskeline, parallel>=3, json
ghc-options: -threaded
text,
hashable,
unordered-containers
hs-source-dirs: src/runtime/haskell
other-modules:
-- not really part of GF but I have changed the original binary library
-- and we have to keep the copy for now.
Data.Binary
Data.Binary.Put
Data.Binary.Get
Data.Binary.Builder
Data.Binary.IEEE754
--ghc-options: -fwarn-unused-imports
--if impl(ghc>=7.8)
-- ghc-options: +RTS -A20M -RTS
ghc-prof-options: -fprof-auto
exposed-modules:
PGF
PGF.Internal
PGF.Haskell
LPGF
other-modules:
PGF.Data
PGF.Macros
PGF.Binary
PGF.Optimize
PGF.Printer
PGF.CId
PGF.Expr
PGF.Generate
PGF.Linearize
PGF.Morphology
PGF.Paraphrase
PGF.Parse
PGF.Probabilistic
PGF.SortTop
PGF.Tree
PGF.Type
PGF.TypeCheck
PGF.Forest
PGF.TrieMap
PGF.VisualizeTree
PGF.ByteCode
PGF.OldBinary
PGF.Utilities
if flag(c-runtime)
exposed-modules: PGF2
other-modules: PGF2.FFI PGF2.Expr PGF2.Type
GF.Interactive2 GF.Command.Commands2
hs-source-dirs: src/runtime/haskell-bind
build-tools: hsc2hs
extra-libraries: pgf gu
c-sources: src/runtime/haskell-bind/utils.c
cc-options: -std=c99
---- GF compiler as a library:
build-depends: filepath, directory>=1.2, time,
process, haskeline, parallel>=3, json
hs-source-dirs: src/compiler
exposed-modules:
GF
GF.Support
GF.Text.Pretty
GF.Text.Lexing
GF.Grammar.Canonical
other-modules:
GF.Main GF.Compiler GF.Interactive
GF.Compile GF.CompileInParallel GF.CompileOne GF.Compile.GetGrammar
@@ -109,21 +181,27 @@ executable gf
GF.Command.TreeOperations
GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar
GF.Compile.Compute.Concrete
GF.Compile.Compute.ConcreteNew
GF.Compile.Compute.Predef
GF.Compile.Compute.Value
GF.Compile.ExampleBased
GF.Compile.Export
GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF
GF.Compile.Multi
GF.Compile.OptimizePGF
GF.Compile.Optimize
GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJava
GF.Haskell
GF.Compile.ConcreteToHaskell
GF.Compile.GrammarToCanonical
GF.Grammar.CanonicalJSON
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles
GF.Compile.Rename
GF.Compile.SubExOpt
@@ -133,12 +211,14 @@ executable gf
GF.Compile.TypeCheck.Concrete
GF.Compile.TypeCheck.ConcreteNew
GF.Compile.TypeCheck.Primitives
GF.Compile.TypeCheck.RConcrete
GF.Compile.TypeCheck.TC
GF.Compile.Update
GF.Data.BacktrackM
GF.Data.ErrM
GF.Data.Graph
GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Relation
GF.Data.Str
GF.Data.Utilities
@@ -190,38 +270,491 @@ executable gf
GF.System.Directory
GF.System.Process
GF.System.Signal
GF.System.NoSignal
GF.Text.Clitics
GF.Text.Coding
GF.Text.Transliterations
Paths_gf
if flag(c-runtime)
cpp-options: -DC_RUNTIME
if flag(server)
build-depends: httpd-shed>=0.4.0.3, network>=2.3 && <2.7,
cgi>=3001.2.2.0
if flag(network-uri)
build-depends: network-uri>=2.6, network>=2.6
else
build-depends: network<2.6
cpp-options: -DSERVER_MODE
other-modules:
GF.Server
PGFService
RunHTTP
SimpleEditor.Convert
SimpleEditor.JSON
SimpleEditor.Syntax
URLEncoding
CGI
CGIUtils
Cache
Fold
ExampleDemo
ExampleService
hs-source-dirs: src/server src/server/transfer src/example-based
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
other-modules: GF.System.NoSignal
if impl(ghc>=7.8)
build-tools: happy>=1.19, alex>=3.1
-- ghc-options: +RTS -A20M -RTS
else
build-tools: happy, alex>=3
ghc-options: -fno-warn-tabs
if os(windows)
build-depends: Win32
else
build-depends: unix, terminfo>=0.4
if impl(ghc>=8.2)
ghc-options: -fhide-source-paths
Executable gf
hs-source-dirs: src/programs
main-is: gf-main.hs
default-language: Haskell2010
build-depends: gf, base
ghc-options: -threaded
--ghc-options: -fwarn-unused-imports
if impl(ghc>=7.0)
ghc-options: -rtsopts -with-rtsopts=-I5
if impl(ghc<7.8)
ghc-options: -with-rtsopts=-K64M
ghc-prof-options: -auto-all
if impl(ghc>=8.2)
ghc-options: -fhide-source-paths
executable pgf-shell
--if !flag(c-runtime)
buildable: False
main-is: pgf-shell.hs
hs-source-dirs: src/runtime/haskell-bind/examples
build-depends: gf, base, containers, mtl, lifted-base
default-language: Haskell2010
if impl(ghc>=7.0)
ghc-options: -rtsopts
test-suite gf-tests
type: exitcode-stdio-1.0
main-is: run.hs
hs-source-dirs: testsuite
build-depends: base>=4.3 && <5, Cabal>=1.8, directory, filepath, process
default-language: Haskell2010
test-suite lpgf
type: exitcode-stdio-1.0
main-is: test.hs
hs-source-dirs:
src/compiler
src/runtime/haskell
testsuite/lpgf
other-modules:
Data.Binary
Data.Binary.Builder
Data.Binary.Get
Data.Binary.IEEE754
Data.Binary.Put
GF
GF.Command.Abstract
GF.Command.CommandInfo
GF.Command.Commands
GF.Command.CommonCommands
GF.Command.Help
GF.Command.Importing
GF.Command.Interpreter
GF.Command.Messages
GF.Command.Parse
GF.Command.SourceCommands
GF.Command.TreeOperations
GF.Compile
GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar
GF.Compile.Compute.ConcreteNew
GF.Compile.Compute.Predef
GF.Compile.Compute.Value
GF.Compile.ConcreteToHaskell
GF.Compile.ExampleBased
GF.Compile.Export
GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG
GF.Compile.GetGrammar
GF.Compile.GrammarToCanonical
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF
GF.Compile.Multi
GF.Compile.Optimize
GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJava
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles
GF.Compile.Rename
GF.Compile.SubExOpt
GF.Compile.Tags
GF.Compile.ToAPI
GF.Compile.TypeCheck.Abstract
GF.Compile.TypeCheck.ConcreteNew
GF.Compile.TypeCheck.Primitives
GF.Compile.TypeCheck.RConcrete
GF.Compile.TypeCheck.TC
GF.Compile.Update
GF.CompileInParallel
GF.CompileOne
GF.Compiler
GF.Data.BacktrackM
GF.Data.ErrM
GF.Data.Graph
GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Operations
GF.Data.Relation
GF.Data.Str
GF.Data.Utilities
GF.Data.XML
GF.Grammar
GF.Grammar.Analyse
GF.Grammar.Binary
GF.Grammar.BNFC
GF.Grammar.Canonical
GF.Grammar.CanonicalJSON
GF.Grammar.CFG
GF.Grammar.EBNF
GF.Grammar.Grammar
GF.Grammar.Lexer
GF.Grammar.Lockfield
GF.Grammar.Lookup
GF.Grammar.Macros
GF.Grammar.Parser
GF.Grammar.PatternMatch
GF.Grammar.Predef
GF.Grammar.Printer
GF.Grammar.ShowTerm
GF.Grammar.Unify
GF.Grammar.Values
GF.Haskell
GF.Infra.BuildInfo
GF.Infra.CheckM
GF.Infra.Concurrency
GF.Infra.Dependencies
GF.Infra.GetOpt
GF.Infra.Ident
GF.Infra.Location
GF.Infra.Option
GF.Infra.SIO
GF.Infra.UseIO
GF.Interactive
GF.JavaScript.AbsJS
GF.JavaScript.PrintJS
GF.Main
GF.Quiz
GF.Speech.CFGToFA
GF.Speech.FiniteState
GF.Speech.GSL
GF.Speech.JSGF
GF.Speech.PGFToCFG
GF.Speech.PrRegExp
GF.Speech.RegExp
GF.Speech.SISR
GF.Speech.SLF
GF.Speech.SRG
GF.Speech.SRGS_ABNF
GF.Speech.SRGS_XML
GF.Speech.VoiceXML
GF.Support
GF.System.Catch
GF.System.Concurrency
GF.System.Console
GF.System.Directory
GF.System.Process
GF.System.Signal
GF.Text.Clitics
GF.Text.Coding
GF.Text.Lexing
GF.Text.Pretty
GF.Text.Transliterations
LPGF
PGF
PGF.Binary
PGF.ByteCode
PGF.CId
PGF.Data
PGF.Expr
PGF.Forest
PGF.Generate
PGF.Internal
PGF.Linearize
PGF.Macros
PGF.Morphology
PGF.OldBinary
PGF.Optimize
PGF.Paraphrase
PGF.Parse
PGF.Printer
PGF.Probabilistic
PGF.Tree
PGF.TrieMap
PGF.Type
PGF.TypeCheck
PGF.Utilities
PGF.VisualizeTree
Paths_gf
-- not really part of GF but I have changed the original binary library
-- and we have to keep the copy for now.
Data.Binary
Data.Binary.Put
Data.Binary.Get
Data.Binary.Builder
Data.Binary.IEEE754
if os(windows)
build-depends:
Win32 >= 2.3.1.1 && < 2.7
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
build-depends:
terminfo >=0.4.0 && < 0.5,
unix >= 2.7.2 && < 2.8
test-suite gf-tests
type: exitcode-stdio-1.0
main-is: run.hs
hs-source-dirs: testsuite
other-modules: GF.System.NoSignal
build-depends:
base >= 4.9.1 && < 4.15,
Cabal >= 1.8,
directory >= 1.3.0 && < 1.4,
filepath >= 1.4.1 && < 1.5,
process >= 1.4.3 && < 1.7
build-tool-depends: gf:gf
ansi-terminal,
array,
base>=4.6 && <5,
bytestring,
containers,
directory,
filepath,
ghc-prim,
hashable,
haskeline,
json,
mtl,
parallel>=3,
pretty,
process,
random,
terminfo,
text,
time,
transformers-compat,
unix,
unordered-containers,
utf8-string
default-language: Haskell2010
benchmark lpgf-bench
type: exitcode-stdio-1.0
main-is: bench.hs
hs-source-dirs:
src/compiler
src/runtime/haskell
testsuite/lpgf
other-modules:
Data.Binary
Data.Binary.Builder
Data.Binary.Get
Data.Binary.IEEE754
Data.Binary.Put
GF
GF.Command.Abstract
GF.Command.CommandInfo
GF.Command.Commands
GF.Command.CommonCommands
GF.Command.Help
GF.Command.Importing
GF.Command.Interpreter
GF.Command.Messages
GF.Command.Parse
GF.Command.SourceCommands
GF.Command.TreeOperations
GF.Compile
GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar
GF.Compile.Compute.ConcreteNew
GF.Compile.Compute.Predef
GF.Compile.Compute.Value
GF.Compile.ConcreteToHaskell
GF.Compile.ExampleBased
GF.Compile.Export
GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG
GF.Compile.GetGrammar
GF.Compile.GrammarToCanonical
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF
GF.Compile.Multi
GF.Compile.Optimize
GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON
GF.Compile.PGFtoJava
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles
GF.Compile.Rename
GF.Compile.SubExOpt
GF.Compile.Tags
GF.Compile.ToAPI
GF.Compile.TypeCheck.Abstract
GF.Compile.TypeCheck.ConcreteNew
GF.Compile.TypeCheck.Primitives
GF.Compile.TypeCheck.RConcrete
GF.Compile.TypeCheck.TC
GF.Compile.Update
GF.CompileInParallel
GF.CompileOne
GF.Compiler
GF.Data.BacktrackM
GF.Data.ErrM
GF.Data.Graph
GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Operations
GF.Data.Relation
GF.Data.Str
GF.Data.Utilities
GF.Data.XML
GF.Grammar
GF.Grammar.Analyse
GF.Grammar.BNFC
GF.Grammar.Binary
GF.Grammar.CFG
GF.Grammar.Canonical
GF.Grammar.CanonicalJSON
GF.Grammar.EBNF
GF.Grammar.Grammar
GF.Grammar.Lexer
GF.Grammar.Lockfield
GF.Grammar.Lookup
GF.Grammar.Macros
GF.Grammar.Parser
GF.Grammar.PatternMatch
GF.Grammar.Predef
GF.Grammar.Printer
GF.Grammar.ShowTerm
GF.Grammar.Unify
GF.Grammar.Values
GF.Haskell
GF.Infra.BuildInfo
GF.Infra.CheckM
GF.Infra.Concurrency
GF.Infra.Dependencies
GF.Infra.GetOpt
GF.Infra.Ident
GF.Infra.Location
GF.Infra.Option
GF.Infra.SIO
GF.Infra.UseIO
GF.Interactive
GF.JavaScript.AbsJS
GF.JavaScript.PrintJS
GF.Main
GF.Quiz
GF.Speech.CFGToFA
GF.Speech.FiniteState
GF.Speech.GSL
GF.Speech.JSGF
GF.Speech.PGFToCFG
GF.Speech.PrRegExp
GF.Speech.RegExp
GF.Speech.SISR
GF.Speech.SLF
GF.Speech.SRG
GF.Speech.SRGS_ABNF
GF.Speech.SRGS_XML
GF.Speech.VoiceXML
GF.Support
GF.System.Catch
GF.System.Concurrency
GF.System.Console
GF.System.Directory
GF.System.Process
GF.System.Signal
GF.Text.Clitics
GF.Text.Coding
GF.Text.Lexing
GF.Text.Pretty
GF.Text.Transliterations
LPGF
PGF
PGF.Binary
PGF.ByteCode
PGF.CId
PGF.Data
PGF.Expr
PGF.Expr
PGF.Forest
PGF.Generate
PGF.Internal
PGF.Linearize
PGF.Macros
PGF.Morphology
PGF.OldBinary
PGF.Optimize
PGF.Paraphrase
PGF.Parse
PGF.Printer
PGF.Probabilistic
PGF.Tree
PGF.TrieMap
PGF.Type
PGF.TypeCheck
PGF.Utilities
PGF.VisualizeTree
PGF2
PGF2.Expr
PGF2.Type
PGF2.FFI
Paths_gf
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
other-modules: GF.System.NoSignal
hs-source-dirs:
src/runtime/haskell-bind
other-modules:
PGF2
PGF2.FFI
PGF2.Expr
PGF2.Type
build-tools: hsc2hs
extra-libraries: pgf gu
c-sources: src/runtime/haskell-bind/utils.c
cc-options: -std=c99
build-depends:
ansi-terminal,
array,
base>=4.6 && <5,
bytestring,
containers,
deepseq,
directory,
filepath,
ghc-prim,
hashable,
haskeline,
json,
mtl,
parallel>=3,
pretty,
process,
random,
terminfo,
text,
time,
transformers-compat,
unix,
unordered-containers,
utf8-string
default-language: Haskell2010

View File

@@ -8,7 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.15.4/css/all.css" crossorigin="anonymous">
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.4.2/css/all.css" integrity="sha384-/rXc/GQVaYpyDdyxK+ecHPVYJSN9bmVFBvjA/9eOB+pb3F2w2N6fc5qB9Ew5yIns" crossorigin="anonymous">
<link rel="alternate" href="https://github.com/GrammaticalFramework/gf-core/" title="GF GitHub repository">
</head>
@@ -85,27 +85,10 @@
<div class="col-sm-6 col-md-3 mb-4">
<h3>Contribute</h3>
<ul class="mb-2">
<li>
<a href="https://web.libera.chat/?channels=#gf">
<i class="fas fa-hashtag"></i>
IRC
</a>
/
<a href="https://discord.gg/EvfUsjzmaz">
<i class="fab fa-discord"></i>
Discord
</a>
</li>
<li>
<a href="https://stackoverflow.com/questions/tagged/gf">
<i class="fab fa-stack-overflow"></i>
Stack Overflow
</a>
</li>
<li><a href="https://groups.google.com/group/gf-dev">Mailing List</a></li>
<li><a href="http://groups.google.com/group/gf-dev">Mailing List</a></li>
<li><a href="https://github.com/GrammaticalFramework/gf-core/issues">Issue Tracker</a></li>
<li><a href="//school.grammaticalframework.org/2020/">Summer School</a></li>
<li><a href="doc/gf-people.html">Authors</a></li>
<li><a href="//school.grammaticalframework.org/2020/">Summer School</a></li>
</ul>
<a href="https://github.com/GrammaticalFramework/" class="btn btn-primary ml-3">
<i class="fab fa-github mr-1"></i>
@@ -171,7 +154,7 @@ least one, it may help you to get a first idea of what GF is.
<div class="row">
<div class="col-md-6">
<h2>Applications & availability</h2>
<h2>Applications & Availability</h2>
<p>
GF can be used for building
<a href="//cloud.grammaticalframework.org/translator/">translation systems</a>,
@@ -231,45 +214,56 @@ least one, it may help you to get a first idea of what GF is.
</p>
<p>
We run the IRC channel <strong><code>#gf</code></strong> on the Libera network, where you are welcome to look for help with small questions or just start a general discussion.
You can <a href="https://web.libera.chat/?channels=#gf">open a web chat</a>
or <a href="https://www.grammaticalframework.org/irc/?C=M;O=D">browse the channel logs</a>.
We run the IRC channel <strong><code>#gf</code></strong> on the Freenode network, where you are welcome to look for help with small questions or just start a general discussion.
You can <a href="https://webchat.freenode.net/?channels=gf">open a web chat</a>
or <a href="/irc/">browse the channel logs</a>.
</p>
<p>
There is also a <a href="https://discord.gg/EvfUsjzmaz">GF server on Discord</a>.
</p>
<p>
For bug reports and feature requests, please create an issue in the
<a href="https://github.com/GrammaticalFramework/gf-core/issues">GF Core</a> or
<a href="https://github.com/GrammaticalFramework/gf-rgl/issues">RGL</a> repository.
For programming questions, consider asking them on <a href="https://stackoverflow.com/questions/tagged/gf">Stack Overflow with the <code>gf</code> tag</a>.
If you have a more general question to the community, we recommend you ask it on the <a href="http://groups.google.com/group/gf-dev">mailing list</a>.
If you have a larger question which the community may benefit from, we recommend you ask it on the <a href="http://groups.google.com/group/gf-dev">mailing list</a>.
</p>
</div>
<div class="col-md-6">
<h2>News</h2>
<dl class="row">
<dt class="col-sm-3 text-center text-nowrap">2021-07-25</dt>
<dd class="col-sm-9">
<strong>GF 3.11 released.</strong>
<a href="download/release-3.11.html">Release notes</a>
</dd>
<dt class="col-sm-3 text-center text-nowrap">2021-05-05</dt>
<dd class="col-sm-9">
<a href="https://cloud.grammaticalframework.org/wordnet/">GF WordNet</a> now supports languages for which there are no other WordNets. New additions: Afrikaans, German, Korean, Maltese, Polish, Somali, Swahili.
</dd>
<dt class="col-sm-3 text-center text-nowrap">2021-03-01</dt>
<dd class="col-sm-9">
<a href="//school.grammaticalframework.org/2020/">Seventh GF Summer School</a>, in Singapore and online, 26 July &ndash; 6 August 2021.
<a href="//school.grammaticalframework.org/2020/">Seventh GF Summer School</a>, in Singapore and online, 26 July &ndash; 8 August 2021.
</dd>
<dt class="col-sm-3 text-center text-nowrap">2020-09-29</dt>
<dd class="col-sm-9">
<a href="https://www.mitpressjournals.org/doi/pdf/10.1162/COLI_a_00378">Abstract Syntax as Interlingua</a>: Scaling Up the Grammatical Framework from Controlled Languages to Robust Pipelines. A paper in Computational Linguistics (2020) summarizing much of the development in GF in the past ten years.
</dd>
<dt class="col-sm-3 text-center text-nowrap">2018-12-03</dt>
<dd class="col-sm-9">
<a href="//school.grammaticalframework.org/2018/">Sixth GF Summer School</a> in Stellenbosch (South Africa), 314 December 2018
</dd>
<dt class="col-sm-3 text-center text-nowrap">2018-12-02</dt>
<dd class="col-sm-9">
<strong>GF 3.10 released.</strong>
<a href="download/release-3.10.html">Release notes</a>
</dd>
<dt class="col-sm-3 text-center text-nowrap">2018-07-25</dt>
<dd class="col-sm-9">
The GF repository has been split in two:
<a href="https://github.com/GrammaticalFramework/gf-core">gf-core</a> and
<a href="https://github.com/GrammaticalFramework/gf-rgl">gf-rgl</a>.
The original <a href="https://github.com/GrammaticalFramework/GF">GF</a> repository is now archived.
</dd>
<dt class="col-sm-3 text-center text-nowrap">2017-08-11</dt>
<dd class="col-sm-9">
<strong>GF 3.9 released.</strong>
<a href="download/release-3.9.html">Release notes</a>
</dd>
<dt class="col-sm-3 text-center text-nowrap">2017-06-29</dt>
<dd class="col-sm-9">
GF is moving to <a href="https://github.com/GrammaticalFramework/GF/">GitHub</a>.</dd>
<dt class="col-sm-3 text-center text-nowrap">2017-03-13</dt>
<dd class="col-sm-9">
<a href="//school.grammaticalframework.org/2017/">GF Summer School</a> in Riga (Latvia), 14-25 August 2017
</dd>
</dl>
<h2>Projects</h2>
@@ -339,7 +333,7 @@ least one, it may help you to get a first idea of what GF is.
Libraries are at the heart of modern software engineering. In natural language
applications, libraries are a way to cope with thousands of details involved in
syntax, lexicon, and inflection. The
<a href="lib/doc/synopsis/index.html">GF resource grammar library</a> (RGL) has
<a href="lib/doc/synopsis/index.html">GF resource grammar library</a> has
support for an increasing number of languages, currently including
Afrikaans,
Amharic (partial),

View File

@@ -1,6 +1,6 @@
module GF.Command.Abstract(module GF.Command.Abstract,Expr,showExpr,Term) where
import PGF2(Expr,showExpr)
import PGF(CId,mkCId,Expr,showExpr)
import GF.Grammar.Grammar(Term)
type Ident = String
@@ -11,7 +11,7 @@ type Pipe = [Command]
data Command
= Command Ident [Option] Argument
deriving Show
deriving (Eq,Ord,Show)
data Option
= OOpt Ident
@@ -29,7 +29,13 @@ data Argument
| ATerm Term
| ANoArg
| AMacro Ident
deriving Show
deriving (Eq,Ord,Show)
valCIdOpts :: String -> CId -> [Option] -> CId
valCIdOpts flag def opts =
case [v | OFlag f (VId v) <- opts, f == flag] of
(v:_) -> mkCId v
_ -> def
valIntOpts :: String -> Int -> [Option] -> Int
valIntOpts flag def opts =
@@ -43,18 +49,6 @@ valStrOpts flag def opts =
v:_ -> valueString v
_ -> def
maybeIntOpts :: String -> a -> (Int -> a) -> [Option] -> a
maybeIntOpts flag def fn opts =
case [v | OFlag f (VInt v) <- opts, f == flag] of
(v:_) -> fn v
_ -> def
maybeStrOpts :: String -> a -> (String -> a) -> [Option] -> a
maybeStrOpts flag def fn opts =
case listFlags flag opts of
v:_ -> fn (valueString v)
_ -> def
listFlags flag opts = [v | OFlag f v <- opts, f == flag]
valueString v =

View File

@@ -3,7 +3,8 @@ import GF.Command.Abstract(Option,Expr,Term)
import GF.Text.Pretty(render)
import GF.Grammar.Printer() -- instance Pretty Term
import GF.Grammar.Macros(string2term)
import PGF2(mkStr,unStr,showExpr)
import qualified PGF as H(showExpr)
import qualified PGF.Internal as H(Literal(LStr),Expr(ELit)) ----
data CommandInfo m = CommandInfo {
exec :: [Option] -> CommandArguments -> m CommandOutput,
@@ -37,19 +38,21 @@ class Monad m => TypeCheckArg m where typeCheckArg :: Expr -> m Expr
--------------------------------------------------------------------------------
data CommandArguments = Exprs [(Expr,Float)] | Strings [String] | Term Term
data CommandArguments = Exprs [Expr] | Strings [String] | Term Term
newtype CommandOutput = Piped (CommandArguments,String) ---- errors, etc
-- ** Converting command output
fromStrings ss = Piped (Strings ss, unlines ss)
fromExprs show_p es = Piped (Exprs es,unlines (map (\(e,p) -> (if show_p then (++) ("["++show p++"] ") else id) (showExpr [] e)) es))
fromExprs es = Piped (Exprs es,unlines (map (H.showExpr []) es))
fromString s = Piped (Strings [s], s)
pipeWithMessage es msg = Piped (Exprs es,msg)
pipeMessage msg = Piped (Exprs [],msg)
pipeExprs es = Piped (Exprs es,[]) -- only used in emptyCommandInfo
void = Piped (Exprs [],"")
stringAsExpr = H.ELit . H.LStr -- should be a pattern macro
-- ** Converting command input
toStrings args =
@@ -58,23 +61,23 @@ toStrings args =
Exprs es -> zipWith showAsString (True:repeat False) es
Term t -> [render t]
where
showAsString first (e,p) =
case unStr e of
Just s -> s
Nothing -> ['\n'|not first] ++
showExpr [] e ---newline needed in other cases than the first
showAsString first t =
case t of
H.ELit (H.LStr s) -> s
_ -> ['\n'|not first] ++
H.showExpr [] t ---newline needed in other cases than the first
toExprs args =
case args of
Exprs es -> map fst es
Strings ss -> map mkStr ss
Term t -> [mkStr (render t)]
Exprs es -> es
Strings ss -> map stringAsExpr ss
Term t -> [stringAsExpr (render t)]
toTerm args =
case args of
Term t -> t
Strings ss -> string2term $ unwords ss -- hmm
Exprs es -> string2term $ unwords $ map (showExpr [] . fst) es -- hmm
Exprs es -> string2term $ unwords $ map (H.showExpr []) es -- hmm
-- ** Creating documentation

View File

@@ -1,11 +1,16 @@
{-# LANGUAGE FlexibleInstances, UndecidableInstances, CPP #-}
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module GF.Command.Commands (
HasPGF(..),pgfCommands,
PGFEnv,HasPGFEnv(..),pgf,mos,pgfEnv,pgfCommands,
options,flags,
) where
import Prelude hiding (putStrLn,(<>))
import Prelude hiding (putStrLn,(<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import PGF2
import PGF
import PGF.Internal(lookStartCat,functionsToCat,lookValCat,restrictPGF,hasLin)
import PGF.Internal(abstract,funs,cats,Expr(EFun)) ----
import PGF.Internal(ppFun,ppCat)
import PGF.Internal(optimizePGF)
import GF.Compile.Export
import GF.Compile.ToAPI
@@ -23,28 +28,28 @@ import GF.Command.TreeOperations ---- temporary place for typecheck and compute
import GF.Data.Operations
import Data.Char
import PGF.Internal (encodeFile)
import Data.List(intersperse,nub)
import Data.Maybe
import qualified Data.Map as Map
import GF.Text.Pretty
import Data.List (sort)
import Control.Monad(mplus)
import qualified Control.Monad.Fail as Fail
--import Debug.Trace
class (Functor m,Monad m,MonadSIO m) => HasPGF m where getPGF :: m (Maybe PGF)
data PGFEnv = Env {pgf::PGF,mos::Map.Map Language Morpho}
instance (Monad m,HasPGF m,Fail.MonadFail m) => TypeCheckArg m where
typeCheckArg e = do mb_pgf <- getPGF
case mb_pgf of
Just pgf -> either fail
(return . fst)
(inferExpr pgf e)
Nothing -> fail "Import a grammar before using this command"
pgfEnv pgf = Env pgf mos
where mos = Map.fromList [(la,buildMorpho pgf la) | la <- languages pgf]
pgfCommands :: HasPGF m => Map.Map String (CommandInfo m)
class (Functor m,Monad m,MonadSIO m) => HasPGFEnv m where getPGFEnv :: m PGFEnv
instance (Monad m,HasPGFEnv m,Fail.MonadFail m) => TypeCheckArg m where
typeCheckArg e = (either (fail . render . ppTcError) (return . fst)
. flip inferExpr e . pgf) =<< getPGFEnv
pgfCommands :: HasPGFEnv m => Map.Map String (CommandInfo m)
pgfCommands = Map.fromList [
("aw", emptyCommandInfo {
longname = "align_words",
@@ -57,7 +62,7 @@ pgfCommands = Map.fromList [
"by the view flag. The target format is png, unless overridden by the",
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick)."
],
exec = needPGF $ \ opts arg pgf -> do
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let es = toExprs arg
let langs = optLangs pgf opts
if isOpt "giza" opts
@@ -69,7 +74,7 @@ pgfCommands = Map.fromList [
let grph = if null es then [] else lsrc ++ "\n--end_source--\n\n"++ltrg++"\n-end_target--\n\n"++align
return $ fromString grph
else do
let grphs = map (graphvizWordAlignment langs graphvizDefaults) es
let grphs = map (graphvizAlignment pgf langs) es
if isFlag "view" opts || isFlag "format" opts
then do
let view = optViewGraph opts
@@ -91,7 +96,6 @@ pgfCommands = Map.fromList [
("view", "program to open the resulting file")
]
}),
("ca", emptyCommandInfo {
longname = "clitic_analyse",
synopsis = "print the analyses of all words into stems and clitics",
@@ -102,17 +106,16 @@ pgfCommands = Map.fromList [
"by the flag '-clitics'. The list of stems is given as the list of words",
"of the language given by the '-lang' flag."
],
exec = needPGF $ \opts ts pgf -> do
concr <- optLang pgf opts
case opts of
_ | isOpt "raw" opts ->
return . fromString .
unlines . map (unwords . map (concat . intersperse "+")) .
map (getClitics (not . null . lookupMorpho concr) (optClitics opts)) .
concatMap words $ toStrings ts
_ -> return . fromStrings .
getCliticsText (not . null . lookupMorpho concr) (optClitics opts) .
concatMap words $ toStrings ts,
exec = getEnv $ \opts ts env -> case opts of
_ | isOpt "raw" opts ->
return . fromString .
unlines . map (unwords . map (concat . intersperse "+")) .
map (getClitics (isInMorpho (optMorpho env opts)) (optClitics opts)) .
concatMap words $ toStrings ts
_ ->
return . fromStrings .
getCliticsText (isInMorpho (optMorpho env opts)) (optClitics opts) .
concatMap words $ toStrings ts,
flags = [
("clitics","the list of possible clitics (comma-separated, no spaces)"),
("lang", "the language of analysis")
@@ -144,19 +147,19 @@ pgfCommands = Map.fromList [
],
flags = [
("file","the file to be converted (suffix .gfe must be given)"),
("lang","the language in which to parse")
("lang","the language in which to parse"),
("probs","file with probabilities to rank the parses")
],
exec = needPGF $ \opts _ pgf -> do
exec = getEnv $ \ opts _ env@(Env pgf mos) -> do
let file = optFile opts
pgf <- optProbs opts pgf
let printer = if (isOpt "api" opts) then exprToAPI else (showExpr [])
concr <- optLang pgf opts
let conf = configureExBased pgf concr printer
let conf = configureExBased pgf (optMorpho env opts) (optLang pgf opts) printer
(file',ws) <- restricted $ parseExamplesInGrammar conf file
if null ws then return () else putStrLn ("unknown words: " ++ unwords ws)
return (fromString ("wrote " ++ file')),
needsTypeCheck = False
}),
("gr", emptyCommandInfo {
longname = "generate_random",
synopsis = "generate random trees in the current abstract syntax",
@@ -171,53 +174,54 @@ pgfCommands = Map.fromList [
explanation = unlines [
"Generates a list of random trees, by default one tree.",
"If a tree argument is given, the command completes the Tree with values to",
"all metavariables in the tree. The generation can be biased by probabilities",
"if the grammar was compiled with option -probs"
],
options = [
("show_probs", "show the probability of each result")
"all metavariables in the tree. The generation can be biased by probabilities,",
"given in a file in the -probs flag."
],
flags = [
("cat","generation category"),
("lang","uses only functions that have linearizations in all these languages"),
("number","number of trees generated")
("number","number of trees generated"),
("depth","the maximum generation depth"),
("probs", "file with biased probabilities (format 'f 0.4' one by line)")
],
exec = needPGF $ \opts arg pgf -> do
exec = getEnv $ \ opts arg (Env pgf mos) -> do
pgf <- optProbs opts (optRestricted opts pgf)
gen <- newStdGen
let dp = valIntOpts "depth" 4 opts
let ts = case mexp (toExprs arg) of
Just ex -> generateRandomFrom gen pgf ex
Nothing -> generateRandom gen pgf (optType pgf opts)
returnFromExprs (isOpt "show_probs" opts) $ take (optNum opts) ts
Just ex -> generateRandomFromDepth gen pgf ex (Just dp)
Nothing -> generateRandomDepth gen pgf (optType pgf opts) (Just dp)
returnFromExprs $ take (optNum opts) ts
}),
("gt", emptyCommandInfo {
longname = "generate_trees",
synopsis = "generates a list of trees, by default exhaustive",
explanation = unlines [
"Generates all trees of a given category.",
"Generates all trees of a given category. By default, ",
"the depth is limited to 4, but this can be changed by a flag.",
"If a Tree argument is given, the command completes the Tree with values",
"to all metavariables in the tree."
],
options = [
("show_probs", "show the probability of each result")
],
flags = [
("cat","the generation category"),
("depth","the maximum generation depth"),
("lang","excludes functions that have no linearization in this language"),
("number","the number of trees generated")
],
examples = [
mkEx "gt -- all trees in the startcat",
mkEx "gt -- all trees in the startcat, to depth 4",
mkEx "gt -cat=NP -number=16 -- 16 trees in the category NP",
mkEx "gt -cat=NP -depth=2 -- trees in the category NP to depth 2",
mkEx "gt (AdjCN ? (UseN ?)) -- trees of form (AdjCN ? (UseN ?))"
],
exec = needPGF $ \opts arg pgf -> do
let es = case mexp (toExprs arg) of
Just ex -> generateAllFrom pgf ex
Nothing -> generateAll pgf (optType pgf opts)
returnFromExprs (isOpt "show_probs" opts) $ takeOptNum opts es
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let pgfr = optRestricted opts pgf
let dp = valIntOpts "depth" 4 opts
let ts = case mexp (toExprs arg) of
Just ex -> generateFromDepth pgfr ex (Just dp)
Nothing -> generateAllDepth pgfr (optType pgf opts) (Just dp)
returnFromExprs $ take (optNumInf opts) ts
}),
("i", emptyCommandInfo {
longname = "import",
synopsis = "import a grammar from source code or compiled .pgf file",
@@ -238,28 +242,33 @@ pgfCommands = Map.fromList [
("probs","file with biased probabilities for generation")
],
options = [
-- ["gfo", "src", "no-cpu", "cpu", "quiet", "verbose"]
("retain","retain operations (used for cc command)"),
("src", "force compilation from source"),
("v", "be verbose - show intermediate status information")
],
needsTypeCheck = False
}),
("l", emptyCommandInfo {
longname = "linearize",
synopsis = "convert an abstract syntax expression to string",
explanation = unlines [
"Shows the linearization of a tree by the grammars in scope.",
"Shows the linearization of a Tree by the grammars in scope.",
"The -lang flag can be used to restrict this to fewer languages.",
"A sequence of string operations (see command ps) can be given",
"as options, and works then like a pipe to the ps command, except",
"that it only affect the strings, not e.g. the table labels."
"that it only affect the strings, not e.g. the table labels.",
"These can be given separately to each language with the unlexer flag",
"whose results are prepended to the other lexer flags. The value of the",
"unlexer flag is a space-separated list of comma-separated string operation",
"sequences; see example."
],
examples = [
mkEx "l -lang=LangSwe,LangNor no_Utt -- linearize tree to LangSwe and LangNor",
mkEx "gr -lang=LangHin -cat=Cl | l -table -to_devanagari -- hindi table"
mkEx "gr -lang=LangHin -cat=Cl | l -table -to_devanagari -- hindi table",
mkEx "l -unlexer=\"LangAra=to_arabic LangHin=to_devanagari\" -- different unlexers"
],
exec = needPGF $ \ opts ts pgf -> return . fromStrings . optLins pgf opts $ toExprs ts,
exec = getEnv $ \ opts ts (Env pgf mos) -> return . fromStrings . optLins pgf opts $ toExprs ts,
options = [
("all", "show all forms and variants, one by line (cf. l -list)"),
("bracket","show tree structure with brackets and paths to nodes"),
@@ -267,13 +276,33 @@ pgfCommands = Map.fromList [
("list","show all forms and variants, comma-separated on one line (cf. l -all)"),
("multi","linearize to all languages (default)"),
("table","show all forms labelled by parameters"),
("tabtreebank","show the tree and its linearizations on a tab-separated line"),
("treebank","show the tree and tag linearizations with language names")
] ++ stringOpOptions,
flags = [
("lang","the languages of linearization (comma-separated, no spaces)"),
("unlexer","set unlexers separately to each language (space-separated)")
]
}),
("lc", emptyCommandInfo {
longname = "linearize_chunks",
synopsis = "linearize a tree that has metavariables in maximal chunks without them",
explanation = unlines [
"A hopefully temporary command, intended to work around the type checker that fails",
"trees where a function node is a metavariable."
],
examples = [
mkEx "l -lang=LangSwe,LangNor -chunks ? a b (? c d)"
],
exec = getEnv $ \ opts ts (Env pgf mos) -> return . fromStrings $ optLins pgf (opts ++ [OOpt "chunks"]) (toExprs ts),
options = [
("treebank","show the tree and tag linearizations with language names")
] ++ stringOpOptions,
flags = [
("lang","the languages of linearization (comma-separated, no spaces)")
]
],
needsTypeCheck = False
}),
("ma", emptyCommandInfo {
longname = "morpho_analyse",
synopsis = "print the morphological analyses of all words in the string",
@@ -281,20 +310,18 @@ pgfCommands = Map.fromList [
"Prints all the analyses of space-separated words in the input string,",
"using the morphological analyser of the actual grammar (see command pg)"
],
exec = needPGF $ \opts ts pgf -> do
concr <- optLang pgf opts
case opts of
_ | isOpt "missing" opts ->
return . fromString . unwords .
morphoMissing concr .
concatMap words $ toStrings ts
_ | isOpt "known" opts ->
return . fromString . unwords .
morphoKnown concr .
concatMap words $ toStrings ts
_ -> return . fromString . unlines .
map prMorphoAnalysis . concatMap (morphos pgf opts) .
concatMap words $ toStrings ts,
exec = getEnv $ \opts ts env -> case opts of
_ | isOpt "missing" opts ->
return . fromString . unwords .
morphoMissing (optMorpho env opts) .
concatMap words $ toStrings ts
_ | isOpt "known" opts ->
return . fromString . unwords .
morphoKnown (optMorpho env opts) .
concatMap words $ toStrings ts
_ -> return . fromString . unlines .
map prMorphoAnalysis . concatMap (morphos env opts) .
concatMap words $ toStrings ts,
flags = [
("lang","the languages of analysis (comma-separated, no spaces)")
],
@@ -308,16 +335,18 @@ pgfCommands = Map.fromList [
longname = "morpho_quiz",
synopsis = "start a morphology quiz",
syntax = "mq (-cat=CAT)? (-probs=FILE)? TREE?",
exec = needPGF $ \ opts arg pgf -> do
lang <- optLang pgf opts
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let lang = optLang pgf opts
let typ = optType pgf opts
pgf <- optProbs opts pgf
let mt = mexp (toExprs arg)
restricted $ morphologyQuiz mt pgf lang typ
return void,
flags = [
("lang","language of the quiz"),
("cat","category of the quiz"),
("number","maximum number of questions")
("number","maximum number of questions"),
("probs","file with biased probabilities for generation")
]
}),
@@ -328,25 +357,24 @@ pgfCommands = Map.fromList [
"Shows all trees returned by parsing a string in the grammars in scope.",
"The -lang flag can be used to restrict this to fewer languages.",
"The default start category can be overridden by the -cat flag.",
"See also the ps command for lexing and character encoding."
],
exec = needPGF $ \opts ts pgf ->
return $
foldr (joinPiped . fromParse1 opts) void
(concat [
[(s,parse concr (optType pgf opts) s) |
concr <- optLangs pgf opts]
| s <- toStrings ts]),
options = [
("show_probs", "show the probability of each result")
"See also the ps command for lexing and character encoding.",
"",
"The -openclass flag is experimental and allows some robustness in ",
"the parser. For example if -openclass=\"A,N,V\" is given, the parser",
"will accept unknown adjectives, nouns and verbs with the resource grammar."
],
exec = getEnv $ \ opts ts (Env pgf mos) ->
return $ fromParse opts (concat [map ((,) s) (par pgf opts s) | s <- toStrings ts]),
flags = [
("cat","target category of parsing"),
("lang","the languages of parsing (comma-separated, no spaces)"),
("number","limit the results to the top N trees")
("openclass","list of open-class categories for robust parsing"),
("depth","maximal depth for proof search if the abstract syntax tree has meta variables")
],
options = [
("bracket","prints the bracketed string from the parser")
]
}),
("pg", emptyCommandInfo { -----
longname = "print_grammar",
synopsis = "print the actual grammar with the given printer",
@@ -366,8 +394,9 @@ pgfCommands = Map.fromList [
" " ++ opt ++ "\t\t" ++ expl |
((opt,_),expl) <- outputFormatsExpl, take 1 expl /= "*"
]),
exec = needPGF $ \opts _ pgf -> prGrammar pgf opts,
exec = getEnv $ \opts _ env -> prGrammar env opts,
flags = [
--"cat",
("file", "set the file name when printing with -pgf option"),
("lang", "select languages for the some options (default all languages)"),
("printer","select the printing format (see flag values above)")
@@ -387,7 +416,6 @@ pgfCommands = Map.fromList [
mkEx ("pg -funs | ? grep \" S ;\" -- show functions with value cat S")
]
}),
("pt", emptyCommandInfo {
longname = "put_tree",
syntax = "pt OPT? TREE",
@@ -401,12 +429,11 @@ pgfCommands = Map.fromList [
examples = [
mkEx "pt -compute (plus one two) -- compute value"
],
exec = needPGF $ \opts arg pgf ->
returnFromExprs False . takeOptNum opts . map (flip (,) 0) . treeOps pgf opts $ toExprs arg,
exec = getEnv $ \ opts arg (Env pgf mos) ->
returnFromExprs . takeOptNum opts . treeOps pgf opts $ toExprs arg,
options = treeOpOptions undefined{-pgf-},
flags = [("number","take at most this many trees")] ++ treeOpFlags undefined{-pgf-}
}),
("rf", emptyCommandInfo {
longname = "read_file",
synopsis = "read string or tree input from a file",
@@ -419,9 +446,10 @@ pgfCommands = Map.fromList [
],
options = [
("lines","return the list of lines, instead of the singleton of all contents"),
("paragraphs","return the list of paragraphs, as separated by empty lines"),
("tree","convert strings into trees")
],
exec = needPGF $ \ opts _ pgf -> do
exec = getEnv $ \ opts _ (Env pgf mos) -> do
let file = valStrOpts "file" "_gftmp" opts
let exprs [] = ([],empty)
exprs ((n,s):ls) | null s
@@ -430,12 +458,12 @@ pgfCommands = Map.fromList [
Just e -> let (es,err) = exprs ls
in case inferExpr pgf e of
Right (e,t) -> (e:es,err)
Left err -> (es,"on line" <+> n <> ':' $$ nest 2 err $$ err)
Left tcerr -> (es,"on line" <+> n <> ':' $$ nest 2 (ppTcError tcerr) $$ err)
Nothing -> let (es,err) = exprs ls
in (es,"on line" <+> n <> ':' <+> "parse error" $$ err)
returnFromLines ls = case exprs ls of
(es, err) | null es -> return $ pipeMessage $ render (err $$ "no trees found")
| otherwise -> return $ pipeWithMessage (map (flip (,) 0) es) (render err)
| otherwise -> return $ pipeWithMessage es (render err)
s <- restricted $ readFile file
case opts of
@@ -444,26 +472,56 @@ pgfCommands = Map.fromList [
_ | isOpt "tree" opts ->
returnFromLines [(1::Int,s)]
_ | isOpt "lines" opts -> return (fromStrings $ lines s)
_ | isOpt "paragraphs" opts -> return (fromStrings $ toParagraphs $ lines s)
_ -> return (fromString s),
flags = [("file","the input file name")]
}),
("rt", emptyCommandInfo {
longname = "rank_trees",
synopsis = "show trees in an order of decreasing probability",
explanation = unlines [
"Order trees from the most to the least probable, using either",
"even distribution in each category (default) or biased as specified",
"by the file given by flag -probs=FILE, where each line has the form",
"'function probability', e.g. 'youPol_Pron 0.01'."
],
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let ts = toExprs arg
pgf <- optProbs opts pgf
let tds = rankTreesByProbs pgf ts
if isOpt "v" opts
then putStrLn $
unlines [showExpr [] t ++ "\t--" ++ show d | (t,d) <- tds]
else return ()
returnFromExprs $ map fst tds,
flags = [
("probs","probabilities from this file (format 'f 0.6' per line)")
],
options = [
("v","show all trees with their probability scores")
],
examples = [
mkEx "p \"you are here\" | rt -probs=probs | pt -number=1 -- most probable result"
]
}),
("tq", emptyCommandInfo {
longname = "translation_quiz",
syntax = "tq -from=LANG -to=LANG (-cat=CAT)? (-probs=FILE)? TREE?",
synopsis = "start a translation quiz",
exec = needPGF $ \ opts arg pgf -> do
from <- optLangFlag "from" pgf opts
to <- optLangFlag "to" pgf opts
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let from = optLangFlag "from" pgf opts
let to = optLangFlag "to" pgf opts
let typ = optType pgf opts
let mt = mexp (toExprs arg)
pgf <- optProbs opts pgf
restricted $ translationQuiz mt pgf from to typ
return void,
flags = [
("from","translate from this language"),
("to","translate to this language"),
("cat","translate in this category"),
("number","the maximum number of questions")
("number","the maximum number of questions"),
("probs","file with biased probabilities for generation")
],
examples = [
mkEx ("tq -from=Eng -to=Swe -- any trees in startcat"),
@@ -471,6 +529,7 @@ pgfCommands = Map.fromList [
]
}),
("vd", emptyCommandInfo {
longname = "visualize_dependency",
synopsis = "show word dependency tree graphically",
@@ -488,7 +547,7 @@ pgfCommands = Map.fromList [
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick).",
"See also 'vp -showdep' for another visualization of dependencies."
],
exec = needPGF $ \ opts arg pgf -> do
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let absname = abstractName pgf
let es = toExprs arg
let debug = isOpt "v" opts
@@ -501,8 +560,8 @@ pgfCommands = Map.fromList [
mclab <- case cnclabels of
"" -> return Nothing
_ -> (Just . getCncDepLabels) `fmap` restricted (readFile cnclabels)
concr <- optLang pgf opts
let grphs = map (graphvizDependencyTree outp debug mlab mclab concr) es
let lang = optLang pgf opts
let grphs = map (graphvizDependencyTree outp debug mlab mclab pgf lang) es
if isOpt "conll2latex" opts
then return $ fromString $ conlls2latexDoc $ stanzas $ unlines $ toStrings arg
else if isFlag "view" opts && valStrOpts "output" "" opts == "latex"
@@ -537,6 +596,7 @@ pgfCommands = Map.fromList [
]
}),
("vp", emptyCommandInfo {
longname = "visualize_parse",
synopsis = "show parse tree graphically",
@@ -548,8 +608,9 @@ pgfCommands = Map.fromList [
"by the view flag. The target format is png, unless overridden by the",
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick)."
],
exec = needPGF $ \opts arg pgf -> do
let es = toExprs arg
exec = getEnv $ \ opts arg (Env pgf mos) -> do
let es = toExprs arg
let lang = optLang pgf opts
let gvOptions = GraphvizOptions {noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts),
noFun = isOpt "nofun" opts || not (isOpt "showfun" opts),
noCat = isOpt "nocat" opts && not (isOpt "showcat" opts),
@@ -562,11 +623,10 @@ pgfCommands = Map.fromList [
leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts
}
let depfile = valStrOpts "file" "" opts
concr <- optLang pgf opts
mlab <- case depfile of
"" -> return Nothing
_ -> (Just . getDepLabels) `fmap` restricted (readFile depfile)
let grphs = map (graphvizDependencyTree "dot" False mlab Nothing concr) es
let grphs = map (graphvizParseTreeDep mlab pgf lang gvOptions) es
if isFlag "view" opts || isFlag "format" opts
then do
let view = optViewGraph opts
@@ -601,6 +661,7 @@ pgfCommands = Map.fromList [
]
}),
("vt", emptyCommandInfo {
longname = "visualize_tree",
synopsis = "show a set of trees graphically",
@@ -613,7 +674,7 @@ pgfCommands = Map.fromList [
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick).",
"With option -mk, use for showing library style function names of form 'mkC'."
],
exec = needPGF $ \opts arg pgf ->
exec = getEnv $ \ opts arg (Env pgf mos) ->
let es = toExprs arg in
if isOpt "mk" opts
then return $ fromString $ unlines $ map (tree2mk pgf) es
@@ -625,7 +686,7 @@ pgfCommands = Map.fromList [
else do
let funs = not (isOpt "nofun" opts)
let cats = not (isOpt "nocat" opts)
let grphs = map (graphvizAbstractTree pgf (graphvizDefaults{noFun=funs,noCat=cats})) es
let grphs = map (graphvizAbstractTree pgf (funs,cats)) es
if isFlag "view" opts || isFlag "format" opts
then do
let view = optViewGraph opts
@@ -647,7 +708,6 @@ pgfCommands = Map.fromList [
("view","program to open the resulting file (default \"open\")")
]
}),
("ai", emptyCommandInfo {
longname = "abstract_info",
syntax = "ai IDENTIFIER or ai EXPR",
@@ -660,150 +720,205 @@ pgfCommands = Map.fromList [
"If a whole expression is given it prints the expression with refined",
"metavariables and the type of the expression."
],
exec = needPGF $ \opts arg pgf -> do
exec = getEnv $ \ opts arg (Env pgf mos) -> do
case toExprs arg of
[e] -> case unApp e of
Just (id, []) -> case functionType pgf id of
Just ty -> do putStrLn (showFun pgf id ty)
putStrLn ("Probability: "++show (exprProbability pgf e))
return void
Nothing -> case categoryContext pgf id of
Just hypos -> do putStrLn ("cat "++id++if null hypos then "" else ' ':showContext [] hypos)
let ls = [showFun pgf fn ty | fn <- functionsByCat pgf id, Just ty <- [functionType pgf fn]]
if null ls
then return ()
else putStrLn (unlines ("":ls))
putStrLn ("Probability: "++show (categoryProbability pgf id))
return void
Nothing -> do putStrLn ("unknown category of function identifier "++show id)
return void
_ -> case inferExpr pgf e of
Left err -> error err
Right (e,ty) -> do putStrLn ("Expression: "++showExpr [] e)
putStrLn ("Type: "++showType [] ty)
putStrLn ("Probability: "++show (exprProbability pgf e))
return void
[EFun id] -> case Map.lookup id (funs (abstract pgf)) of
Just fd -> do putStrLn $ render (ppFun id fd)
let (_,_,_,prob) = fd
putStrLn ("Probability: "++show prob)
return void
Nothing -> case Map.lookup id (cats (abstract pgf)) of
Just cd -> do putStrLn $
render (ppCat id cd $$
if null (functionsToCat pgf id)
then empty
else ' ' $$
vcat [ppFun fid (ty,0,Just ([],[]),0) | (fid,ty) <- functionsToCat pgf id] $$
' ')
let (_,_,prob) = cd
putStrLn ("Probability: "++show prob)
return void
Nothing -> do putStrLn ("unknown category of function identifier "++show id)
return void
[e] -> case inferExpr pgf e of
Left tcErr -> error $ render (ppTcError tcErr)
Right (e,ty) -> do putStrLn ("Expression: "++showExpr [] e)
putStrLn ("Type: "++showType [] ty)
putStrLn ("Probability: "++show (probTree pgf e))
return void
_ -> do putStrLn "a single identifier or expression is expected from the command"
return void,
needsTypeCheck = False
})
]
where
needPGF exec opts ts = do
mb_pgf <- getPGF
case mb_pgf of
Just pgf -> liftSIO $ exec opts ts pgf
_ -> fail "Import a grammar before using this command"
getEnv exec opts ts = liftSIO . exec opts ts =<< getPGFEnv
par pgf opts s = case optOpenTypes opts of
[] -> [parse_ pgf lang (optType pgf opts) (Just dp) s | lang <- optLangs pgf opts]
open_typs -> [parseWithRecovery pgf lang (optType pgf opts) open_typs (Just dp) s | lang <- optLangs pgf opts]
where
dp = valIntOpts "depth" 4 opts
fromParse opts = foldr (joinPiped . fromParse1 opts) void
joinPiped (Piped (es1,ms1)) (Piped (es2,ms2)) = Piped (jA es1 es2,ms1+++-ms2)
where
jA (Exprs es1) (Exprs es2) = Exprs (es1++es2)
-- ^ fromParse1 always output Exprs
fromParse1 opts (s,po) =
case po of
ParseOk ts -> fromExprs (isOpt "show_probs" opts) (takeOptNum opts ts)
ParseFailed i t -> pipeMessage $ "The parser failed at token "
++ show i ++": "
++ show t
ParseIncomplete -> pipeMessage "The sentence is not complete"
fromParse1 opts (s,(po,bs))
| isOpt "bracket" opts = pipeMessage (showBracketedString bs)
| otherwise =
case po of
ParseOk ts -> fromExprs ts
ParseFailed i -> pipeMessage $ "The parser failed at token "
++ show i ++": "
++ show (words s !! max 0 (i-1))
-- ++ " in " ++ show s
ParseIncomplete -> pipeMessage "The sentence is not complete"
TypeError errs ->
pipeMessage . render $
"The parsing is successful but the type checking failed with error(s):"
$$ nest 2 (vcat (map (ppTcError . snd) errs))
optLins pgf opts ts = concatMap (optLin pgf opts) ts
optLins pgf opts ts = case opts of
_ | isOpt "groups" opts ->
concatMap snd $ groupResults
[[(lang, s) | lang <- optLangs pgf opts,s <- linear pgf opts lang t] | t <- ts]
_ -> concatMap (optLin pgf opts) ts
optLin pgf opts t =
case opts of
_ | isOpt "treebank" opts && isOpt "chunks" opts ->
(showCId (abstractName pgf) ++ ": " ++ showExpr [] t) :
[showCId lang ++ ": " ++ li | (lang,li) <- linChunks pgf opts t] --linear pgf opts lang t | lang <- optLangs pgf opts]
_ | isOpt "treebank" opts ->
(abstractName pgf ++ ": " ++ showExpr [] t) :
[concreteName concr ++ ": " ++ s | concr <- optLangs pgf opts, s<-linear opts concr t]
_ -> [s | concr <- optLangs pgf opts, s <- linear opts concr t]
(showCId (abstractName pgf) ++ ": " ++ showExpr [] t) :
[showCId lang ++ ": " ++ s | lang <- optLangs pgf opts, s<-linear pgf opts lang t]
_ | isOpt "tabtreebank" opts ->
return $ concat $ intersperse "\t" $ (showExpr [] t) :
[s | lang <- optLangs pgf opts, s <- linear pgf opts lang t]
_ | isOpt "chunks" opts -> map snd $ linChunks pgf opts t
_ -> [s | lang <- optLangs pgf opts, s<-linear pgf opts lang t]
linChunks pgf opts t =
[(lang, unwords (intersperse "<+>" (map (unlines . linear pgf opts lang) (treeChunks t)))) | lang <- optLangs pgf opts]
linear :: [Option] -> Concr -> Expr -> [String]
linear opts concr = case opts of
_ | isOpt "all" opts -> concat .
map (map snd) . tabularLinearizeAll concr
linear :: PGF -> [Option] -> CId -> Expr -> [String]
linear pgf opts lang = let unl = unlex opts lang in case opts of
_ | isOpt "all" opts -> concat . -- intersperse [[]] .
map (map (unl . snd)) . tabularLinearizes pgf lang
_ | isOpt "list" opts -> (:[]) . commaList . concat .
map (map snd) . tabularLinearizeAll concr
_ | isOpt "table" opts -> concat .
map (map (\(p,v) -> p+++":"+++v)) . tabularLinearizeAll concr
_ | isOpt "bracket" opts -> (:[]) . unwords . map showBracketedString . bracketedLinearize concr
_ -> (:[]) . linearize concr
map (map (unl . snd)) . tabularLinearizes pgf lang
_ | isOpt "table" opts -> concat . -- intersperse [[]] .
map (map (\(p,v) -> p+++":"+++unl v)) . tabularLinearizes pgf lang
_ | isOpt "bracket" opts -> (:[]) . unwords . map showBracketedString . bracketedLinearize pgf lang
_ -> (:[]) . unl . linearize pgf lang
-- replace each non-atomic constructor with mkC, where C is the val cat
tree2mk pgf = showExpr [] . t2m where
t2m t = case unApp t of
Just (cid,ts@(_:_)) -> mkApp (mk cid) (map t2m ts)
_ -> t
mk f = case functionType pgf f of
Just ty -> let (_,cat,_) = unType ty
in "mk" ++ cat
Nothing -> f
Just (cid,ts@(_:_)) -> mkApp (mk cid) (map t2m ts)
_ -> t
mk = mkCId . ("mk" ++) . showCId . lookValCat (abstract pgf)
unlex opts lang = stringOps Nothing (getUnlex opts lang ++ map prOpt opts) ----
getUnlex opts lang = case words (valStrOpts "unlexer" "" opts) of
lexs -> case lookup lang
[(mkCId la,tail le) | lex <- lexs, let (la,le) = span (/='=') lex, not (null le)] of
Just le -> chunks ',' le
_ -> []
commaList [] = []
commaList ws = concat $ head ws : map (", " ++) (tail ws)
-- Proposed logic of coding in unlexing:
-- - If lang has no coding flag, or -to_utf8 is not in opts, just opts are used.
-- - If lang has flag coding=utf8, -to_utf8 is ignored.
-- - If lang has coding=other, and -to_utf8 is in opts, from_other is applied first.
-- THIS DOES NOT WORK UNFORTUNATELY - can't use the grammar flag properly
{-
unlexx pgf opts lang = {- trace (unwords optsC) $ -} stringOps Nothing optsC where ----
optsC = case lookConcrFlag pgf (mkCId lang) (mkCId "coding") of
Just (LStr "utf8") -> filter (/="to_utf8") $ map prOpt opts
Just (LStr other) | isOpt "to_utf8" opts ->
let cod = ("from_" ++ other)
in cod : filter (/=cod) (map prOpt opts)
_ -> map prOpt opts
-}
optRestricted opts pgf =
restrictPGF (\f -> and [hasLin pgf la f | la <- optLangs pgf opts]) pgf
optLang = optLangFlag "lang"
optLangs = optLangsFlag "lang"
optLangFlag flag pgf opts =
case optLangsFlag flag pgf opts of
[] -> fail "no language specified"
(l:ls) -> return l
optLangsFlag f pgf opts = case valStrOpts f "" opts of
"" -> languages pgf
lang -> map (completeLang pgf) (chunks ',' lang)
completeLang pgf la = let cla = (mkCId la) in
if elem cla (languages pgf)
then cla
else (mkCId (showCId (abstractName pgf) ++ la))
optLangsFlag flag pgf opts =
case valStrOpts flag "" opts of
"" -> Map.elems langs
str -> mapMaybe (completeLang pgf) (chunks ',' str)
where
langs = languages pgf
optLangFlag f pgf opts = head $ optLangsFlag f pgf opts ++ [wildCId]
completeLang pgf la =
mplus (Map.lookup la langs)
(Map.lookup (abstractName pgf ++ la) langs)
optOpenTypes opts = case valStrOpts "openclass" "" opts of
"" -> []
cats -> mapMaybe readType (chunks ',' cats)
optProbs opts pgf = case valStrOpts "probs" "" opts of
"" -> return pgf
file -> do
probs <- restricted $ readProbabilitiesFromFile file pgf
return (setProbabilities probs pgf)
optFile opts = valStrOpts "file" "_gftmp" opts
optType pgf opts =
let readOpt str = case readType str of
Just ty -> case checkType pgf ty of
Left err -> error err
Right ty -> ty
Nothing -> error ("Can't parse '"++str++"' as a type")
in maybeStrOpts "cat" (startCat pgf) readOpt opts
let str = valStrOpts "cat" (showCId $ lookStartCat pgf) opts
in case readType str of
Just ty -> case checkType pgf ty of
Left tcErr -> error $ render (ppTcError tcErr)
Right ty -> ty
Nothing -> error ("Can't parse '"++str++"' as a type")
optViewFormat opts = valStrOpts "format" "png" opts
optViewGraph opts = valStrOpts "view" "open" opts
optNum opts = valIntOpts "number" 1 opts
optNumInf opts = valIntOpts "number" 1000000000 opts ---- 10^9
takeOptNum opts = take (optNumInf opts)
returnFromExprs show_p es =
return $
case es of
[] -> pipeMessage "no trees found"
_ -> fromExprs show_p es
returnFromExprs es = return $ case es of
[] -> pipeMessage "no trees found"
_ -> fromExprs es
prGrammar pgf opts
prGrammar (Env pgf mos) opts
| isOpt "pgf" opts = do
let outfile = valStrOpts "file" (abstractName pgf ++ ".pgf") opts
restricted $ writePGF outfile pgf
let pgf1 = if isOpt "opt" opts then optimizePGF pgf else pgf
let outfile = valStrOpts "file" (showCId (abstractName pgf) ++ ".pgf") opts
restricted $ encodeFile outfile pgf1
putStrLn $ "wrote file " ++ outfile
return void
| isOpt "cats" opts = return $ fromString $ unwords $ categories pgf
| isOpt "funs" opts = return $ fromString $ unlines [showFun pgf f ty | f <- functions pgf, Just ty <- [functionType pgf f]]
| isOpt "fullform" opts = return $ fromString $ concatMap prFullFormLexicon $ optLangs pgf opts
| isOpt "langs" opts = return $ fromString $ unwords $ Map.keys $ languages pgf
| isOpt "cats" opts = return $ fromString $ unwords $ map showCId $ categories pgf
| isOpt "funs" opts = return $ fromString $ unlines $ map showFun $ funsigs pgf
| isOpt "fullform" opts = return $ fromString $ concatMap (morpho mos "" prFullFormLexicon) $ optLangs pgf opts
| isOpt "langs" opts = return $ fromString $ unwords $ map showCId $ languages pgf
| isOpt "lexc" opts = return $ fromString $ concatMap prLexcLexicon $ optLangs pgf opts
| isOpt "missing" opts = return $ fromString $ unlines $ [unwords (concreteName concr:":":[f | f <- functions pgf, not (hasLinearization concr f)]) |
concr <- optLangs pgf opts]
| isOpt "words" opts = return $ fromString $ concatMap prAllWords $ optLangs pgf opts
| isOpt "lexc" opts = return $ fromString $ concatMap (morpho mos "" prLexcLexicon) $ optLangs pgf opts
| isOpt "missing" opts = return $ fromString $ unlines $ [unwords (showCId la:":": map showCId cs) |
la <- optLangs pgf opts, let cs = missingLins pgf la]
| isOpt "words" opts = return $ fromString $ concatMap (morpho mos "" prAllWords) $ optLangs pgf opts
| otherwise = do fmt <- readOutputFormat (valStrOpts "printer" "pgf_pretty" opts)
return $ fromString $ concatMap snd $ exportPGF noOptions fmt pgf
showFun pgf id ty = kwd++" "++ id ++ " : " ++ showType [] ty
where
kwd | functionIsConstructor pgf id = "data"
| otherwise = "fun"
funsigs pgf = [(f,ty) | (f,(ty,_,_,_)) <- Map.assocs (funs (abstract pgf))]
showFun (f,ty) = showCId f ++ " : " ++ showType [] ty ++ " ;"
morphos pgf opts s =
[(s,lookupMorpho concr s) | concr <- optLangs pgf opts]
morphos (Env pgf mos) opts s =
[(s,morpho mos [] (\mo -> lookupMorpho mo s) la) | la <- optLangs pgf opts]
morpho mos z f la = maybe z f $ Map.lookup la mos
optMorpho (Env pgf mos) opts = morpho mos (error "no morpho") id (head (optLangs pgf opts))
optClitics opts = case valStrOpts "clitics" "" opts of
"" -> []
@@ -816,28 +931,18 @@ pgfCommands = Map.fromList [
-- ps -f -g s returns g (f s)
treeOps pgf opts s = foldr app s (reverse opts) where
app (OOpt op) | Just (Left f) <- treeOp pgf op = f
app (OFlag op (VId x)) | Just (Right f) <- treeOp pgf op = f x
app (OFlag op (VId x)) | Just (Right f) <- treeOp pgf op = f (mkCId x)
app _ = id
morphoMissing :: Concr -> [String] -> [String]
morphoMissing = morphoClassify False
morphoKnown :: Concr -> [String] -> [String]
morphoKnown = morphoClassify True
morphoClassify :: Bool -> Concr -> [String] -> [String]
morphoClassify k concr ws = [w | w <- ws, k /= null (lookupMorpho concr w), notLiteral w] where
notLiteral w = not (all isDigit w)
treeOpOptions pgf = [(op,expl) | (op,(expl,Left _)) <- allTreeOps pgf]
treeOpFlags pgf = [(op,expl) | (op,(expl,Right _)) <- allTreeOps pgf]
translationQuiz :: Maybe Expr -> PGF -> Concr -> Concr -> Type -> IO ()
translationQuiz :: Maybe Expr -> PGF -> Language -> Language -> Type -> IO ()
translationQuiz mex pgf ig og typ = do
tts <- translationList mex pgf ig og typ infinity
mkQuiz "Welcome to GF Translation Quiz." tts
morphologyQuiz :: Maybe Expr -> PGF -> Concr -> Type -> IO ()
morphologyQuiz :: Maybe Expr -> PGF -> Language -> Type -> IO ()
morphologyQuiz mex pgf ig typ = do
tts <- morphologyList mex pgf ig typ infinity
mkQuiz "Welcome to GF Morphology Quiz." tts
@@ -846,28 +951,30 @@ morphologyQuiz mex pgf ig typ = do
infinity :: Int
infinity = 256
prLexcLexicon :: Concr -> String
prLexcLexicon concr =
unlines $ "Multichar_Symbols":multichars:"":"LEXICON Root" : [prLexc l p ++ ":" ++ w ++ " # ;" | (w,lps) <- morpho, (l,p,_) <- lps] ++ ["END"]
prLexcLexicon :: Morpho -> String
prLexcLexicon mo =
unlines $ "Multichar_Symbols":multichars:"":"LEXICON Root" : [prLexc l p ++ ":" ++ w ++ " # ;" | (w,lps) <- morpho, (l,p) <- lps] ++ ["END"]
where
morpho = fullFormLexicon concr
prLexc l p = l ++ concat (mkTags (words p))
morpho = fullFormLexicon mo
prLexc l p = showCId l ++ concat (mkTags (words p))
mkTags p = case p of
"s":ws -> mkTags ws --- remove record field
ws -> map ('+':) ws
multichars = unwords $ nub $ concat [mkTags (words p) | (w,lps) <- morpho, (l,p,_) <- lps]
multichars = unwords $ nub $ concat [mkTags (words p) | (w,lps) <- morpho, (l,p) <- lps]
-- thick_A+(AAdj+Posit+Gen):thick's # ;
prFullFormLexicon :: Concr -> String
prFullFormLexicon concr =
unlines (map prMorphoAnalysis (fullFormLexicon concr))
prFullFormLexicon :: Morpho -> String
prFullFormLexicon mo =
unlines (map prMorphoAnalysis (fullFormLexicon mo))
prAllWords :: Concr -> String
prAllWords concr =
unwords [w | (w,_) <- fullFormLexicon concr]
prAllWords :: Morpho -> String
prAllWords mo =
unwords [w | (w,_) <- fullFormLexicon mo]
prMorphoAnalysis :: (String,[(Lemma,Analysis)]) -> String
prMorphoAnalysis (w,lps) =
unlines (w:[l ++ " : " ++ p ++ show prob | (l,p,prob) <- lps])
unlines (w:[showCId l ++ " : " ++ p | (l,p) <- lps])
viewGraphviz :: String -> String -> String -> [String] -> SIO CommandOutput
viewGraphviz view format name grphs = do
@@ -912,7 +1019,3 @@ stanzas = map unlines . chop . lines where
chop ls = case break (=="") ls of
(ls1,[]) -> [ls1]
(ls1,_:ls2) -> ls1 : chop ls2
#if !(MIN_VERSION_base(4,9,0))
errorWithoutStackTrace = error
#endif

View File

@@ -0,0 +1,831 @@
{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}
module GF.Command.Commands2 (
PGFEnv,HasPGFEnv(..),pgf,concs,pgfEnv,emptyPGFEnv,pgfCommands,
options, flags,
) where
import Prelude hiding (putStrLn,(<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import PGF2
import qualified PGF as H
import GF.Compile.ToAPI(exprToAPI)
import GF.Infra.UseIO(writeUTF8File)
import GF.Infra.SIO(MonadSIO,liftSIO,putStrLn,restricted,restrictedSystem)
import GF.Command.Abstract
import GF.Command.CommandInfo
import GF.Data.Operations
import Data.List(intersperse,intersect,nub,sortBy)
import Data.Maybe
import qualified Data.Map as Map
import GF.Text.Pretty
import Control.Monad(mplus)
import qualified Control.Monad.Fail as Fail
data PGFEnv = Env {pgf::Maybe PGF,concs::Map.Map ConcName Concr}
pgfEnv pgf = Env (Just pgf) (languages pgf)
emptyPGFEnv = Env Nothing Map.empty
class (Fail.MonadFail m,MonadSIO m) => HasPGFEnv m where getPGFEnv :: m PGFEnv
instance (Monad m,HasPGFEnv m) => TypeCheckArg m where
typeCheckArg e = do env <- getPGFEnv
case pgf env of
Just gr -> either fail
(return . hsExpr . fst)
(inferExpr gr (cExpr e))
Nothing -> fail "Import a grammar before using this command"
pgfCommands :: HasPGFEnv m => Map.Map String (CommandInfo m)
pgfCommands = Map.fromList [
("aw", emptyCommandInfo {
longname = "align_words",
synopsis = "show word alignments between languages graphically",
explanation = unlines [
"Prints a set of strings in the .dot format (the graphviz format).",
"The graph can be saved in a file by the wf command as usual.",
"If the -view flag is defined, the graph is saved in a temporary file",
"which is processed by graphviz and displayed by the program indicated",
"by the flag. The target format is postscript, unless overridden by the",
"flag -format."
],
exec = needPGF $ \opts es env -> do
let cncs = optConcs env opts
if isOpt "giza" opts
then if length cncs == 2
then let giz = map (gizaAlignment pgf (snd (cncs !! 0)) (snd (cncs !! 1)) . cExpr) (toExprs es)
lsrc = unlines $ map (\(x,_,_) -> x) giz
ltrg = unlines $ map (\(_,x,_) -> x) giz
align = unlines $ map (\(_,_,x) -> x) giz
grph = if null (toExprs es) then [] else lsrc ++ "\n--end_source--\n\n"++ltrg++"\n-end_target--\n\n"++align
in return (fromString grph)
else error "For giza alignment you need exactly two languages"
else let gvOptions=graphvizDefaults{leafFont = valStrOpts "font" "" opts,
leafColor = valStrOpts "color" "" opts,
leafEdgeStyle = valStrOpts "edgestyle" "" opts
}
grph = if null (toExprs es) then [] else graphvizWordAlignment (map snd cncs) gvOptions (cExpr (head (toExprs es)))
in if isFlag "view" opts || isFlag "format" opts
then do let file s = "_grph." ++ s
let view = optViewGraph opts
let format = optViewFormat opts
restricted $ writeUTF8File (file "dot") grph
restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
restrictedSystem $ view ++ " " ++ file format
return void
else return (fromString grph),
examples = [
("gr | aw" , "generate a tree and show word alignment as graph script"),
("gr | aw -view=\"open\"" , "generate a tree and display alignment on Mac"),
("gr | aw -view=\"eog\"" , "generate a tree and display alignment on Ubuntu"),
("gt | aw -giza | wf -file=aligns" , "generate trees, send giza alignments to file")
],
options = [
("giza", "show alignments in the Giza format; the first two languages")
],
flags = [
("format","format of the visualization file (default \"png\")"),
("lang", "alignments for this list of languages (default: all)"),
("view", "program to open the resulting file"),
("font", "font for the words"),
("color", "color for the words"),
("edgestyle", "the style for links between words")
]
}),
{-
("eb", emptyCommandInfo {
longname = "example_based",
syntax = "eb (-probs=FILE | -lang=LANG)* -file=FILE.gfe",
synopsis = "converts .gfe files to .gf files by parsing examples to trees",
explanation = unlines [
"Reads FILE.gfe and writes FILE.gf. Each expression of form",
"'%ex CAT QUOTEDSTRING' in FILE.gfe is replaced by a syntax tree.",
"This tree is the first one returned by the parser; a biased ranking",
"can be used to regulate the order. If there are more than one parses",
"the rest are shown in comments, with probabilities if the order is biased.",
"The probabilities flag and configuration file is similar to the commands",
"gr and rt. Notice that the command doesn't change the environment,",
"but the resulting .gf file must be imported separately."
],
options = [
("api","convert trees to overloaded API expressions (using Syntax not Lang)")
],
flags = [
("file","the file to be converted (suffix .gfe must be given)"),
("lang","the language in which to parse"),
("probs","file with probabilities to rank the parses")
],
exec = \env@(pgf, mos) opts _ -> do
let file = optFile opts
pgf <- optProbs opts pgf
let printer = if (isOpt "api" opts) then exprToAPI else (H.showExpr [])
let conf = configureExBased pgf (optMorpho env opts) (optLang pgf opts) printer
(file',ws) <- restricted $ parseExamplesInGrammar conf file
if null ws then return () else putStrLn ("unknown words: " ++ unwords ws)
return (fromString ("wrote " ++ file')),
needsTypeCheck = False
}),
-}
{-
("gr", emptyCommandInfo {
longname = "generate_random",
synopsis = "generate random trees in the current abstract syntax",
syntax = "gr [-cat=CAT] [-number=INT]",
examples = [
mkEx "gr -- one tree in the startcat of the current grammar",
mkEx "gr -cat=NP -number=16 -- 16 trees in the category NP",
mkEx "gr -lang=LangHin,LangTha -cat=Cl -- Cl, both in LangHin and LangTha",
mkEx "gr -probs=FILE -- generate with bias",
mkEx "gr (AdjCN ? (UseN ?)) -- generate trees of form (AdjCN ? (UseN ?))"
],
explanation = unlines [
"Generates a list of random trees, by default one tree.",
"If a tree argument is given, the command completes the Tree with values to",
"all metavariables in the tree. The generation can be biased by probabilities,",
"given in a file in the -probs flag."
],
flags = [
("cat","generation category"),
("lang","uses only functions that have linearizations in all these languages"),
("number","number of trees generated"),
("depth","the maximum generation depth"),
("probs", "file with biased probabilities (format 'f 0.4' one by line)")
],
exec = \env@(pgf, mos) opts xs -> do
pgf <- optProbs opts (optRestricted opts pgf)
gen <- newStdGen
let dp = valIntOpts "depth" 4 opts
let ts = case mexp xs of
Just ex -> H.generateRandomFromDepth gen pgf ex (Just dp)
Nothing -> H.generateRandomDepth gen pgf (optType pgf opts) (Just dp)
returnFromExprs $ take (optNum opts) ts
}),
-}
("gt", emptyCommandInfo {
longname = "generate_trees",
synopsis = "generates a list of trees, by default exhaustive",
flags = [("cat","the generation category"),
("number","the number of trees generated")],
examples = [
mkEx "gt -- all trees in the startcat",
mkEx "gt -cat=NP -number=16 -- 16 trees in the category NP"],
exec = needPGF $ \ opts _ env@(pgf,_) ->
let ts = map fst (generateAll pgf cat)
cat = optType pgf opts
in returnFromCExprs (takeOptNum opts ts),
needsTypeCheck = False
}),
("i", emptyCommandInfo {
longname = "import",
synopsis = "import a grammar from a compiled .pgf file",
explanation = unlines [
"Reads a grammar from a compiled .pgf file.",
"Old modules are discarded.",
{-
"The grammar parser depends on the file name suffix:",
" .cf context-free (labelled BNF) source",
" .ebnf extended BNF source",
" .gfm multi-module GF source",
" .gf normal GF source",
" .gfo compiled GF source",
-}
" .pgf precompiled grammar in Portable Grammar Format"
],
flags = [
-- ("probs","file with biased probabilities for generation")
],
options = [
-- ["gfo", "src", "no-cpu", "cpu", "quiet", "verbose"]
-- ("retain","retain operations (used for cc command)"),
-- ("src", "force compilation from source"),
-- ("v", "be verbose - show intermediate status information")
],
needsTypeCheck = False
}),
("l", emptyCommandInfo {
longname = "linearize",
synopsis = "convert an abstract syntax expression to string",
explanation = unlines [
"Shows the linearization of a Tree by the grammars in scope.",
"The -lang flag can be used to restrict this to fewer languages.",
"A sequence of string operations (see command ps) can be given",
"as options, and works then like a pipe to the ps command, except",
"that it only affect the strings, not e.g. the table labels.",
"These can be given separately to each language with the unlexer flag",
"whose results are prepended to the other lexer flags. The value of the",
"unlexer flag is a space-separated list of comma-separated string operation",
"sequences; see example."
],
examples = [
mkEx "l -lang=LangSwe,LangNor no_Utt -- linearize a tree to LangSwe and LangNor",
mkEx "gr -lang=LangHin -cat=Cl | l -table -to_devanagari -- hindi table",
mkEx "l -unlexer=\"LangAra=to_arabic LangHin=to_devanagari\" -- different unlexers"
],
exec = needPGF $ \ opts arg env ->
return . fromStrings . optLins env opts . map cExpr $ toExprs arg,
options = [
("all", "show all forms and variants, one by line (cf. l -list)"),
("bracket","show tree structure with brackets and paths to nodes"),
("groups", "all languages, grouped by lang, remove duplicate strings"),
("list","show all forms and variants, comma-separated on one line (cf. l -all)"),
("multi","linearize to all languages (default)"),
("table","show all forms labelled by parameters"),
("treebank","show the tree and tag linearizations with language names")
],
flags = [
("lang","the languages of linearization (comma-separated, no spaces)")
]
}),
("ma", emptyCommandInfo {
longname = "morpho_analyse",
synopsis = "print the morphological analyses of the (multiword) expression in the string",
explanation = unlines [
"Prints all the analyses of the (multiword) expression in the input string,",
"using the morphological analyser of the actual grammar (see command pg)"
],
exec = needPGF $ \opts args env ->
return ((fromString . unlines .
map prMorphoAnalysis . concatMap (morphos env opts) . toStrings) args),
flags = [
("lang","the languages of analysis (comma-separated, no spaces)")
]
}),
{-
("mq", emptyCommandInfo {
longname = "morpho_quiz",
synopsis = "start a morphology quiz",
syntax = "mq (-cat=CAT)? (-probs=FILE)? TREE?",
exec = \env@(pgf, mos) opts xs -> do
let lang = optLang pgf opts
let typ = optType pgf opts
pgf <- optProbs opts pgf
let mt = mexp xs
restricted $ morphologyQuiz mt pgf lang typ
return void,
flags = [
("lang","language of the quiz"),
("cat","category of the quiz"),
("number","maximum number of questions"),
("probs","file with biased probabilities for generation")
]
}),
-}
("p", emptyCommandInfo {
longname = "parse",
synopsis = "parse a string to abstract syntax expression",
explanation = unlines [
"Shows all trees returned by parsing a string in the grammars in scope.",
"The -lang flag can be used to restrict this to fewer languages.",
"The default start category can be overridden by the -cat flag.",
"See also the ps command for lexing and character encoding."
],
flags = [
("cat","target category of parsing"),
("lang","the languages of parsing (comma-separated, no spaces)"),
("number","maximum number of trees returned")
],
examples = [
mkEx "p \"this fish is fresh\" | l -lang=Swe -- try parsing with all languages and translate the successful parses to Swedish"
],
exec = needPGF $ \ opts ts env -> return . cParse env opts $ toStrings ts
}),
("pg", emptyCommandInfo {
longname = "print_grammar",
synopsis = "prints different information about the grammar",
exec = needPGF $ \opts _ env -> prGrammar env opts,
options = [
("cats", "show just the names of abstract syntax categories"),
("fullform", "print the fullform lexicon"),
("funs", "show just the names and types of abstract syntax functions"),
("langs", "show just the names of top concrete syntax modules"),
("lexc", "print the lexicon in Xerox LEXC format"),
("missing","show just the names of functions that have no linearization"),
("words", "print the list of words")
],
flags = [
("lang","the languages that need to be printed")
],
examples = [
mkEx "pg -langs -- show the names of top concrete syntax modules",
mkEx "pg -funs | ? grep \" S ;\" -- show functions with value cat S"
]
}),
{-
("pt", emptyCommandInfo {
longname = "put_tree",
syntax = "pt OPT? TREE",
synopsis = "return a tree, possibly processed with a function",
explanation = unlines [
"Returns a tree obtained from its argument tree by applying",
"tree processing functions in the order given in the command line",
"option list. Thus 'pt -f -g s' returns g (f s). Typical tree processors",
"are type checking and semantic computation."
],
examples = [
mkEx "pt -compute (plus one two) -- compute value",
mkEx "p \"4 dogs love 5 cats\" | pt -transfer=digits2numeral | l -- four...five..."
],
exec = \env@(pgf, mos) opts ->
returnFromExprs . takeOptNum opts . treeOps pgf opts,
options = treeOpOptions undefined{-pgf-},
flags = [("number","take at most this many trees")] ++ treeOpFlags undefined{-pgf-}
}),
-}
("rf", emptyCommandInfo {
longname = "read_file",
synopsis = "read string or tree input from a file",
explanation = unlines [
"Reads input from file. The filename must be in double quotes.",
"The input is interpreted as a string by default, and can hence be",
"piped e.g. to the parse command. The option -tree interprets the",
"input as a tree, which can be given e.g. to the linearize command.",
"The option -lines will result in a list of strings or trees, one by line."
],
options = [
("lines","return the list of lines, instead of the singleton of all contents"),
("tree","convert strings into trees")
],
exec = needPGF $ \opts _ env@(pgf, mos) -> do
let file = optFile opts
let exprs [] = ([],empty)
exprs ((n,s):ls) | null s
= exprs ls
exprs ((n,s):ls) = case readExpr s of
Just e -> let (es,err) = exprs ls
in case inferExpr pgf e of
Right (e,t) -> (e:es,err)
Left msg -> (es,"on line" <+> n <> ':' $$ msg $$ err)
Nothing -> let (es,err) = exprs ls
in (es,"on line" <+> n <> ':' <+> "parse error" $$ err)
returnFromLines ls = case exprs ls of
(es, err) | null es -> return $ pipeMessage $ render (err $$ "no trees found")
| otherwise -> return $ pipeWithMessage (map hsExpr es) (render err)
s <- restricted $ readFile file
case opts of
_ | isOpt "lines" opts && isOpt "tree" opts ->
returnFromLines (zip [1::Int ..] (lines s))
_ | isOpt "tree" opts ->
returnFromLines [(1::Int,s)]
_ | isOpt "lines" opts -> return (fromStrings $ lines s)
_ -> return (fromString s),
flags = [("file","the input file name")]
}),
("rt", emptyCommandInfo {
longname = "rank_trees",
synopsis = "show trees in an order of decreasing probability",
explanation = unlines [
"Order trees from the most to the least probable, using either",
"even distribution in each category (default) or biased as specified",
"by the file given by flag -probs=FILE, where each line has the form",
"'function probability', e.g. 'youPol_Pron 0.01'."
],
exec = needPGF $ \opts es env@(pgf, _) -> do
let tds = sortBy (\(_,p) (_,q) -> compare p q)
[(t, treeProbability pgf t) | t <- map cExpr (toExprs es)]
if isOpt "v" opts
then putStrLn $
unlines [PGF2.showExpr [] t ++ "\t--" ++ show d | (t,d) <- tds]
else return ()
returnFromExprs $ map (hsExpr . fst) tds,
flags = [
("probs","probabilities from this file (format 'f 0.6' per line)")
],
options = [
("v","show all trees with their probability scores")
],
examples = [
mkEx "p \"you are here\" | rt -probs=probs | pt -number=1 -- most probable result"
]
}),
{-
("tq", emptyCommandInfo {
longname = "translation_quiz",
syntax = "tq -from=LANG -to=LANG (-cat=CAT)? (-probs=FILE)? TREE?",
synopsis = "start a translation quiz",
exec = \env@(pgf, mos) opts xs -> do
let from = optLangFlag "from" pgf opts
let to = optLangFlag "to" pgf opts
let typ = optType pgf opts
let mt = mexp xs
pgf <- optProbs opts pgf
restricted $ translationQuiz mt pgf from to typ
return void,
flags = [
("from","translate from this language"),
("to","translate to this language"),
("cat","translate in this category"),
("number","the maximum number of questions"),
("probs","file with biased probabilities for generation")
],
examples = [
mkEx ("tq -from=Eng -to=Swe -- any trees in startcat"),
mkEx ("tq -from=Eng -to=Swe (AdjCN (PositA ?2) (UseN ?)) -- only trees of this form")
]
}),
("vd", emptyCommandInfo {
longname = "visualize_dependency",
synopsis = "show word dependency tree graphically",
explanation = unlines [
"Prints a dependency tree in the .dot format (the graphviz format, default)",
"or the CoNLL/MaltParser format (flag -output=conll for training, malt_input",
"for unanalysed input).",
"By default, the last argument is the head of every abstract syntax",
"function; moreover, the head depends on the head of the function above.",
"The graph can be saved in a file by the wf command as usual.",
"If the -view flag is defined, the graph is saved in a temporary file",
"which is processed by graphviz and displayed by the program indicated",
"by the flag. The target format is png, unless overridden by the",
"flag -format."
],
exec = \env@(pgf, mos) opts es -> do
let debug = isOpt "v" opts
let file = valStrOpts "file" "" opts
let outp = valStrOpts "output" "dot" opts
mlab <- case file of
"" -> return Nothing
_ -> (Just . H.getDepLabels . lines) `fmap` restricted (readFile file)
let lang = optLang pgf opts
let grphs = unlines $ map (H.graphvizDependencyTree outp debug mlab Nothing pgf lang) es
if isFlag "view" opts || isFlag "format" opts then do
let file s = "_grphd." ++ s
let view = optViewGraph opts
let format = optViewFormat opts
restricted $ writeUTF8File (file "dot") grphs
restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
restrictedSystem $ view ++ " " ++ file format
return void
else return $ fromString grphs,
examples = [
mkEx "gr | vd -- generate a tree and show dependency tree in .dot",
mkEx "gr | vd -view=open -- generate a tree and display dependency tree on a Mac",
mkEx "gr -number=1000 | vd -file=dep.labels -output=malt -- generate training treebank",
mkEx "gr -number=100 | vd -file=dep.labels -output=malt_input -- generate test sentences"
],
options = [
("v","show extra information")
],
flags = [
("file","configuration file for labels per fun, format 'fun l1 ... label ... l2'"),
("format","format of the visualization file (default \"png\")"),
("output","output format of graph source (default \"dot\")"),
("view","program to open the resulting file (default \"open\")"),
("lang","the language of analysis")
]
}),
-}
("vp", emptyCommandInfo {
longname = "visualize_parse",
synopsis = "show parse tree graphically",
explanation = unlines [
"Prints a parse tree in the .dot format (the graphviz format).",
"The graph can be saved in a file by the wf command as usual.",
"If the -view flag is defined, the graph is saved in a temporary file",
"which is processed by graphviz and displayed by the program indicated",
"by the flag. The target format is png, unless overridden by the",
"flag -format."
],
exec = needPGF $ \opts arg env@(pgf, concs) ->
do let es = toExprs arg
let concs = optConcs env opts
let gvOptions=graphvizDefaults{noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts),
noFun = isOpt "nofun" opts || not (isOpt "showfun" opts),
noCat = isOpt "nocat" opts && not (isOpt "showcat" opts),
nodeFont = valStrOpts "nodefont" "" opts,
leafFont = valStrOpts "leaffont" "" opts,
nodeColor = valStrOpts "nodecolor" "" opts,
leafColor = valStrOpts "leafcolor" "" opts,
nodeEdgeStyle = valStrOpts "nodeedgestyle" "solid" opts,
leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts
}
let grph= if null es || null concs
then []
else graphvizParseTree (snd (head concs)) gvOptions (cExpr (head es))
if isFlag "view" opts || isFlag "format" opts then do
let file s = "_grph." ++ s
let view = optViewGraph opts
let format = optViewFormat opts
restricted $ writeUTF8File (file "dot") grph
restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
restrictedSystem $ view ++ " " ++ file format
return void
else return $ fromString grph,
examples = [
mkEx "p -lang=Eng \"John walks\" | vp -- generate a tree and show parse tree as .dot script",
mkEx "gr | vp -view=\"open\" -- generate a tree and display parse tree on a Mac"
],
options = [
("showcat","show categories in the tree nodes (default)"),
("nocat","don't show categories"),
("showfun","show function names in the tree nodes"),
("nofun","don't show function names (default)"),
("showleaves","show the leaves of the tree (default)"),
("noleaves","don't show the leaves of the tree (i.e., only the abstract tree)")
],
flags = [
("lang","the language to visualize"),
("format","format of the visualization file (default \"png\")"),
("view","program to open the resulting file (default \"open\")"),
("nodefont","font for tree nodes (default: Times -- graphviz standard font)"),
("leaffont","font for tree leaves (default: nodefont)"),
("nodecolor","color for tree nodes (default: black -- graphviz standard color)"),
("leafcolor","color for tree leaves (default: nodecolor)"),
("nodeedgestyle","edge style between tree nodes (solid/dashed/dotted/bold, default: solid)"),
("leafedgestyle","edge style for links to leaves (solid/dashed/dotted/bold, default: dashed)")
]
}),
("vt", emptyCommandInfo {
longname = "visualize_tree",
synopsis = "show a set of trees graphically",
explanation = unlines [
"Prints a set of trees in the .dot format (the graphviz format).",
"The graph can be saved in a file by the wf command as usual.",
"If the -view flag is defined, the graph is saved in a temporary file",
"which is processed by graphviz and displayed by the program indicated",
"by the flag. The target format is postscript, unless overridden by the",
"flag -format."
],
exec = needPGF $ \opts arg env@(pgf, _) ->
let es = toExprs arg in
if isOpt "api" opts
then do
mapM_ (putStrLn . exprToAPI) es
return void
else do
let gvOptions=graphvizDefaults{noFun = isOpt "nofun" opts,
noCat = isOpt "nocat" opts,
nodeFont = valStrOpts "nodefont" "" opts,
nodeColor = valStrOpts "nodecolor" "" opts,
nodeEdgeStyle = valStrOpts "nodeedgestyle" "solid" opts
}
let grph = unlines (map (graphvizAbstractTree pgf gvOptions . cExpr) es)
if isFlag "view" opts || isFlag "format" opts then do
let file s = "_grph." ++ s
let view = optViewGraph opts
let format = optViewFormat opts
restricted $ writeUTF8File (file "dot") grph
restrictedSystem $ "dot -T" ++ format ++ " " ++ file "dot" ++ " > " ++ file format
restrictedSystem $ view ++ " " ++ file format
return void
else return $ fromString grph,
examples = [
mkEx "p \"hello\" | vt -- parse a string and show trees as graph script",
mkEx "p \"hello\" | vt -view=\"open\" -- parse a string and display trees on a Mac"
],
options = [
("api", "show the tree with function names converted to 'mkC' with value cats C"),
("nofun","don't show functions but only categories"),
("nocat","don't show categories but only functions")
],
flags = [
("format","format of the visualization file (default \"png\")"),
("view","program to open the resulting file (default \"open\")"),
("nodefont","font for tree nodes (default: Times -- graphviz standard font)"),
("nodecolor","color for tree nodes (default: black -- graphviz standard color)"),
("nodeedgestyle","edge style between tree nodes (solid/dashed/dotted/bold, default: solid)")
]
}),
("ai", emptyCommandInfo {
longname = "abstract_info",
syntax = "ai IDENTIFIER or ai EXPR",
synopsis = "Provides an information about a function, an expression or a category from the abstract syntax",
explanation = unlines [
"The command has one argument which is either function, expression or",
"a category defined in the abstract syntax of the current grammar. ",
"If the argument is a function then its type is printed out.",
"If it is a category then the category definition is printed.",
"If a whole expression is given it prints the expression with refined",
"metavariables and the type of the expression."
],
exec = needPGF $ \opts args env@(pgf,cncs) ->
case map cExpr (toExprs args) of
[e] -> case unApp e of
Just (id,[]) -> return (fromString
(case functionType pgf id of
Just ty -> showFun id ty
Nothing -> let funs = functionsByCat pgf id
in showCat id funs))
where
showCat c funs = "cat "++c++
" ;\n\n"++
unlines [showFun f ty| f<-funs,
Just ty <- [functionType pgf f]]
showFun f ty = "fun "++f++" : "++showType [] ty++" ;"
_ -> case inferExpr pgf e of
Left msg -> error msg
Right (e,ty) -> do putStrLn ("Expression: "++PGF2.showExpr [] e)
putStrLn ("Type: "++PGF2.showType [] ty)
putStrLn ("Probability: "++show (treeProbability pgf e))
return void
_ -> do putStrLn "a single function name or category name is expected"
return void,
needsTypeCheck = False
})
]
where
cParse env@(pgf,_) opts ss =
parsed [ parse cnc cat s | s<-ss,(lang,cnc)<-cncs]
where
cat = optType pgf opts
cncs = optConcs env opts
parsed rs = Piped (Exprs ts,unlines msgs)
where
ts = [hsExpr t|ParseOk ts<-rs,(t,p)<-takeOptNum opts ts]
msgs = concatMap mkMsg rs
mkMsg (ParseOk ts) = (map (PGF2.showExpr [] . fst).takeOptNum opts) ts
mkMsg (ParseFailed _ tok) = ["Parse failed: "++tok]
mkMsg (ParseIncomplete) = ["The sentence is incomplete"]
optLins env opts ts = case opts of
_ | isOpt "groups" opts ->
concatMap snd $ groupResults
[[(lang, s) | (lang,concr) <- optConcs env opts,s <- linear opts lang concr t] | t <- ts]
_ -> concatMap (optLin env opts) ts
optLin env@(pgf,_) opts t =
case opts of
_ | isOpt "treebank" opts ->
(abstractName pgf ++ ": " ++ PGF2.showExpr [] t) :
[lang ++ ": " ++ s | (lang,concr) <- optConcs env opts, s<-linear opts lang concr t]
_ -> [s | (lang,concr) <- optConcs env opts, s<-linear opts lang concr t]
linear :: [Option] -> ConcName -> Concr -> PGF2.Expr -> [String]
linear opts lang concr = case opts of
_ | isOpt "all" opts -> concat . map (map snd) . tabularLinearizeAll concr
_ | isOpt "list" opts -> (:[]) . commaList .
concatMap (map snd) . tabularLinearizeAll concr
_ | isOpt "table" opts -> concatMap (map (\(p,v) -> p+++":"+++v)) . tabularLinearizeAll concr
_ | isOpt "bracket" opts -> (:[]) . unwords . map showBracketedString . bracketedLinearize concr
_ -> (:[]) . linearize concr
groupResults :: [[(ConcName,String)]] -> [(ConcName,[String])]
groupResults = Map.toList . foldr more Map.empty . start . concat
where
start ls = [(l,[s]) | (l,s) <- ls]
more (l,s) =
Map.insertWith (\ [x] xs -> if elem x xs then xs else (x : xs)) l s
optConcs = optConcsFlag "lang"
optConcsFlag f (pgf,cncs) opts =
case valStrOpts f "" opts of
"" -> Map.toList cncs
lang -> mapMaybe pickLang (chunks ',' lang)
where
pickLang l = pick l `mplus` pick fl
where
fl = abstractName pgf++l
pick l = (,) l `fmap` Map.lookup l cncs
{-
-- replace each non-atomic constructor with mkC, where C is the val cat
tree2mk pgf = H.showExpr [] . t2m where
t2m t = case H.unApp t of
Just (cid,ts@(_:_)) -> H.mkApp (mk cid) (map t2m ts)
_ -> t
mk = H.mkCId . ("mk" ++) . H.showCId . H.lookValCat (H.abstract pgf)
unlex opts lang = stringOps Nothing (getUnlex opts lang ++ map prOpt opts) ----
getUnlex opts lang = case words (valStrOpts "unlexer" "" opts) of
lexs -> case lookup lang
[(H.mkCId la,tail le) | lex <- lexs, let (la,le) = span (/='=') lex, not (null le)] of
Just le -> chunks ',' le
_ -> []
-}
commaList [] = []
commaList ws = concat $ head ws : map (", " ++) (tail ws)
optFile opts = valStrOpts "file" "_gftmp" opts
optType pgf opts =
case listFlags "cat" opts of
v:_ -> let str = valueString v
in case readType str of
Just ty -> case checkType pgf ty of
Left msg -> error msg
Right ty -> ty
Nothing -> error ("Can't parse '"++str++"' as a type")
_ -> startCat pgf
optViewFormat opts = valStrOpts "format" "png" opts
optViewGraph opts = valStrOpts "view" "open" opts
{-
optNum opts = valIntOpts "number" 1 opts
-}
optNumInf opts = valIntOpts "number" 1000000000 opts ---- 10^9
takeOptNum opts = take (optNumInf opts)
returnFromCExprs = returnFromExprs . map hsExpr
returnFromExprs es =
return $ case es of
[] -> pipeMessage "no trees found"
_ -> fromExprs es
prGrammar env@(pgf,cncs) opts
| isOpt "langs" opts = return . fromString . unwords $ (map fst (optConcs env opts))
| isOpt "cats" opts = return . fromString . unwords $ categories pgf
| isOpt "funs" opts = return . fromString . unwords $ functions pgf
| isOpt "missing" opts = return . fromString . unwords $
[f | f <- functions pgf, not (and [hasLinearization concr f | (_,concr) <- optConcs env opts])]
| isOpt "fullform" opts = return $ fromString $ concatMap (prFullFormLexicon . snd) $ optConcs env opts
| isOpt "words" opts = return $ fromString $ concatMap (prAllWords . snd) $ optConcs env opts
| isOpt "lexc" opts = return $ fromString $ concatMap (prLexcLexicon . snd) $ optConcs env opts
| otherwise = return void
gizaAlignment pgf src_cnc tgt_cnc e =
let src_res = alignWords src_cnc e
tgt_res = alignWords tgt_cnc e
alignment = [show i++"-"++show j | (i,(_,src_fids)) <- zip [0..] src_res, (j,(_,tgt_fids)) <- zip [0..] tgt_res, not (null (intersect src_fids tgt_fids))]
in (unwords (map fst src_res), unwords (map fst tgt_res), unwords alignment)
morphos env opts s =
[(s,res) | (lang,concr) <- optConcs env opts, let res = lookupMorpho concr s, not (null res)]
{-
mexp xs = case xs of
t:_ -> Just t
_ -> Nothing
-}
-- ps -f -g s returns g (f s)
{-
treeOps pgf opts s = foldr app s (reverse opts) where
app (OOpt op) | Just (Left f) <- treeOp pgf op = f
app (OFlag op (VId x)) | Just (Right f) <- treeOp pgf op = f (H.mkCId x)
app _ = id
treeOpOptions pgf = [(op,expl) | (op,(expl,Left _)) <- allTreeOps pgf]
treeOpFlags pgf = [(op,expl) | (op,(expl,Right _)) <- allTreeOps pgf]
translationQuiz :: Maybe H.Expr -> H.PGF -> H.Language -> H.Language -> H.Type -> IO ()
translationQuiz mex pgf ig og typ = do
tts <- translationList mex pgf ig og typ infinity
mkQuiz "Welcome to GF Translation Quiz." tts
morphologyQuiz :: Maybe H.Expr -> H.PGF -> H.Language -> H.Type -> IO ()
morphologyQuiz mex pgf ig typ = do
tts <- morphologyList mex pgf ig typ infinity
mkQuiz "Welcome to GF Morphology Quiz." tts
-- | the maximal number of precompiled quiz problems
infinity :: Int
infinity = 256
-}
prLexcLexicon :: Concr -> String
prLexcLexicon concr =
unlines $ "Multichar_Symbols":multichars:"":"LEXICON Root" : [prLexc l p ++ ":" ++ w ++ " # ;" | (w,lps) <- morpho, (l,p,_) <- lps] ++ ["END"]
where
morpho = fullFormLexicon concr
prLexc l p = l ++ concat (mkTags (words p))
mkTags p = case p of
"s":ws -> mkTags ws --- remove record field
ws -> map ('+':) ws
multichars = unwords $ nub $ concat [mkTags (words p) | (w,lps) <- morpho, (l,p,_) <- lps]
-- thick_A+(AAdj+Posit+Gen):thick's # ;
prFullFormLexicon :: Concr -> String
prFullFormLexicon concr =
unlines (map prMorphoAnalysis (fullFormLexicon concr))
prAllWords :: Concr -> String
prAllWords concr =
unwords [w | (w,_) <- fullFormLexicon concr]
prMorphoAnalysis :: (String,[MorphoAnalysis]) -> String
prMorphoAnalysis (w,lps) =
unlines (w:[fun ++ " : " ++ cat | (fun,cat,p) <- lps])
hsExpr c =
case unApp c of
Just (f,cs) -> H.mkApp (H.mkCId f) (map hsExpr cs)
_ -> case unStr c of
Just str -> H.mkStr str
_ -> case unInt c of
Just n -> H.mkInt n
_ -> case unFloat c of
Just d -> H.mkFloat d
_ -> error $ "GF.Command.Commands2.hsExpr "++show c
cExpr e =
case H.unApp e of
Just (f,es) -> mkApp (H.showCId f) (map cExpr es)
_ -> case H.unStr e of
Just str -> mkStr str
_ -> case H.unInt e of
Just n -> mkInt n
_ -> case H.unFloat e of
Just d -> mkFloat d
_ -> error $ "GF.Command.Commands2.cExpr "++show e
needPGF exec opts ts =
do Env mb_pgf cncs <- getPGFEnv
case mb_pgf of
Just pgf -> liftSIO $ exec opts ts (pgf,cncs)
_ -> fail "Import a grammar before using this command"

View File

@@ -3,6 +3,7 @@
-- elsewhere
module GF.Command.CommonCommands where
import Data.List(sort)
import Data.Char (isSpace)
import GF.Command.CommandInfo
import qualified Data.Map as Map
import GF.Infra.SIO
@@ -14,9 +15,8 @@ import GF.Command.Abstract --(isOpt,valStrOpts,prOpt)
import GF.Text.Pretty
import GF.Text.Transliterations
import GF.Text.Lexing(stringOp,opInEnv)
import Data.Char (isSpace)
import PGF2(showExpr)
import qualified PGF as H(showCId,showExpr,toATree,toTrie,Trie(..))
extend old new = Map.union (Map.fromList new) old -- Map.union is left-biased
@@ -102,7 +102,9 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
"To see transliteration tables, use command ut."
],
examples = [
-- mkEx "l (EAdd 3 4) | ps -code -- linearize code-like output",
mkEx "l (EAdd 3 4) | ps -unlexcode -- linearize code-like output",
-- mkEx "ps -lexer=code | p -cat=Exp -- parse code-like input",
mkEx "ps -lexcode | p -cat=Exp -- parse code-like input",
mkEx "gr -cat=QCl | l | ps -bind -- linearization output from LangFin",
mkEx "ps -to_devanagari \"A-p\" -- show Devanagari in UTF8 terminal",
@@ -115,11 +117,13 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
let (os,fs) = optsAndFlags opts
trans <- optTranslit opts
if isOpt "lines" opts
then return $ fromStrings $ map (trans . stringOps (envFlag fs) (map prOpt os)) $ toStrings x
else return ((fromString . trans . stringOps (envFlag fs) (map prOpt os) . toString) x),
case opts of
_ | isOpt "lines" opts -> return $ fromStrings $ map (trans . stringOps (envFlag fs) (map prOpt os)) $ toStrings x
_ | isOpt "paragraphs" opts -> return $ fromStrings $ map (trans . stringOps (envFlag fs) (map prOpt os)) $ toParagraphs $ toStrings x
_ -> return ((fromString . trans . stringOps (envFlag fs) (map prOpt os) . toString) x),
options = [
("lines","apply the operation separately to each input line, returning a list of lines")
("lines","apply the operation separately to each input line, returning a list of lines"),
("paragraphs","apply separately to each input paragraph (as separated by empty lines), returning a list of lines")
] ++
stringOpOptions,
flags = [
@@ -166,8 +170,7 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
restrictedSystem $ syst ++ " <" ++ tmpi ++ " >" ++ tmpo
fmap fromString $ restricted $ readFile tmpo,
-}
fmap (fromStrings . lines) . restricted . readShellProcess syst . unlines . map (dropWhile (=='\n')) $ toStrings $ arg,
fmap fromString . restricted . readShellProcess syst $ toString arg,
flags = [
("command","the system command applied to the argument")
],
@@ -175,6 +178,12 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
mkEx "gt | l | ? wc -- generate trees, linearize, and count words"
]
}),
("tt", emptyCommandInfo {
longname = "to_trie",
syntax = "to_trie",
synopsis = "combine a list of trees into a trie",
exec = \ _ -> return . fromString . trie . toExprs
}),
("ut", emptyCommandInfo {
longname = "unicode_table",
synopsis = "show a transliteration table for a unicode character set",
@@ -222,6 +231,7 @@ envFlag fs =
_ -> Nothing
stringOpOptions = sort $ [
("bind","bind tokens separated by Prelude.BIND, i.e. &+"),
("chars","lexer that makes every non-space character a token"),
("from_cp1251","decode from cp1251 (Cyrillic used in Bulgarian resource)"),
("from_utf8","decode from utf8 (default)"),
@@ -246,6 +256,27 @@ stringOpOptions = sort $ [
("to_" ++ p, "from GF " ++ n ++ " transliteration to unicode")] |
(p,n) <- transliterationPrintNames]
trie = render . pptss . H.toTrie . map H.toATree
where
pptss [ts] = "*"<+>nest 2 (ppts ts)
pptss tss = vcat [i<+>nest 2 (ppts ts)|(i,ts)<-zip [(1::Int)..] tss]
ppts = vcat . map ppt
ppt t =
case t of
H.Oth e -> pp (H.showExpr [] e)
H.Ap f [[]] -> pp (H.showCId f)
H.Ap f tss -> H.showCId f $$ nest 2 (pptss tss)
-- ** Converting command input
toString = unwords . toStrings
toLines = unlines . toStrings
toParagraphs = map (unwords . words) . toParas
where
toParas ls = case break (all isSpace) ls of
([],[]) -> []
([],_:ll) -> toParas ll
(l, []) -> [unwords l]
(l, _:ll) -> unwords l : toParas ll

View File

@@ -1,7 +1,7 @@
module GF.Command.Importing (importGrammar, importSource) where
import PGF2
import PGF2.Internal(unionPGF)
import PGF
import PGF.Internal(optimizePGF,unionPGF,msgUnionPGF)
import GF.Compile
import GF.Compile.Multi (readMulti)
@@ -17,16 +17,14 @@ import GF.Data.ErrM
import System.FilePath
import qualified Data.Set as Set
import qualified Data.Map as Map
import Control.Monad(foldM)
-- import a grammar in an environment where it extends an existing grammar
importGrammar :: Maybe PGF -> Options -> [FilePath] -> IO (Maybe PGF)
importGrammar pgf0 _ [] = return pgf0
importGrammar :: PGF -> Options -> [FilePath] -> IO PGF
importGrammar pgf0 _ [] = return pgf0
importGrammar pgf0 opts files =
case takeExtensions (last files) of
".cf" -> fmap Just $ importCF opts files getBNFCRules bnfc2cf
".ebnf" -> fmap Just $ importCF opts files getEBNFRules ebnf2cf
".cf" -> importCF opts files getBNFCRules bnfc2cf
".ebnf" -> importCF opts files getEBNFRules ebnf2cf
".gfm" -> do
ascss <- mapM readMulti files
let cs = concatMap snd ascss
@@ -38,17 +36,14 @@ importGrammar pgf0 opts files =
Bad msg -> do putStrLn ('\n':'\n':msg)
return pgf0
".pgf" -> do
mapM readPGF files >>= foldM ioUnionPGF pgf0
".ngf" -> do
mapM readNGF files >>= foldM ioUnionPGF pgf0
pgf2 <- mapM readPGF files >>= return . foldl1 unionPGF
ioUnionPGF pgf0 pgf2
ext -> die $ "Unknown filename extension: " ++ show ext
ioUnionPGF :: Maybe PGF -> PGF -> IO (Maybe PGF)
ioUnionPGF Nothing two = return (Just two)
ioUnionPGF (Just one) two =
case unionPGF one two of
Nothing -> putStrLn "Abstract changed, previous concretes discarded." >> return (Just two)
Just pgf -> return (Just pgf)
ioUnionPGF :: PGF -> PGF -> IO PGF
ioUnionPGF one two = case msgUnionPGF one two of
(pgf, Just msg) -> putStrLn msg >> return pgf
(pgf,_) -> return pgf
importSource :: Options -> [FilePath] -> IO SourceGrammar
importSource opts files = fmap (snd.snd) (batchCompile opts files)
@@ -61,6 +56,7 @@ importCF opts files get convert = impCF
startCat <- case rules of
(Rule cat _ _ : _) -> return cat
_ -> fail "empty CFG"
probs <- maybe (return Map.empty) readProbabilitiesFromFile (flag optProbsFile opts)
let pgf = cf2pgf opts (last files) (mkCFG startCat Set.empty rules) probs
return pgf
let pgf = cf2pgf (last files) (mkCFG startCat Set.empty rules)
probs <- maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf
return $ setProbabilities probs
$ if flag optOptimizePGF opts then optimizePGF pgf else pgf

View File

@@ -6,8 +6,8 @@ module GF.Command.Interpreter (
import GF.Command.CommandInfo
import GF.Command.Abstract
import GF.Command.Parse
import PGF.Internal(Expr(..))
import GF.Infra.UseIO(putStrLnE)
import PGF2
import Control.Monad(when)
import qualified Data.Map as Map
@@ -56,8 +56,17 @@ interpretPipe env cs = do
-- | macro definition applications: replace ?i by (exps !! i)
appCommand :: CommandArguments -> Command -> Command
appCommand args c@(Command i os arg) = case arg of
AExpr e -> Command i os (AExpr (exprSubstitute e (toExprs args)))
AExpr e -> Command i os (AExpr (app e))
_ -> c
where
xs = toExprs args
app e = case e of
EAbs b x e -> EAbs b x (app e)
EApp e1 e2 -> EApp (app e1) (app e2)
ELit l -> ELit l
EMeta i -> xs !! i
EFun x -> EFun x
-- | return the trees to be sent in pipe, and the output possibly printed
--interpret :: CommandEnv -> [Expr] -> Command -> SIO CommandOutput
@@ -104,4 +113,4 @@ getCommandTrees env needsTypeCheck a args =
ATerm t -> return (Term t)
ANoArg -> return args -- use piped
where
one e = return (Exprs [(e,0)]) -- ignore piped
one e = return (Exprs [e]) -- ignore piped

View File

@@ -8,11 +8,9 @@ import qualified Data.Map as Map
import GF.Infra.SIO(MonadSIO(..),restricted)
import GF.Infra.Option(modifyFlags,optTrace) --,noOptions
import GF.Infra.Dependencies(depGraph)
import GF.Infra.CheckM
import GF.Text.Pretty(render,pp)
import GF.Data.Str(sstr)
import GF.Data.Operations (chunks,err,raise)
import GF.Text.Pretty(render)
import GF.Data.Str(sstr)
import GF.Grammar hiding (Ident,isPrefixOf)
import GF.Grammar.Analyse
@@ -20,8 +18,10 @@ import GF.Grammar.Parser (runP, pExp)
import GF.Grammar.ShowTerm
import GF.Grammar.Lookup (allOpers,allOpersTo)
import GF.Compile.Rename(renameSourceTerm)
import GF.Compile.Compute.Concrete(normalForm)
import GF.Compile.TypeCheck.Concrete as TC(inferLType,ppType)
import qualified GF.Compile.Compute.ConcreteNew as CN(normalForm,resourceValues)
import GF.Compile.TypeCheck.RConcrete as TC(inferLType,ppType)
import GF.Infra.Dependencies(depGraph)
import GF.Infra.CheckM(runCheck)
import GF.Command.Abstract(Option(..),isOpt,listFlags,valueString,valStrOpts)
import GF.Command.CommandInfo
@@ -162,11 +162,12 @@ sourceCommands = Map.fromList [
do sgr <- getGrammar
liftSIO (exec opts (toStrings ts) sgr)
compute_concrete opts ws sgr = fmap fst $ runCheck $
compute_concrete opts ws sgr =
case runP pExp (UTF8.fromString s) of
Left (_,msg) -> return $ pipeMessage msg
Right t -> do t <- checkComputeTerm opts sgr t
return (fromString (showTerm sgr style q t))
Right t -> return $ err pipeMessage
(fromString . showTerm sgr style q)
$ checkComputeTerm opts sgr t
where
(style,q) = pOpts TermPrintDefault Qualified opts
s = unwords ws
@@ -199,16 +200,16 @@ sourceCommands = Map.fromList [
| otherwise = unwords $ map prTerm ops
return $ fromString printed
show_operations os ts sgr = fmap fst $ runCheck $
show_operations os ts sgr =
case greatestResource sgr of
Nothing -> checkError (pp "no source grammar in scope; did you import with -retain?")
Nothing -> return $ fromString "no source grammar in scope; did you import with -retain?"
Just mo -> do
let greps = map valueString (listFlags "grep" os)
let isRaw = isOpt "raw" os
ops <- case ts of
_:_ -> do
let Right t = runP pExp (UTF8.fromString (unwords ts))
ty <- checkComputeTerm os sgr t
ty <- err error return $ checkComputeTerm os sgr t
return $ allOpersTo sgr ty
_ -> return $ allOpers sgr
let sigs = [(op,ty) | ((mo,op),ty,pos) <- ops]
@@ -253,12 +254,14 @@ sourceCommands = Map.fromList [
return void
checkComputeTerm os sgr t =
do mo <- maybe (checkError (pp "no source grammar in scope")) return $
do mo <- maybe (raise "no source grammar in scope") return $
greatestResource sgr
t <- renameSourceTerm sgr mo t
(t,_) <- inferLType sgr [] t
((t,_),_) <- runCheck $ do t <- renameSourceTerm sgr mo t
inferLType sgr [] t
let opts = modifyFlags (\fs->fs{optTrace=isOpt "trace" os})
fmap evalStr (normalForm sgr t)
t1 = CN.normalForm (CN.resourceValues opts sgr) (L NoLoc identW) t
t2 = evalStr t1
checkPredefError t2
where
-- ** Try to compute pre{...} tokens in token sequences
evalStr t =

View File

@@ -1,17 +1,18 @@
module GF.Command.TreeOperations (
treeOp,
allTreeOps,
treeChunks
) where
import PGF2(Expr,PGF,Fun,compute,mkApp,unApp,unMeta,exprSize,exprFunctions)
import PGF(Expr,PGF,CId,compute,mkApp,unApp,unapply,unMeta,exprSize,exprFunctions)
import Data.List
type TreeOp = [Expr] -> [Expr]
treeOp :: PGF -> String -> Maybe (Either TreeOp (Fun -> TreeOp))
treeOp :: PGF -> String -> Maybe (Either TreeOp (CId -> TreeOp))
treeOp pgf f = fmap snd $ lookup f $ allTreeOps pgf
allTreeOps :: PGF -> [(String,(String,Either TreeOp (Fun -> TreeOp)))]
allTreeOps :: PGF -> [(String,(String,Either TreeOp (CId -> TreeOp)))]
allTreeOps pgf = [
("compute",("compute by using semantic definitions (def)",
Left $ map (compute pgf))),
@@ -33,6 +34,16 @@ largest = reverse . smallest
smallest :: [Expr] -> [Expr]
smallest = sortBy (\t u -> compare (exprSize t) (exprSize u))
treeChunks :: Expr -> [Expr]
treeChunks = snd . cks where
cks t =
case unapply t of
(t, ts) -> case unMeta t of
Just _ -> (False,concatMap (snd . cks) ts)
Nothing -> case unzip (map cks ts) of
(bs,_) | and bs -> (True, [t])
(_,cts) -> (False,concat cts)
subtrees :: Expr -> [Expr]
subtrees t = t : case unApp t of
Just (f,ts) -> concatMap subtrees ts

View File

@@ -1,6 +1,7 @@
module GF.Compile (compileToPGF, link, batchCompile, srcAbsName) where
module GF.Compile (compileToPGF, compileToLPGF, link, linkl, batchCompile, srcAbsName) where
import GF.Compile.GrammarToPGF(grammar2PGF)
import GF.Compile.GrammarToPGF(mkCanon2pgf)
import GF.Compile.GrammarToLPGF(mkCanon2lpgf)
import GF.Compile.ReadFiles(ModEnv,getOptionsFromFile,getAllFiles,
importsOfModule)
import GF.CompileOne(compileOne)
@@ -14,7 +15,7 @@ import GF.Infra.UseIO(IOE,FullPath,liftIO,getLibraryDirectory,putIfVerb,
justModuleName,extendPathEnv,putStrE,putPointE)
import GF.Data.Operations(raise,(+++),err)
import Control.Monad(foldM,when,(<=<))
import Control.Monad(foldM,when,(<=<),filterM)
import GF.System.Directory(doesFileExist,getModificationTime)
import System.FilePath((</>),isRelative,dropFileName)
import qualified Data.Map as Map(empty,insert,elems) --lookup
@@ -22,23 +23,37 @@ import Data.List(nub)
import Data.Time(UTCTime)
import GF.Text.Pretty(render,($$),(<+>),nest)
import PGF2(PGF,readProbabilitiesFromFile)
import PGF.Internal(optimizePGF)
import PGF(PGF,defaultProbabilities,setProbabilities,readProbabilitiesFromFile)
import LPGF(LPGF)
-- | Compiles a number of source files and builds a 'PGF' structure for them.
-- This is a composition of 'link' and 'batchCompile'.
compileToPGF :: Options -> [FilePath] -> IOE PGF
compileToPGF opts fs = link opts . snd =<< batchCompile opts fs
compileToLPGF :: Options -> [FilePath] -> IOE LPGF
compileToLPGF opts fs = linkl opts . snd =<< batchCompile opts fs
-- | Link a grammar into a 'PGF' that can be used to 'PGF.linearize' and
-- 'PGF.parse' with the "PGF" run-time system.
link :: Options -> (ModuleName,Grammar) -> IOE PGF
link opts (cnc,gr) =
putPointE Normal opts "linking ... " $ do
let abs = srcAbsName gr cnc
probs <- liftIO (maybe (return Map.empty) readProbabilitiesFromFile (flag optProbsFile opts))
pgf <- grammar2PGF opts gr abs probs
pgf <- mkCanon2pgf opts gr abs
probs <- liftIO (maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf)
when (verbAtLeast opts Normal) $ putStrE "OK"
return pgf
return $ setProbabilities probs
$ if flag optOptimizePGF opts then optimizePGF pgf else pgf
-- | Link a grammar into a 'LPGF' that can be used for linearization only.
linkl :: Options -> (ModuleName,Grammar) -> IOE LPGF
linkl opts (cnc,gr) =
putPointE Normal opts "linking ... " $ do
let abs = srcAbsName gr cnc
lpgf <- mkCanon2lpgf opts gr abs
return lpgf
-- | Returns the name of the abstract syntax corresponding to the named concrete syntax
srcAbsName gr cnc = err (const cnc) id $ abstractOfConcrete gr cnc
@@ -76,10 +91,14 @@ compileModule opts1 env@(_,rfs) file =
do file <- getRealFile file
opts0 <- getOptionsFromFile file
let curr_dir = dropFileName file
lib_dir <- getLibraryDirectory (addOptions opts0 opts1)
let opts = addOptions (fixRelativeLibPaths curr_dir lib_dir opts0) opts1
lib_dirs <- getLibraryDirectory (addOptions opts0 opts1)
let opts = addOptions (fixRelativeLibPaths curr_dir lib_dirs opts0) opts1
-- putIfVerb opts $ "curr_dir:" +++ show curr_dir ----
-- putIfVerb opts $ "lib_dir:" +++ show lib_dirs ----
ps0 <- extendPathEnv opts
let ps = nub (curr_dir : ps0)
-- putIfVerb opts $ "options from file: " ++ show opts0
-- putIfVerb opts $ "augmented options: " ++ show opts
putIfVerb opts $ "module search path:" +++ show ps ----
files <- getAllFiles opts ps rfs file
putIfVerb opts $ "files to read:" +++ show files ----
@@ -92,13 +111,17 @@ compileModule opts1 env@(_,rfs) file =
if exists
then return file
else if isRelative file
then do lib_dir <- getLibraryDirectory opts1
let file1 = lib_dir </> file
exists <- doesFileExist file1
if exists
then return file1
else raise (render ("None of these files exists:" $$ nest 2 (file $$ file1)))
else raise (render ("File" <+> file <+> "does not exist."))
then do
lib_dirs <- getLibraryDirectory opts1
let candidates = [ lib_dir </> file | lib_dir <- lib_dirs ]
putIfVerb opts1 (render ("looking for: " $$ nest 2 candidates))
file1s <- filterM doesFileExist candidates
case length file1s of
0 -> raise (render ("Unable to find: " $$ nest 2 candidates))
1 -> do return $ head file1s
_ -> do putIfVerb opts1 ("matched multiple candidates: " +++ show file1s)
return $ head file1s
else raise (render ("File" <+> file <+> "does not exist"))
compileOne' :: Options -> CompileEnv -> FullPath -> IOE CompileEnv
compileOne' opts env@(gr,_) = extendCompileEnv env <=< compileOne opts gr

View File

@@ -1,110 +1,99 @@
{-# LANGUAGE FlexibleContexts, ImplicitParams #-}
{-# LANGUAGE FlexibleContexts #-}
module GF.Compile.CFGtoPGF (cf2pgf) where
import GF.Grammar.CFG
import GF.Infra.UseIO
import GF.Infra.Option
import GF.Compile.OptimizePGF
import PGF2
import PGF2.Internal
import PGF
import PGF.Internal
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import Data.Array.IArray
import Data.List
import Data.Maybe(fromMaybe)
--------------------------
-- the compiler ----------
--------------------------
cf2pgf :: Options -> FilePath -> ParamCFG -> Map.Map Fun Double -> PGF
cf2pgf opts fpath cf probs = error "TODO: cf2pgf" {-
build (let abstr = cf2abstr cf probs
in newPGF [] aname abstr [(cname, cf2concr opts abstr cf)])
cf2pgf :: FilePath -> ParamCFG -> PGF
cf2pgf fpath cf =
let pgf = PGF Map.empty aname (cf2abstr cf) (Map.singleton cname (cf2concr cf))
in updateProductionIndices pgf
where
name = justModuleName fpath
aname = name ++ "Abs"
cname = name
aname = mkCId (name ++ "Abs")
cname = mkCId name
cf2abstr :: (?builder :: Builder s) => ParamCFG -> Map.Map Fun Double -> B s AbstrInfo
cf2abstr cfg probs = newAbstr aflags acats afuns
cf2abstr :: ParamCFG -> Abstr
cf2abstr cfg = Abstr aflags afuns acats
where
aflags = [("startcat", LStr (fst (cfgStartCat cfg)))]
aflags = Map.singleton (mkCId "startcat") (LStr (fst (cfgStartCat cfg)))
acats = [(c', [], toLogProb (fromMaybe 0 (Map.lookup c' probs))) | cat <- allCats' cfg, let c' = cat2id cat]
afuns = [(f', dTyp [hypo Explicit "_" (dTyp [] (cat2id c) []) | NonTerminal c <- ruleRhs rule] (cat2id (ruleLhs rule)) [], 0, [], toLogProb (fromMaybe 0 (Map.lookup f' funs_probs)))
| rule <- allRules cfg
, let f' = mkRuleName rule]
acats = Map.fromList [(cat, ([], [(0,mkRuleName rule) | rule <- rules], 0))
| (cat,rules) <- (Map.toList . Map.fromListWith (++))
[(cat2id cat, catRules cfg cat) |
cat <- allCats' cfg]]
afuns = Map.fromList [(mkRuleName rule, (cftype [cat2id c | NonTerminal c <- ruleRhs rule] (cat2id (ruleLhs rule)), 0, Nothing, 0))
| rule <- allRules cfg]
funs_probs = (Map.fromList . concat . Map.elems . fmap pad . Map.fromListWith (++))
[(cat,[(f',Map.lookup f' probs)]) | rule <- allRules cfg,
let cat = cat2id (ruleLhs rule),
let f' = mkRuleName rule]
where
pad :: [(a,Maybe Double)] -> [(a,Double)]
pad pfs = [(f,fromMaybe deflt mb_p) | (f,mb_p) <- pfs]
where
deflt = case length [f | (f,Nothing) <- pfs] of
0 -> 0
n -> max 0 ((1 - sum [d | (f,Just d) <- pfs]) / fromIntegral n)
cat2id = mkCId . fst
toLogProb = realToFrac . negate . log
cat2id = fst
cf2concr :: (?builder :: Builder s) => Options -> B s AbstrInfo -> ParamCFG -> B s ConcrInfo
cf2concr opts abstr cfg =
let (lindefs',linrefs',productions',cncfuns',sequences',cnccats') =
(if flag optOptimizePGF opts then optimizePGF (fst (cfgStartCat cfg)) else id)
(lindefsrefs,lindefsrefs,IntMap.toList productions,cncfuns,sequences,cnccats)
in newConcr abstr [] []
lindefs' linrefs'
productions' cncfuns'
sequences' cnccats' totalCats
cf2concr :: ParamCFG -> Concr
cf2concr cfg = Concr Map.empty Map.empty
cncfuns lindefsrefs lindefsrefs
sequences productions
IntMap.empty Map.empty
cnccats
IntMap.empty
totalCats
where
cats = allCats' cfg
rules = allRules cfg
idSeq = [SymCat 0 0]
sequences0 = Set.fromList (idSeq :
sequences0 = Set.fromList (listArray (0,0) [SymCat 0 0] :
map mkSequence rules)
sequences = Set.toList sequences0
sequences = listArray (0,Set.size sequences0-1) (Set.toList sequences0)
idFun = ("_",[Set.findIndex idSeq sequences0])
idFun = CncFun wildCId (listArray (0,0) [seqid])
where
seq = listArray (0,0) [SymCat 0 0]
seqid = binSearch seq sequences (bounds sequences)
((fun_cnt,cncfuns0),productions0) = mapAccumL (convertRule cs) (1,[idFun]) rules
productions = foldl addProd IntMap.empty (concat (productions0++coercions))
cncfuns = reverse cncfuns0
cncfuns = listArray (0,fun_cnt-1) (reverse cncfuns0)
lbls = ["s"]
(fid,cnccats) = (mapAccumL mkCncCat 0 . Map.toList . Map.fromListWith max)
[(c,p) | (c,ps) <- cats, p <- ps]
lbls = listArray (0,0) ["s"]
(fid,cnccats0) = (mapAccumL mkCncCat 0 . Map.toList . Map.fromListWith max)
[(c,p) | (c,ps) <- cats, p <- ps]
((totalCats,cs), coercions) = mapAccumL mkCoercions (fid,Map.empty) cats
cnccats = Map.fromList cnccats0
lindefsrefs = map mkLinDefRef cats
lindefsrefs =
IntMap.fromList (map mkLinDefRef cats)
convertRule cs (funid,funs) rule =
let args = [PArg [] (cat2arg c) | NonTerminal c <- ruleRhs rule]
prod = PApply funid args
seqid = Set.findIndex (mkSequence rule) sequences0
fun = (mkRuleName rule, [seqid])
seqid = binSearch (mkSequence rule) sequences (bounds sequences)
fun = CncFun (mkRuleName rule) (listArray (0,0) [seqid])
funid' = funid+1
in funid' `seq` ((funid',fun:funs),let (c,ps) = ruleLhs rule in [(cat2fid c p, prod) | p <- ps])
mkSequence rule = snd $ mapAccumL convertSymbol 0 (ruleRhs rule)
mkSequence rule = listArray (0,length syms-1) syms
where
syms = snd $ mapAccumL convertSymbol 0 (ruleRhs rule)
convertSymbol d (NonTerminal (c,_)) = (d+1,if c `elem` ["Int","Float","String"] then SymLit d 0 else SymCat d 0)
convertSymbol d (Terminal t) = (d, SymKS t)
mkCncCat fid (cat,n)
| cat == "Int" = (fid, (cat, fidInt, fidInt, lbls))
| cat == "Float" = (fid, (cat, fidFloat, fidFloat, lbls))
| cat == "String" = (fid, (cat, fidString, fidString, lbls))
| cat == "Int" = (fid, (mkCId cat, CncCat fidInt fidInt lbls))
| cat == "Float" = (fid, (mkCId cat, CncCat fidFloat fidFloat lbls))
| cat == "String" = (fid, (mkCId cat, CncCat fidString fidString lbls))
| otherwise = let fid' = fid+n+1
in fid' `seq` (fid', (cat, fid, fid+n, lbls))
in fid' `seq` (fid', (mkCId cat,CncCat fid (fid+n) lbls))
mkCoercions (fid,cs) c@(cat,[p]) = ((fid,cs),[])
mkCoercions (fid,cs) c@(cat,ps ) =
@@ -113,16 +102,25 @@ cf2concr opts abstr cfg =
mkLinDefRef (cat,_) =
(cat2fid cat 0,[0])
addProd prods (fid,prod) =
case IntMap.lookup fid prods of
Just set -> IntMap.insert fid (prod:set) prods
Nothing -> IntMap.insert fid [prod] prods
Just set -> IntMap.insert fid (Set.insert prod set) prods
Nothing -> IntMap.insert fid (Set.singleton prod) prods
binSearch v arr (i,j)
| i <= j = case compare v (arr ! k) of
LT -> binSearch v arr (i,k-1)
EQ -> k
GT -> binSearch v arr (k+1,j)
| otherwise = error "binSearch"
where
k = (i+j) `div` 2
cat2fid cat p =
case [start | (cat',start,_,_) <- cnccats, cat == cat'] of
(start:_) -> fid+p
_ -> error "cat2fid"
case Map.lookup (mkCId cat) cnccats of
Just (CncCat fid _ _) -> fid+p
_ -> error "cat2fid"
cat2arg c@(cat,[p]) = cat2fid cat p
cat2arg c@(cat,ps ) =
@@ -133,5 +131,4 @@ cf2concr opts abstr cfg =
mkRuleName rule =
case ruleName rule of
CFObj n _ -> n
_ -> "_"
-}
_ -> wildCId

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/11/11 23:24:33 $
-- > CVS $Date: 2005/11/11 23:24:33 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.31 $
--
@@ -21,15 +21,15 @@
-----------------------------------------------------------------------------
module GF.Compile.CheckGrammar(checkModule) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Prelude hiding ((<>))
import GF.Infra.Ident
import GF.Infra.Option
import GF.Compile.TypeCheck.Abstract
import GF.Compile.TypeCheck.Concrete(checkLType,inferLType,ppType)
import qualified GF.Compile.TypeCheck.ConcreteNew as CN(checkLType,inferLType)
import GF.Compile.Compute.Concrete(normalForm)
import GF.Compile.TypeCheck.RConcrete
import qualified GF.Compile.TypeCheck.ConcreteNew as CN
import qualified GF.Compile.Compute.ConcreteNew as CN
import GF.Grammar
import GF.Grammar.Lexer
@@ -54,7 +54,11 @@ checkModule opts cwd sgr mo@(m,mi) = do
checkCompleteGrammar opts cwd gr (a,abs) mo
_ -> return mo
infoss <- checkInModule cwd mi NoLoc empty $ topoSortJments2 mo
foldM (foldM (checkInfo opts cwd sgr)) mo infoss
foldM updateCheckInfos mo infoss
where
updateCheckInfos mo = fmap (foldl update mo) . parallelCheck . map check
where check (i,info) = fmap ((,) i) (checkInfo opts cwd sgr mo i info)
update mo@(m,mi) (i,info) = (m,mi{jments=Map.insert i info (jments mi)})
-- check if restricted inheritance modules are still coherent
-- i.e. that the defs of remaining names don't depend on omitted names
@@ -70,9 +74,9 @@ checkRestrictedInheritance cwd sgr (name,mo) = checkInModule cwd mo NoLoc empty
let (incl,excl) = partition (isInherited mi) (Map.keys (jments m))
let incld c = Set.member c (Set.fromList incl)
let illegal c = Set.member c (Set.fromList excl)
let illegals = [(f,is) |
let illegals = [(f,is) |
(f,cs) <- allDeps, incld f, let is = filter illegal cs, not (null is)]
case illegals of
case illegals of
[] -> return ()
cs -> checkWarn ("In inherited module" <+> i <> ", dependence of excluded constants:" $$
nest 2 (vcat [f <+> "on" <+> fsep is | (f,is) <- cs]))
@@ -88,12 +92,12 @@ checkCompleteGrammar opts cwd gr (am,abs) (cm,cnc) = checkInModule cwd cnc NoLoc
-- check that all abstract constants are in concrete; build default lin and lincats
jsc <- foldM checkAbs jsc (Map.toList jsa)
return (cm,cnc{jments=jsc})
where
checkAbs js i@(c,info) =
case info of
AbsFun (Just (L loc ty)) _ _ _
AbsFun (Just (L loc ty)) _ _ _
-> do let mb_def = do
let (cxt,(_,i),_) = typeForm ty
info <- lookupIdent i js
@@ -116,7 +120,8 @@ checkCompleteGrammar opts cwd gr (am,abs) (cm,cnc) = checkInModule cwd cnc NoLoc
return js
_ -> do
case mb_def of
Ok def -> do linty <- linTypeOfType gr cm (L loc ty)
Ok def -> do (cont,val) <- linTypeOfType gr cm ty
let linty = (snd (valCat ty),cont,val)
return $ Map.insert c (CncFun (Just linty) (Just (L NoLoc def)) Nothing Nothing) js
Bad _ -> do noLinOf c
return js
@@ -131,12 +136,13 @@ checkCompleteGrammar opts cwd gr (am,abs) (cm,cnc) = checkInModule cwd cnc NoLoc
checkWarn ("no linearization type for" <+> c <> ", inserting default {s : Str}")
return $ Map.insert c (CncCat (Just (L NoLoc defLinType)) Nothing Nothing Nothing Nothing) js
_ -> return js
checkCnc js (c,info) =
case info of
CncFun _ d mn mf -> case lookupOrigInfo gr (am,c) of
Ok (_,AbsFun (Just (L loc ty)) _ _ _) ->
do linty <- linTypeOfType gr cm (L loc ty)
Ok (_,AbsFun (Just (L _ ty)) _ _ _) ->
do (cont,val) <- linTypeOfType gr cm ty
let linty = (snd (valCat ty),cont,val)
return $ Map.insert c (CncFun (Just linty) d mn mf) js
_ -> do checkWarn ("function" <+> c <+> "is not in abstract")
return js
@@ -152,123 +158,130 @@ checkCompleteGrammar opts cwd gr (am,abs) (cm,cnc) = checkInModule cwd cnc NoLoc
_ -> return $ Map.insert c info js
checkInfo :: Options -> FilePath -> SourceGrammar -> SourceModule -> (Ident,Info) -> Check SourceModule
checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ do
-- | General Principle: only Just-values are checked.
-- A May-value has always been checked in its origin module.
checkInfo :: Options -> FilePath -> SourceGrammar -> SourceModule -> Ident -> Info -> Check Info
checkInfo opts cwd sgr (m,mo) c info = checkInModule cwd mo NoLoc empty $ do
checkReservedId c
case info of
AbsCat (Just (L loc cont)) ->
mkCheck loc "the category" $
AbsCat (Just (L loc cont)) ->
mkCheck loc "the category" $
checkContext gr cont
AbsFun (Just (L loc typ)) ma md moper -> do
AbsFun (Just (L loc typ0)) ma md moper -> do
typ <- compAbsTyp [] typ0 -- to calculate let definitions
mkCheck loc "the type of function" $
checkTyp gr typ
typ <- compAbsTyp [] typ -- to calculate let definitions
case md of
Just eqs -> mapM_ (\(L loc eq) -> mkCheck loc "the definition of function" $
checkDef gr (fst sm,c) typ eq) eqs
checkDef gr (m,c) typ eq) eqs
Nothing -> return ()
update sm c (AbsFun (Just (L loc typ)) ma md moper)
return (AbsFun (Just (L loc typ)) ma md moper)
CncCat mty mdef mref mpr mpmcfg -> do
mty <- case mty of
Just (L loc typ) -> chIn loc "linearization type of" $ do
(typ,_) <- checkLType gr [] typ typeType
typ <- normalForm gr typ
return (Just (L loc typ))
Just (L loc typ) -> chIn loc "linearization type of" $
(if False --flag optNewComp opts
then do (typ,_) <- CN.checkLType (CN.resourceValues opts gr) typ typeType
typ <- computeLType gr [] typ
return (Just (L loc typ))
else do (typ,_) <- checkLType gr [] typ typeType
typ <- computeLType gr [] typ
return (Just (L loc typ)))
Nothing -> return Nothing
mdef <- case (mty,mdef) of
(Just (L _ typ),Just (L loc def)) ->
(Just (L _ typ),Just (L loc def)) ->
chIn loc "default linearization of" $ do
(def,_) <- checkLType gr [] def (mkFunType [typeStr] typ)
return (Just (L loc def))
_ -> return Nothing
mref <- case (mty,mref) of
(Just (L _ typ),Just (L loc ref)) ->
(Just (L _ typ),Just (L loc ref)) ->
chIn loc "reference linearization of" $ do
(ref,_) <- checkLType gr [] ref (mkFunType [typ] typeStr)
return (Just (L loc ref))
_ -> return Nothing
mpr <- case mpr of
(Just (L loc t)) ->
(Just (L loc t)) ->
chIn loc "print name of" $ do
(t,_) <- checkLType gr [] t typeStr
return (Just (L loc t))
_ -> return Nothing
update sm c (CncCat mty mdef mref mpr mpmcfg)
return (CncCat mty mdef mref mpr mpmcfg)
CncFun mty mt mpr mpmcfg -> do
mt <- case (mty,mt) of
(Just (_,cat,cont,val),Just (L loc trm)) ->
(Just (cat,cont,val),Just (L loc trm)) ->
chIn loc "linearization of" $ do
(trm,_) <- checkLType gr [] trm (mkFunType (map (\(_,_,ty) -> ty) cont) val) -- erases arg vars
return (Just (L loc trm))
_ -> return mt
mpr <- case mpr of
(Just (L loc t)) ->
(Just (L loc t)) ->
chIn loc "print name of" $ do
(t,_) <- checkLType gr [] t typeStr
return (Just (L loc t))
_ -> return Nothing
update sm c (CncFun mty mt mpr mpmcfg)
return (CncFun mty mt mpr mpmcfg)
ResOper pty pde -> do
(pty', pde') <- case (pty,pde) of
(Just (L loct ty), Just (L locd de)) -> do
ty' <- chIn loct "operation" $ do
(ty,_) <- checkLType gr [] ty typeType
normalForm gr ty
ty' <- chIn loct "operation" $
(if False --flag optNewComp opts
then CN.checkLType (CN.resourceValues opts gr) ty typeType >>= return . CN.normalForm (CN.resourceValues opts gr) (L loct c) . fst -- !!
else checkLType gr [] ty typeType >>= computeLType gr [] . fst)
(de',_) <- chIn locd "operation" $
checkLType gr [] de ty'
(if False -- flag optNewComp opts
then CN.checkLType (CN.resourceValues opts gr) de ty'
else checkLType gr [] de ty')
return (Just (L loct ty'), Just (L locd de'))
(Nothing , Just (L locd de)) -> do
(de',ty') <- chIn locd "operation" $
inferLType gr [] de
(if False -- flag optNewComp opts
then CN.inferLType (CN.resourceValues opts gr) de
else inferLType gr [] de)
return (Just (L locd ty'), Just (L locd de'))
(Just (L loct ty), Nothing) -> do
chIn loct "operation" $
checkError (pp "No definition given to the operation")
update sm c (ResOper pty' pde')
return (ResOper pty' pde')
ResOverload os tysts -> chIn NoLoc "overloading" $ do
tysts' <- mapM (uncurry $ flip (\(L loc1 t) (L loc2 ty) -> checkLType gr [] t ty >>= \(t,ty) -> return (L loc1 t, L loc2 ty))) tysts -- return explicit ones
tysts0 <- lookupOverload gr (fst sm,c) -- check against inherited ones too
tysts1 <- mapM (uncurry $ flip (checkLType gr []))
tysts0 <- lookupOverload gr (m,c) -- check against inherited ones too
tysts1 <- mapM (uncurry $ flip (checkLType gr []))
[(mkFunType args val,tr) | (args,(val,tr)) <- tysts0]
--- this can only be a partial guarantee, since matching
--- with value type is only possible if expected type is given
checkUniq $
checkUniq $
sort [let (xs,t) = typeFormCnc x in t : map (\(b,x,t) -> t) xs | (_,x) <- tysts1]
update sm c (ResOverload os [(y,x) | (x,y) <- tysts'])
return (ResOverload os [(y,x) | (x,y) <- tysts'])
ResParam (Just (L loc pcs)) _ -> do
(sm,cnt,ts) <- chIn loc "parameter type" $
mkParamValues sm 0 [] pcs
update sm c (ResParam (Just (L loc pcs)) (Just (ts,cnt)))
ts <- chIn loc "parameter type" $
liftM concat $ mapM mkPar pcs
return (ResParam (Just (L loc pcs)) (Just ts))
_ -> return sm
_ -> return info
where
gr = prependModule sgr sm
chIn loc cat = checkInModule cwd (snd sm) loc ("Happened in" <+> cat <+> c)
gr = prependModule sgr (m,mo)
chIn loc cat = checkInModule cwd mo loc ("Happened in" <+> cat <+> c)
mkParamValues sm cnt ts [] = return (sm,cnt,[])
mkParamValues sm@(mn,mi) cnt ts ((f,co):fs) = do
sm <- case lookupIdent f (jments mi) of
Ok (ResValue ty _) -> update sm f (ResValue ty cnt)
Bad msg -> checkError (pp msg)
vs <- liftM sequence $ mapM (\(_,_,ty) -> allParamValues gr ty) co
(sm,cnt,ts) <- mkParamValues sm (cnt+length vs) ts fs
return (sm,cnt,map (mkApp (QC (mn,f))) vs ++ ts)
mkPar (f,co) = do
vs <- liftM sequence $ mapM (\(_,_,ty) -> allParamValues gr ty) co
return $ map (mkApp (QC (m,f))) vs
checkUniq xss = case xss of
x:y:xs
x:y:xs
| x == y -> checkError $ "ambiguous for type" <+>
ppType (mkFunType (tail x) (head x))
ppType (mkFunType (tail x) (head x))
| otherwise -> checkUniq $ y:xs
_ -> return ()
mkCheck loc cat ss = case ss of
[] -> return sm
[] -> return info
_ -> chIn loc cat $ checkError (vcat ss)
compAbsTyp g t = case t of
@@ -283,8 +296,6 @@ checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ do
Abs _ _ _ -> return t
_ -> composOp (compAbsTyp g) t
update (mn,mi) c info = return (mn,mi{jments=Map.insert c info (jments mi)})
-- | for grammars obtained otherwise than by parsing ---- update!!
checkReservedId :: Ident -> Check ()
@@ -295,26 +306,25 @@ checkReservedId x =
-- auxiliaries
-- | linearization types and defaults
linTypeOfType :: Grammar -> ModuleName -> L Type -> Check ([Ident],Ident,Context,Type)
linTypeOfType cnc m (L loc typ) = do
let (ctxt,res_cat) = typeSkeleton typ
val <- lookLin res_cat
lin_args <- mapM mkLinArg (zip [0..] ctxt)
let (args,arg_cats) = unzip lin_args
return (arg_cats, snd res_cat, args, val)
linTypeOfType :: Grammar -> ModuleName -> Type -> Check (Context,Type)
linTypeOfType cnc m typ = do
let (cont,cat) = typeSkeleton typ
val <- lookLin cat
args <- mapM mkLinArg (zip [0..] cont)
return (args, val)
where
mkLinArg (i,(n,mc@(m,cat))) = do
val <- lookLin mc
let vars = mkRecType varLabel $ replicate n typeStr
symb = argIdent n cat i
symb = argIdent n cat i
rec <- if n==0 then return val else
errIn (render ("extending" $$
nest 2 vars $$
"with" $$
nest 2 val)) $
plusRecType vars val
return ((Explicit,symb,rec),cat)
return (Explicit,symb,rec)
lookLin (_,c) = checks [ --- rather: update with defLinType ?
lookupLincat cnc m c >>= normalForm cnc
lookupLincat cnc m c >>= computeLType cnc []
,return defLinType
]

View File

@@ -1,538 +1,3 @@
{-# LANGUAGE RankNTypes, CPP #-}
-- | Functions for computing the values of terms in the concrete syntax, in
-- | preparation for PMCFG generation.
module GF.Compile.Compute.Concrete
( normalForm
, Value(..), Thunk, ThunkState(..), Env
, EvalM, runEvalM, evalError
, eval, apply, force, value2term, patternMatch
, newMeta,getMeta,setMeta
, newThunk,newEvaluatedThunk
, getResDef, getInfo, getAllParamValues
) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Grammar hiding (Env, VGen, VApp, VRecType)
import GF.Grammar.Lookup(lookupResDef,lookupOrigInfo,allParamValues)
import GF.Grammar.Predef
import GF.Grammar.Lockfield(lockLabel)
import GF.Grammar.Printer
import GF.Data.Str(Str,glueStr,str2strings,str,sstr,plusStr,strTok)
import GF.Data.Operations(Err(..),err,errIn,maybeErr,mapPairsM)
import GF.Data.Utilities(mapFst,mapSnd)
import GF.Infra.CheckM
import GF.Infra.Option
import Data.STRef
import Data.Maybe(fromMaybe)
import Data.List
import Data.Char
import Control.Monad
import Control.Monad.ST
import Control.Applicative
import qualified Control.Monad.Fail as Fail
import qualified Data.Map as Map
import GF.Text.Pretty
import PGF2.Transactions(LIndex)
-- * Main entry points
normalForm :: Grammar -> Term -> Check Term
normalForm gr t =
fmap mkFV (runEvalM gr (eval [] t [] >>= value2term 0))
where
mkFV [t] = t
mkFV ts = FV ts
data ThunkState s
= Unevaluated (Env s) Term
| Evaluated (Value s)
| Unbound (Maybe Type) {-# UNPACK #-} !MetaId
type Thunk s = STRef s (ThunkState s)
type Env s = [(Ident,Thunk s)]
data Value s
= VApp QIdent [Thunk s]
| VMeta (Thunk s) (Env s) [Thunk s]
| VSusp (Thunk s) (Env s) [Thunk s] (Thunk s -> EvalM s (Value s))
| VGen {-# UNPACK #-} !Int [Thunk s]
| VClosure (Env s) Term
| VProd BindType Ident (Value s) (Env s) Term
| VRecType [(Label, Value s)]
| VR [(Label, Thunk s)]
| VP (Value s) Label [Thunk s]
| VExtR (Value s) (Value s)
| VTable (Value s) (Value s)
| VT TInfo (Env s) [Case]
| VV Type [Thunk s]
| VS (Value s) (Thunk s) [Thunk s]
| VSort Ident
| VInt Integer
| VFlt Double
| VStr String
| VC [Value s]
| VGlue (Value s) (Value s)
| VPatt Int (Maybe Int) Patt
| VPattType (Value s)
| VAlts (Value s) [(Value s, Value s)]
| VStrs [Value s]
-- This last constructor is only generated internally
-- in the PMCFG generator.
| VSymCat Int LIndex [(LIndex, Thunk s)]
eval env (Vr x) vs = case lookup x env of
Just tnk -> force tnk vs
Nothing -> evalError ("Variable" <+> pp x <+> "is not in scope")
eval env (Sort s) [] = return (VSort s)
eval env (EInt n) [] = return (VInt n)
eval env (EFloat d) [] = return (VFlt d)
eval env (K t) [] = return (VStr t)
eval env Empty [] = return (VC [])
eval env (App t1 t2) vs = do tnk <- newThunk env t2
eval env t1 (tnk : vs)
eval env (Abs b x t) [] = return (VClosure env (Abs b x t))
eval env (Abs b x t) (v:vs) = eval ((x,v):env) t vs
eval env (Meta i) vs = do tnk <- newMeta Nothing i
return (VMeta tnk env vs)
eval env (ImplArg t) [] = eval env t []
eval env (Prod b x t1 t2)[] = do v1 <- eval env t1 []
return (VProd b x v1 env t2)
eval env (Typed t ty) vs = eval env t vs
eval env (RecType lbls) [] = do lbls <- mapM (\(lbl,ty) -> fmap ((,) lbl) (eval env ty [])) lbls
return (VRecType lbls)
eval env (R as) [] = do as <- mapM (\(lbl,(_,t)) -> fmap ((,) lbl) (newThunk env t)) as
return (VR as)
eval env (P t lbl) vs = do v <- eval env t []
case v of
VR as -> case lookup lbl as of
Nothing -> evalError ("Missing value for label" <+> pp lbl $$
"in record" <+> pp t)
Just tnk -> force tnk vs
v -> return (VP v lbl vs)
eval env (ExtR t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
case (v1,v2) of
(VR as1,VR as2) -> return (VR (foldl (\as (lbl,v) -> update lbl v as) as1 as2))
(VRecType as1,VRecType as2) -> return (VRecType (foldl (\as (lbl,v) -> update lbl v as) as1 as2))
_ -> return (VExtR v1 v2)
eval env (Table t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
return (VTable v1 v2)
eval env (T i cs) [] = return (VT i env cs)
eval env (V ty ts) [] = do tnks <- mapM (newThunk env) ts
return (VV ty tnks)
eval env t@(S t1 t2) vs = do v1 <- eval env t1 []
tnk2 <- newThunk env t2
let v0 = VS v1 tnk2 vs
case v1 of
VT _ env cs -> patternMatch v0 (map (\(p,t) -> (env,[p],tnk2:vs,t)) cs)
VV ty tnks -> do t2 <- force tnk2 [] >>= value2term (length env)
ts <- getAllParamValues ty
case lookup t2 (zip ts tnks) of
Just tnk -> force tnk vs
Nothing -> return v0
v1 -> return v0
eval env (Let (x,(_,t1)) t2) vs = do tnk <- newThunk env t1
eval ((x,tnk):env) t2 vs
eval env (Q q@(m,id)) vs
| m == cPredef = do vs' <- mapM (flip force []) vs
mb_res <- evalPredef id vs'
case mb_res of
Just res -> return res
Nothing -> return (VApp q vs)
| otherwise = do t <- getResDef q
eval env t vs
eval env (QC q) vs = return (VApp q vs)
eval env (C t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
case (v1,v2) of
(VC vs1,VC vs2) -> return (VC (vs1++vs2))
(VC vs1,v2 ) -> return (VC (vs1++[v2]))
(v1, VC vs2) -> return (VC ([v1]++vs2))
(v1, v2 ) -> return (VC [v1,v2])
eval env t@(Glue t1 t2) [] = do v1 <- eval env t1 []
v2 <- eval env t2 []
case liftM2 (++) (value2string v1) (value2string v2) of
Just s -> return (string2value s)
Nothing -> return (VGlue v1 v2)
eval env (EPatt min max p) [] = return (VPatt min max p)
eval env (EPattType t) [] = do v <- eval env t []
return (VPattType v)
eval env (ELincat c ty) [] = do v <- eval env ty []
let lbl = lockLabel c
lv = VRecType []
case v of
(VRecType as) -> return (VRecType (update lbl lv as))
_ -> return (VExtR v (VRecType [(lbl,lv)]))
eval env (ELin c t) [] = do v <- eval env t []
let lbl = lockLabel c
tnk <- newEvaluatedThunk (VR [])
case v of
(VR as) -> return (VR (update lbl tnk as))
_ -> return (VExtR v (VR [(lbl,tnk)]))
eval env (FV ts) vs = msum [eval env t vs | t <- ts]
eval env (Alts d as) [] = do vd <- eval env d []
vas <- forM as $ \(t,s) -> do
vt <- eval env t []
vs <- eval env s []
return (vt,vs)
return (VAlts vd vas)
eval env (Strs ts) [] = do vs <- mapM (\t -> eval env t []) ts
return (VStrs vs)
eval env (TSymCat d r rs) []= do rs <- forM rs $ \(i,pv) ->
case lookup pv env of
Just tnk -> return (i,tnk)
Nothing -> evalError ("Variable" <+> pp pv <+> "is not in scope")
return (VSymCat d r rs)
eval env t vs = evalError ("Cannot reduce term" <+> pp t)
apply (VMeta m env vs0) vs = do st <- getMeta m
case st of
Evaluated v -> apply v vs
Unbound _ _ -> return (VMeta m env (vs0++vs))
apply (VApp f vs0) vs = return (VApp f (vs0++vs))
apply (VGen i vs0) vs = return (VGen i (vs0++vs))
apply (VClosure env (Abs b x t)) (v:vs) = eval ((x,v):env) t vs
apply v [] = return v
evalPredef id [v]
| id == cLength = return (fmap VInt (liftM genericLength (value2string v)))
evalPredef id [v1,v2]
| id == cTake = return (fmap string2value (liftM2 genericTake (value2int v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cDrop = return (fmap string2value (liftM2 genericDrop (value2int v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cTk = return (fmap string2value (liftM2 genericTk (value2int v1) (value2string v2)))
where
genericTk n = reverse . genericTake n . reverse
evalPredef id [v1,v2]
| id == cDp = return (fmap string2value (liftM2 genericDp (value2int v1) (value2string v2)))
where
genericDp n = reverse . genericDrop n . reverse
evalPredef id [v]
| id == cIsUpper= return (fmap toPBool (liftM (all isUpper) (value2string v)))
evalPredef id [v]
| id == cToUpper= return (fmap string2value (liftM (map toUpper) (value2string v)))
evalPredef id [v]
| id == cToLower= return (fmap string2value (liftM (map toLower) (value2string v)))
evalPredef id [v1,v2]
| id == cEqStr = return (fmap toPBool (liftM2 (==) (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cOccur = return (fmap toPBool (liftM2 occur (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cOccurs = return (fmap toPBool (liftM2 occurs (value2string v1) (value2string v2)))
evalPredef id [v1,v2]
| id == cEqInt = return (fmap toPBool (liftM2 (==) (value2int v1) (value2int v2)))
evalPredef id [v1,v2]
| id == cLessInt= return (fmap toPBool (liftM2 (<) (value2int v1) (value2int v2)))
evalPredef id [v1,v2]
| id == cPlus = return (fmap VInt (liftM2 (+) (value2int v1) (value2int v2)))
evalPredef id [v]
| id == cError = case value2string v of
Just msg -> fail msg
Nothing -> return Nothing
evalPredef id vs = return Nothing
toPBool True = VApp (cPredef,cPTrue) []
toPBool False = VApp (cPredef,cPFalse) []
occur s1 [] = False
occur s1 s2@(_:tail) = check s1 s2
where
check xs [] = False
check [] ys = True
check (x:xs) (y:ys)
| x == y = check xs ys
check _ _ = occur s1 tail
occurs cs s2 = any (\c -> elem c s2) cs
update lbl v [] = [(lbl,v)]
update lbl v (a@(lbl',_):as)
| lbl==lbl' = (lbl,v) : as
| otherwise = a : update lbl v as
patternMatch v0 [] = fail "No matching pattern found"
patternMatch v0 ((env0,ps,args0,t):eqs) = match env0 ps eqs args0
where
match env [] eqs args = eval env t args
match env (PT ty p :ps) eqs args = match env (p:ps) eqs args
match env (PAlt p1 p2:ps) eqs args = match env (p1:ps) ((env,p2:ps,args,t):eqs) args
match env (PM q :ps) eqs args = do t <- getResDef q
case t of
EPatt _ _ p -> match env (p:ps) eqs args
_ -> evalError $ hang "Expected pattern macro:" 4
(pp t)
match env (PV v :ps) eqs (arg:args) = match ((v,arg):env) ps eqs args
match env (PAs v p :ps) eqs (arg:args) = match ((v,arg):env) (p:ps) eqs (arg:args)
match env (PW :ps) eqs (arg:args) = match env ps eqs args
match env (PTilde _ :ps) eqs (arg:args) = match env ps eqs args
match env (p :ps) eqs (arg:args) = do
v <- force arg []
case (p,v) of
(p, VMeta i envi vs ) -> return (VSusp i envi vs (\tnk -> match env (p:ps) eqs (tnk:args)))
(p, VGen i vs ) -> return v0
(p, VSusp i envi vs k) -> return (VSusp i envi vs (\tnk -> match env (p:ps) eqs (tnk:args)))
(PP q qs, VApp r tnks)
| q == r -> match env (qs++ps) eqs (tnks++args)
(PR pas, VR as) -> matchRec env pas as ps eqs args
(PString s1, VStr s2)
| s1 == s2 -> match env ps eqs args
(PString s1, VC [])
| null s1 -> match env ps eqs args
(PSeq min1 max1 p1 min2 max2 p2,v)
-> case value2string v of
Just s -> do let n = length s
lo = min1 `max` (n-fromMaybe n max2)
hi = (n-min2) `min` fromMaybe n max1
(ds,cs) = splitAt lo s
eqs <- matchStr env (p1:p2:ps) eqs (hi-lo) (reverse ds) cs args
patternMatch v0 eqs
Nothing -> return v0
(PRep minp maxp p, v)
-> case value2string v of
Just s -> do let n = length s `div` (max minp 1)
eqs <- matchRep env n minp maxp p minp maxp p ps ((env,PString []:ps,(arg:args),t) : eqs) (arg:args)
patternMatch v0 eqs
Nothing -> return v0
(PChar, VStr [_]) -> match env ps eqs args
(PChars cs, VStr [c])
| elem c cs -> match env ps eqs args
(PInt n, VInt m)
| n == m -> match env ps eqs args
(PFloat n, VFlt m)
| n == m -> match env ps eqs args
_ -> patternMatch v0 eqs
matchRec env [] as ps eqs args = match env ps eqs args
matchRec env ((lbl,p):pas) as ps eqs args =
case lookup lbl as of
Just tnk -> matchRec env pas as (p:ps) eqs (tnk:args)
Nothing -> evalError ("Missing value for label" <+> pp lbl)
matchStr env ps eqs i ds [] args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value [])
return ((env,ps,arg1:arg2:args,t) : eqs)
matchStr env ps eqs 0 ds cs args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value cs)
return ((env,ps,arg1:arg2:args,t) : eqs)
matchStr env ps eqs i ds (c:cs) args = do
arg1 <- newEvaluatedThunk (string2value (reverse ds))
arg2 <- newEvaluatedThunk (string2value (c:cs))
eqs <- matchStr env ps eqs (i-1 :: Int) (c:ds) cs args
return ((env,ps,arg1:arg2:args,t) : eqs)
matchRep env 0 minp maxp p minq maxq q ps eqs args = do
return eqs
matchRep env n minp maxp p minq maxq q ps eqs args = do
matchRep env (n-1) minp maxp p (minp+minq) (liftM2 (+) maxp maxq) (PSeq minp maxp p minq maxq q) ps ((env,q:ps,args,t) : eqs) args
value2term i (VApp q tnks) =
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (QC q) tnks
value2term i (VMeta m env tnks) = do
res <- zonk m tnks
case res of
Right i -> foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (Meta i) tnks
Left v -> value2term i v
value2term i (VSusp j env vs k) = do
tnk <- newEvaluatedThunk (VGen maxBound vs)
v <- k tnk
value2term i v
value2term i (VGen j tnks) =
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (Vr (identS ('v':show j))) tnks
value2term i (VClosure env (Abs b x t)) = do
tnk <- newGen i
v <- eval ((x,tnk):env) t []
t <- value2term (i+1) v
return (Abs b (identS ('v':show i)) t)
value2term i (VProd b x v1 env t2)
| x == identW = do t1 <- value2term i v1
v2 <- eval env t2 []
t2 <- value2term i v2
return (Prod b x t1 t2)
| otherwise = do t1 <- value2term i v1
tnk <- newGen i
v2 <- eval ((x,tnk):env) t2 []
t2 <- value2term (i+1) v2
return (Prod b (identS ('v':show i)) t1 t2)
value2term i (VRecType lbls) = do
lbls <- mapM (\(lbl,v) -> fmap ((,) lbl) (value2term i v)) lbls
return (RecType lbls)
value2term i (VR as) = do
as <- mapM (\(lbl,tnk) -> fmap (\t -> (lbl,(Nothing,t))) (force tnk [] >>= value2term i)) as
return (R as)
value2term i (VP v lbl tnks) = do
t <- value2term i v
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (P t lbl) tnks
value2term i (VExtR v1 v2) = do
t1 <- value2term i v1
t2 <- value2term i v2
return (ExtR t1 t2)
value2term i (VTable v1 v2) = do
t1 <- value2term i v1
t2 <- value2term i v2
return (Table t1 t2)
value2term i (VT ti _ cs) = return (T ti cs)
value2term i (VV ty tnks) = do ts <- mapM (\tnk -> force tnk [] >>= value2term i) tnks
return (V ty ts)
value2term i (VS v1 tnk2 tnks) = do t1 <- value2term i v1
t2 <- force tnk2 [] >>= value2term i
foldM (\e1 tnk -> fmap (App e1) (force tnk [] >>= value2term i)) (S t1 t2) tnks
value2term i (VSort s) = return (Sort s)
value2term i (VStr tok) = return (K tok)
value2term i (VInt n) = return (EInt n)
value2term i (VFlt n) = return (EFloat n)
value2term i (VC vs) = do
ts <- mapM (value2term i) vs
case ts of
[] -> return Empty
(t:ts) -> return (foldl C t ts)
value2term i (VGlue v1 v2) = do
t1 <- value2term i v1
t2 <- value2term i v2
return (Glue t1 t2)
value2term i (VPatt min max p) = return (EPatt min max p)
value2term i (VPattType v) = do t <- value2term i v
return (EPattType t)
value2term i (VAlts vd vas) = do
d <- value2term i vd
as <- forM vas $ \(vt,vs) -> do
t <- value2term i vt
s <- value2term i vs
return (t,s)
return (Alts d as)
value2term i (VStrs vs) = do
ts <- mapM (value2term i) vs
return (Strs ts)
value2string (VStr s) = Just s
value2string (VC vs) = fmap unwords (mapM value2string vs)
value2string _ = Nothing
string2value s =
case words s of
[] -> VC []
[w] -> VStr w
ws -> VC (map VStr ws)
value2int (VInt n) = Just n
value2int _ = Nothing
-----------------------------------------------------------------------
-- * Evaluation monad
type MetaThunks s = Map.Map MetaId (Thunk s)
type Cont s r = MetaThunks s -> r -> ST s (CheckResult r)
newtype EvalM s a = EvalM (forall r . Grammar -> (a -> Cont s r) -> Cont s r)
instance Functor (EvalM s) where
fmap f (EvalM g) = EvalM (\gr k -> g gr (k . f))
instance Applicative (EvalM s) where
pure x = EvalM (\gr k -> k x)
(EvalM f) <*> (EvalM x) = EvalM (\gr k -> f gr (\f -> x gr (\x -> k (f x))))
instance Monad (EvalM s) where
(EvalM f) >>= g = EvalM (\gr k -> f gr (\x -> case g x of
EvalM g -> g gr k))
#if !(MIN_VERSION_base(4,13,0))
-- Monad(fail) will be removed in GHC 8.8+
fail = Fail.fail
#endif
instance Fail.MonadFail (EvalM s) where
fail msg = EvalM (\gr k _ r -> return (Fail (pp msg)))
instance Alternative (EvalM s) where
empty = EvalM (\gr k _ r -> return (Success r))
(EvalM f) <|> (EvalM g) = EvalM $ \gr k mt r -> do
res <- f gr k mt r
case res of
Fail msg -> return (Fail msg)
Success r -> g gr k mt r
instance MonadPlus (EvalM s) where
runEvalM :: Grammar -> (forall s . EvalM s a) -> Check [a]
runEvalM gr f =
case runST (case f of
EvalM f -> f gr (\x mt xs -> return (Success (x:xs))) Map.empty []) of
Fail msg -> checkError msg
Success xs -> return (reverse xs)
evalError :: Doc -> EvalM s a
evalError msg = EvalM (\gr k _ r -> return (Fail msg))
getResDef :: QIdent -> EvalM s Term
getResDef q = EvalM $ \gr k mt r -> do
case lookupResDef gr q of
Ok t -> k t mt r
Bad msg -> return (Fail (pp msg))
getInfo :: QIdent -> EvalM s (ModuleName,Info)
getInfo q = EvalM $ \gr k mt r -> do
case lookupOrigInfo gr q of
Ok res -> k res mt r
Bad msg -> return (Fail (pp msg))
getAllParamValues :: Type -> EvalM s [Term]
getAllParamValues ty = EvalM $ \gr k mt r ->
case allParamValues gr ty of
Ok ts -> k ts mt r
Bad msg -> return (Fail (pp msg))
newThunk env t = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Unevaluated env t)
k tnk mt r
newEvaluatedThunk v = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Evaluated v)
k tnk mt r
newMeta mb_ty i = EvalM $ \gr k mt r ->
if i == 0
then do tnk <- newSTRef (Unbound mb_ty i)
k tnk mt r
else case Map.lookup i mt of
Just tnk -> k tnk mt r
Nothing -> do tnk <- newSTRef (Unbound mb_ty i)
k tnk (Map.insert i tnk mt) r
getMeta tnk = EvalM $ \gr k mt r -> readSTRef tnk >>= \st -> k st mt r
setMeta tnk st = EvalM $ \gr k mt r -> do
old <- readSTRef tnk
writeSTRef tnk st
r <- k () mt r
writeSTRef tnk old
return r
newGen i = EvalM $ \gr k mt r -> do
tnk <- newSTRef (Evaluated (VGen i []))
k tnk mt r
force tnk vs = EvalM $ \gr k mt r -> do
s <- readSTRef tnk
case s of
Unevaluated env t -> case eval env t vs of
EvalM f -> f gr (\v mt r -> do writeSTRef tnk (Evaluated v)
r <- k v mt r
writeSTRef tnk s
return r) mt r
Evaluated v -> case apply v vs of
EvalM f -> f gr k mt r
Unbound _ _ -> k (VMeta tnk [] vs) mt r
zonk tnk vs = EvalM $ \gr k mt r -> do
s <- readSTRef tnk
case s of
Evaluated v -> case apply v vs of
EvalM f -> f gr (k . Left) mt r
Unbound _ i -> k (Right i) mt r
module GF.Compile.Compute.Concrete{-(module M)-} where
--import GF.Compile.Compute.ConcreteLazy as M -- New
--import GF.Compile.Compute.ConcreteStrict as M -- Old, inefficient

View File

@@ -0,0 +1,588 @@
-- | Functions for computing the values of terms in the concrete syntax, in
-- | preparation for PMCFG generation.
module GF.Compile.Compute.ConcreteNew
(GlobalEnv, GLocation, resourceValues, geLoc, geGrammar,
normalForm,
Value(..), Bind(..), Env, value2term, eval, vapply
) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Grammar hiding (Env, VGen, VApp, VRecType)
import GF.Grammar.Lookup(lookupResDefLoc,allParamValues)
import GF.Grammar.Predef(cPredef,cErrorType,cTok,cStr,cTrace,cPBool)
import GF.Grammar.PatternMatch(matchPattern,measurePatt)
import GF.Grammar.Lockfield(isLockLabel,lockRecType) --unlockRecord,lockLabel
import GF.Compile.Compute.Value hiding (Error)
import GF.Compile.Compute.Predef(predef,predefName,delta)
import GF.Data.Str(Str,glueStr,str2strings,str,sstr,plusStr,strTok)
import GF.Data.Operations(Err,err,errIn,maybeErr,mapPairsM)
import GF.Data.Utilities(mapFst,mapSnd)
import GF.Infra.Option
import Control.Monad(ap,liftM,liftM2) -- ,unless,mplus
import Data.List (findIndex,intersect,nub,elemIndex,(\\)) --,isInfixOf
--import Data.Char (isUpper,toUpper,toLower)
import GF.Text.Pretty
import qualified Data.Map as Map
import Debug.Trace(trace)
-- * Main entry points
normalForm :: GlobalEnv -> L Ident -> Term -> Term
normalForm (GE gr rv opts _) loc = err (bugloc loc) id . nfx (GE gr rv opts loc)
nfx env@(GE _ _ _ loc) t = do
v <- eval env [] t
case value2term loc [] v of
Left i -> fail ("variable #"++show i++" is out of scope")
Right t -> return t
eval :: GlobalEnv -> Env -> Term -> Err Value
eval (GE gr rvs opts loc) env t = ($ (map snd env)) # value cenv t
where
cenv = CE gr rvs opts loc (map fst env)
--apply env = apply' env
--------------------------------------------------------------------------------
-- * Environments
type ResourceValues = Map.Map ModuleName (Map.Map Ident (Err Value))
data GlobalEnv = GE Grammar ResourceValues Options GLocation
data CompleteEnv = CE {srcgr::Grammar,rvs::ResourceValues,
opts::Options,
gloc::GLocation,local::LocalScope}
type GLocation = L Ident
type LocalScope = [Ident]
type Stack = [Value]
type OpenValue = Stack->Value
geLoc (GE _ _ _ loc) = loc
geGrammar (GE gr _ _ _) = gr
ext b env = env{local=b:local env}
extend bs env = env{local=bs++local env}
global env = GE (srcgr env) (rvs env) (opts env) (gloc env)
var :: CompleteEnv -> Ident -> Err OpenValue
var env x = maybe unbound pick' (elemIndex x (local env))
where
unbound = fail ("Unknown variable: "++showIdent x)
pick' i = return $ \ vs -> maybe (err i vs) ok (pick i vs)
err i vs = bug $ "Stack problem: "++showIdent x++": "
++unwords (map showIdent (local env))
++" => "++show (i,length vs)
ok v = --trace ("var "++show x++" = "++show v) $
v
pick :: Int -> Stack -> Maybe Value
pick 0 (v:_) = Just v
pick i (_:vs) = pick (i-1) vs
pick i vs = Nothing -- bug $ "pick "++show (i,vs)
resource env (m,c) =
-- err bug id $
if isPredefCat c
then value0 env =<< lockRecType c defLinType -- hmm
else maybe e id $ Map.lookup c =<< Map.lookup m (rvs env)
where e = fail $ "Not found: "++render m++"."++showIdent c
-- | Convert operators once, not every time they are looked up
resourceValues :: Options -> SourceGrammar -> GlobalEnv
resourceValues opts gr = env
where
env = GE gr rvs opts (L NoLoc identW)
rvs = Map.mapWithKey moduleResources (moduleMap gr)
moduleResources m = Map.mapWithKey (moduleResource m) . jments
moduleResource m c _info = do L l t <- lookupResDefLoc gr (m,c)
let loc = L l c
qloc = L l (Q (m,c))
eval (GE gr rvs opts loc) [] (traceRes qloc t)
traceRes = if flag optTrace opts
then traceResource
else const id
-- * Tracing
-- | Insert a call to the trace function under the top-level lambdas
traceResource (L l q) t =
case termFormCnc t of
(abs,body) -> mkAbs abs (mkApp traceQ [args,body])
where
args = R $ tuple2record (K lstr:[Vr x|(bt,x)<-abs,bt==Explicit])
lstr = render (l<>":"<>ppTerm Qualified 0 q)
traceQ = Q (cPredef,cTrace)
-- * Computing values
-- | Computing the value of a top-level term
value0 :: CompleteEnv -> Term -> Err Value
value0 env = eval (global env) []
-- | Computing the value of a term
value :: CompleteEnv -> Term -> Err OpenValue
value env t0 =
-- Each terms is traversed only once by this function, using only statically
-- available information. Notably, the values of lambda bound variables
-- will be unknown during the term traversal phase.
-- The result is an OpenValue, which is a function that may be applied many
-- times to different dynamic values, but without the term traversal overhead
-- and without recomputing other statically known information.
-- For this to work, there should be no recursive calls under lambdas here.
-- Whenever we need to construct the OpenValue function with an explicit
-- lambda, we have to lift the recursive calls outside the lambda.
-- (See e.g. the rules for Let, Prod and Abs)
{-
trace (render $ text "value"<+>sep [ppL (gloc env)<>text ":",
brackets (fsep (map ppIdent (local env))),
ppTerm Unqualified 10 t0]) $
--}
errIn (render t0) $
case t0 of
Vr x -> var env x
Q x@(m,f)
| m == cPredef -> if f==cErrorType -- to be removed
then let p = identS "P"
in const # value0 env (mkProd [(Implicit,p,typeType)] (Vr p) [])
else if f==cPBool
then const # resource env x
else const . flip VApp [] # predef f
| otherwise -> const # resource env x --valueResDef (fst env) x
QC x -> return $ const (VCApp x [])
App e1 e2 -> apply' env e1 . (:[]) =<< value env e2
Let (x,(oty,t)) body -> do vb <- value (ext x env) body
vt <- value env t
return $ \ vs -> vb (vt vs:vs)
Meta i -> return $ \ vs -> VMeta i (zip (local env) vs) []
Prod bt x t1 t2 ->
do vt1 <- value env t1
vt2 <- value (ext x env) t2
return $ \ vs -> VProd bt (vt1 vs) x $ Bind $ \ vx -> vt2 (vx:vs)
Abs bt x t -> do vt <- value (ext x env) t
return $ VAbs bt x . Bind . \ vs vx -> vt (vx:vs)
EInt n -> return $ const (VInt n)
EFloat f -> return $ const (VFloat f)
K s -> return $ const (VString s)
Empty -> return $ const (VString "")
Sort s | s == cTok -> return $ const (VSort cStr) -- to be removed
| otherwise -> return $ const (VSort s)
ImplArg t -> (VImplArg.) # value env t
Table p res -> liftM2 VTblType # value env p <# value env res
RecType rs -> do lovs <- mapPairsM (value env) rs
return $ \vs->VRecType $ mapSnd ($vs) lovs
t@(ExtR t1 t2) -> ((extR t.)# both id) # both (value env) (t1,t2)
FV ts -> ((vfv .) # sequence) # mapM (value env) ts
R as -> do lovs <- mapPairsM (value env.snd) as
return $ \ vs->VRec $ mapSnd ($vs) lovs
T i cs -> valueTable env i cs
V ty ts -> do pvs <- paramValues env ty
((VV ty pvs .) . sequence) # mapM (value env) ts
C t1 t2 -> ((ok2p vconcat.) # both id) # both (value env) (t1,t2)
S t1 t2 -> ((select env.) # both id) # both (value env) (t1,t2)
P t l -> --maybe (bug $ "project "++show l++" from "++show v) id $
do ov <- value env t
return $ \ vs -> let v = ov vs
in maybe (VP v l) id (proj l v)
Alts t tts -> (\v vts -> VAlts # v <# mapM (both id) vts) # value env t <# mapM (both (value env)) tts
Strs ts -> ((VStrs.) # sequence) # mapM (value env) ts
Glue t1 t2 -> ((ok2p (glue env).) # both id) # both (value env) (t1,t2)
ELin c r -> (unlockVRec (gloc env) c.) # value env r
EPatt p -> return $ const (VPatt p) -- hmm
EPattType ty -> do vt <- value env ty
return (VPattType . vt)
Typed t ty -> value env t
t -> fail.render $ "value"<+>ppTerm Unqualified 10 t $$ show t
vconcat vv@(v1,v2) =
case vv of
(VString "",_) -> v2
(_,VString "") -> v1
(VApp NonExist _,_) -> v1
(_,VApp NonExist _) -> v2
_ -> VC v1 v2
proj l v | isLockLabel l = return (VRec [])
---- a workaround 18/2/2005: take this away and find the reason
---- why earlier compilation destroys the lock field
proj l v =
case v of
VFV vs -> liftM vfv (mapM (proj l) vs)
VRec rs -> lookup l rs
-- VExtR v1 v2 -> proj l v2 `mplus` proj l v1 -- hmm
VS (VV pty pvs rs) v2 -> flip VS v2 . VV pty pvs # mapM (proj l) rs
_ -> return (ok1 VP v l)
ok1 f v1@(VError {}) _ = v1
ok1 f v1 v2 = f v1 v2
ok2 f v1@(VError {}) _ = v1
ok2 f _ v2@(VError {}) = v2
ok2 f v1 v2 = f v1 v2
ok2p f (v1@VError {},_) = v1
ok2p f (_,v2@VError {}) = v2
ok2p f vv = f vv
unlockVRec loc c0 v0 = v0
{-
unlockVRec loc c0 v0 = unlockVRec' c0 v0
where
unlockVRec' ::Ident -> Value -> Value
unlockVRec' c v =
case v of
-- VClosure env t -> err bug (VClosure env) (unlockRecord c t)
VAbs bt x (Bind f) -> VAbs bt x (Bind $ \ v -> unlockVRec' c (f v))
VRec rs -> plusVRec rs lock
-- _ -> VExtR v (VRec lock) -- hmm
_ -> {-trace (render $ ppL loc $ "unlock non-record "++show v0)-} v -- hmm
-- _ -> bugloc loc $ "unlock non-record "++show v0
where
lock = [(lockLabel c,VRec [])]
-}
-- suspicious, but backwards compatible
plusVRec rs1 rs2 = VRec ([(l,v)|(l,v)<-rs1,l `notElem` ls2] ++ rs2)
where ls2 = map fst rs2
extR t vv =
case vv of
(VFV vs,v2) -> vfv [extR t (v1,v2)|v1<-vs]
(v1,VFV vs) -> vfv [extR t (v1,v2)|v2<-vs]
(VRecType rs1, VRecType rs2) ->
case intersect (map fst rs1) (map fst rs2) of
[] -> VRecType (rs1 ++ rs2)
ls -> error $ "clash"<+>show ls
(VRec rs1, VRec rs2) -> plusVRec rs1 rs2
(v1 , VRec [(l,_)]) | isLockLabel l -> v1 -- hmm
(VS (VV t pvs vs) s,v2) -> VS (VV t pvs [extR t (v1,v2)|v1<-vs]) s
-- (v1,v2) -> ok2 VExtR v1 v2 -- hmm
(v1,v2) -> error $ "not records" $$ show v1 $$ show v2
where
error explain = ppbug $ "The term" <+> t
<+> "is not reducible" $$ explain
glue env (v1,v2) = glu v1 v2
where
glu v1 v2 =
case (v1,v2) of
(VFV vs,v2) -> vfv [glu v1 v2|v1<-vs]
(v1,VFV vs) -> vfv [glu v1 v2|v2<-vs]
(VString s1,VString s2) -> VString (s1++s2)
(v1,VAlts d vs) -> VAlts (glx d) [(glx v,c) | (v,c) <- vs]
where glx v2 = glu v1 v2
(v1@(VAlts {}),v2) ->
--err (const (ok2 VGlue v1 v2)) id $
err bug id $
do y' <- strsFromValue v2
x' <- strsFromValue v1
return $ vfv [foldr1 VC (map VString (str2strings (glueStr v u))) | v <- x', u <- y']
(VC va vb,v2) -> VC va (glu vb v2)
(v1,VC va vb) -> VC (glu v1 va) vb
(VS (VV ty pvs vs) vb,v2) -> VS (VV ty pvs [glu v v2|v<-vs]) vb
(v1,VS (VV ty pvs vs) vb) -> VS (VV ty pvs [glu v1 v|v<-vs]) vb
(v1@(VApp NonExist _),_) -> v1
(_,v2@(VApp NonExist _)) -> v2
-- (v1,v2) -> ok2 VGlue v1 v2
(v1,v2) -> if flag optPlusAsBind (opts env)
then VC v1 (VC (VApp BIND []) v2)
else let loc = gloc env
vt v = case value2term loc (local env) v of
Left i -> Error ('#':show i)
Right t -> t
originalMsg = render $ ppL loc (hang "unsupported token gluing" 4
(Glue (vt v1) (vt v2)))
term = render $ pp $ Glue (vt v1) (vt v2)
in error $ unlines
[originalMsg
,""
,"There was a problem in the expression `"++term++"`, either:"
,"1) You are trying to use + on runtime arguments, possibly via an oper."
,"2) One of the arguments in `"++term++"` is a bound variable from pattern matching a string, but the cases are non-exhaustive."
,"For more help see https://github.com/GrammaticalFramework/gf-core/tree/master/doc/errors/gluing.md"
]
-- | to get a string from a value that represents a sequence of terminals
strsFromValue :: Value -> Err [Str]
strsFromValue t = case t of
VString s -> return [str s]
VC s t -> do
s' <- strsFromValue s
t' <- strsFromValue t
return [plusStr x y | x <- s', y <- t']
{-
VGlue s t -> do
s' <- strsFromValue s
t' <- strsFromValue t
return [glueStr x y | x <- s', y <- t']
-}
VAlts d vs -> do
d0 <- strsFromValue d
v0 <- mapM (strsFromValue . fst) vs
c0 <- mapM (strsFromValue . snd) vs
--let vs' = zip v0 c0
return [strTok (str2strings def) vars |
def <- d0,
vars <- [[(str2strings v, map sstr c) | (v,c) <- zip vv c0] |
vv <- sequence v0]
]
VFV ts -> concat # mapM strsFromValue ts
VStrs ts -> concat # mapM strsFromValue ts
_ -> fail ("cannot get Str from value " ++ show t)
vfv vs = case nub vs of
[v] -> v
vs -> VFV vs
select env vv =
case vv of
(v1,VFV vs) -> vfv [select env (v1,v2)|v2<-vs]
(VFV vs,v2) -> vfv [select env (v1,v2)|v1<-vs]
(v1@(VV pty vs rs),v2) ->
err (const (VS v1 v2)) id $
do --ats <- allParamValues (srcgr env) pty
--let vs = map (value0 env) ats
i <- maybeErr "no match" $ findIndex (==v2) vs
return (ix (gloc env) "select" rs i)
(VT _ _ [(PW,Bind b)],_) -> {-trace "eliminate wild card table" $-} b []
(v1@(VT _ _ cs),v2) ->
err (\_->ok2 VS v1 v2) (err bug id . valueMatch env) $
match (gloc env) cs v2
(VS (VV pty pvs rs) v12,v2) -> VS (VV pty pvs [select env (v11,v2)|v11<-rs]) v12
(v1,v2) -> ok2 VS v1 v2
match loc cs v =
case value2term loc [] v of
Left i -> bad ("variable #"++show i++" is out of scope")
Right t -> err bad return (matchPattern cs t)
where
bad = fail . ("In pattern matching: "++)
valueMatch :: CompleteEnv -> (Bind Env,Substitution) -> Err Value
valueMatch env (Bind f,env') = f # mapPairsM (value0 env) env'
valueTable :: CompleteEnv -> TInfo -> [Case] -> Err OpenValue
valueTable env i cs =
case i of
TComp ty -> do pvs <- paramValues env ty
((VV ty pvs .) # sequence) # mapM (value env.snd) cs
_ -> do ty <- getTableType i
cs' <- mapM valueCase cs
err (dynamic cs' ty) return (convert cs' ty)
where
dynamic cs' ty _ = cases cs' # value env ty
cases cs' vty vs = err keep ($vs) (convertv cs' (vty vs))
where
keep msg = --trace (msg++"\n"++render (ppTerm Unqualified 0 (T i cs))) $
VT wild (vty vs) (mapSnd ($vs) cs')
wild = case i of TWild _ -> True; _ -> False
convertv cs' vty =
case value2term (gloc env) [] vty of
Left i -> fail ("variable #"++show i++" is out of scope")
Right pty -> convert' cs' =<< paramValues'' env pty
convert cs' ty = convert' cs' =<< paramValues' env ty
convert' cs' ((pty,vs),pvs) =
do sts <- mapM (matchPattern cs') vs
return $ \ vs -> VV pty pvs $ map (err bug id . valueMatch env)
(mapFst ($vs) sts)
valueCase (p,t) = do p' <- measurePatt # inlinePattMacro p
pvs <- linPattVars p'
vt <- value (extend pvs env) t
return (p',\vs-> Bind $ \bs-> vt (push' p' bs pvs vs))
inlinePattMacro p =
case p of
PM qc -> do r <- resource env qc
case r of
VPatt p' -> inlinePattMacro p'
_ -> ppbug $ hang "Expected pattern macro:" 4
(show r)
_ -> composPattOp inlinePattMacro p
paramValues env ty = snd # paramValues' env ty
paramValues' env ty = paramValues'' env =<< nfx (global env) ty
paramValues'' env pty = do ats <- allParamValues (srcgr env) pty
pvs <- mapM (eval (global env) []) ats
return ((pty,ats),pvs)
push' p bs xs = if length bs/=length xs
then bug $ "push "++show (p,bs,xs)
else push bs xs
push :: Env -> LocalScope -> Stack -> Stack
push bs [] vs = vs
push bs (x:xs) vs = maybe err id (lookup x bs):push bs xs vs
where err = bug $ "Unbound pattern variable "++showIdent x
apply' :: CompleteEnv -> Term -> [OpenValue] -> Err OpenValue
apply' env t [] = value env t
apply' env t vs =
case t of
QC x -> return $ \ svs -> VCApp x (map ($svs) vs)
{-
Q x@(m,f) | m==cPredef -> return $
let constr = --trace ("predef "++show x) .
VApp x
in \ svs -> maybe constr id (Map.lookup f predefs)
$ map ($svs) vs
| otherwise -> do r <- resource env x
return $ \ svs -> vapply (gloc env) r (map ($svs) vs)
-}
App t1 t2 -> apply' env t1 . (:vs) =<< value env t2
_ -> do fv <- value env t
return $ \ svs -> vapply (gloc env) (fv svs) (map ($svs) vs)
vapply :: GLocation -> Value -> [Value] -> Value
vapply loc v [] = v
vapply loc v vs =
case v of
VError {} -> v
-- VClosure env (Abs b x t) -> beta gr env b x t vs
VAbs bt _ (Bind f) -> vbeta loc bt f vs
VApp pre vs1 -> delta' pre (vs1++vs)
where
delta' Trace (v1:v2:vs) = let vr = vapply loc v2 vs
in vtrace loc v1 vr
delta' pre vs = err msg vfv $ mapM (delta pre) (varyList vs)
--msg = const (VApp pre (vs1++vs))
msg = bug . (("Applying Predef."++showIdent (predefName pre)++": ")++)
VS (VV t pvs fs) s -> VS (VV t pvs [vapply loc f vs|f<-fs]) s
VFV fs -> vfv [vapply loc f vs|f<-fs]
VCApp f vs0 -> VCApp f (vs0++vs)
VMeta i env vs0 -> VMeta i env (vs0++vs)
VGen i vs0 -> VGen i (vs0++vs)
v -> bug $ "vapply "++show v++" "++show vs
vbeta loc bt f (v:vs) =
case (bt,v) of
(Implicit,VImplArg v) -> ap v
(Explicit, v) -> ap v
where
ap (VFV avs) = vfv [vapply loc (f v) vs|v<-avs]
ap v = vapply loc (f v) vs
vary (VFV vs) = vs
vary v = [v]
varyList = mapM vary
{-
beta env b x t (v:vs) =
case (b,v) of
(Implicit,VImplArg v) -> apply' (ext (x,v) env) t vs
(Explicit, v) -> apply' (ext (x,v) env) t vs
-}
vtrace loc arg res = trace (render (hang (pv arg) 4 ("->"<+>pv res))) res
where
pv v = case v of
VRec (f:as) -> hang (pf f) 4 (fsep (map pa as))
_ -> ppV v
pf (_,VString n) = pp n
pf (_,v) = ppV v
pa (_,v) = ppV v
ppV v = case value2term' True loc [] v of
Left i -> "variable #" <> pp i <+> "is out of scope"
Right t -> ppTerm Unqualified 10 t
-- | Convert a value back to a term
value2term :: GLocation -> [Ident] -> Value -> Either Int Term
value2term = value2term' False
value2term' stop loc xs v0 =
case v0 of
VApp pre vs -> liftM (foldl App (Q (cPredef,predefName pre))) (mapM v2t vs)
VCApp f vs -> liftM (foldl App (QC f)) (mapM v2t vs)
VGen j vs -> liftM2 (foldl App) (var j) (mapM v2t vs)
VMeta j env vs -> liftM (foldl App (Meta j)) (mapM v2t vs)
VProd bt v x f -> liftM2 (Prod bt x) (v2t v) (v2t' x f)
VAbs bt x f -> liftM (Abs bt x) (v2t' x f)
VInt n -> return (EInt n)
VFloat f -> return (EFloat f)
VString s -> return (if null s then Empty else K s)
VSort s -> return (Sort s)
VImplArg v -> liftM ImplArg (v2t v)
VTblType p res -> liftM2 Table (v2t p) (v2t res)
VRecType rs -> liftM RecType (mapM (\(l,v) -> fmap ((,) l) (v2t v)) rs)
VRec as -> liftM R (mapM (\(l,v) -> v2t v >>= \t -> return (l,(Nothing,t))) as)
VV t _ vs -> liftM (V t) (mapM v2t vs)
VT wild v cs -> v2t v >>= \t -> liftM (T ((if wild then TWild else TTyped) t)) (mapM nfcase cs)
VFV vs -> liftM FV (mapM v2t vs)
VC v1 v2 -> liftM2 C (v2t v1) (v2t v2)
VS v1 v2 -> liftM2 S (v2t v1) (v2t v2)
VP v l -> v2t v >>= \t -> return (P t l)
VPatt p -> return (EPatt p)
VPattType v -> v2t v >>= return . EPattType
VAlts v vvs -> liftM2 Alts (v2t v) (mapM (\(x,y) -> liftM2 (,) (v2t x) (v2t y)) vvs)
VStrs vs -> liftM Strs (mapM v2t vs)
-- VGlue v1 v2 -> Glue (v2t v1) (v2t v2)
-- VExtR v1 v2 -> ExtR (v2t v1) (v2t v2)
VError err -> return (Error err)
_ -> bug ("value2term "++show loc++" : "++show v0)
where
v2t = v2txs xs
v2txs = value2term' stop loc
v2t' x f = v2txs (x:xs) (bind f (gen xs))
var j
| j<length xs = Right (Vr (reverse xs !! j))
| otherwise = Left j
pushs xs e = foldr push e xs
push x (env,xs) = ((x,gen xs):env,x:xs)
gen xs = VGen (length xs) []
nfcase (p,f) = liftM ((,) p) (v2txs xs' (bind f env'))
where (env',xs') = pushs (pattVars p) ([],xs)
bind (Bind f) x = if stop
then VSort (identS "...") -- hmm
else f x
linPattVars p =
if null dups
then return pvs
else fail.render $ hang "Pattern is not linear. All variable names on the left-hand side must be distinct." 4 (ppPatt Unqualified 0 p)
where
allpvs = allPattVars p
pvs = nub allpvs
dups = allpvs \\ pvs
pattVars = nub . allPattVars
allPattVars p =
case p of
PV i -> [i]
PAs i p -> i:allPattVars p
_ -> collectPattOp allPattVars p
---
ix loc fn xs i =
if i<n
then xs !! i
else bugloc loc $ "(!!): index too large in "++fn++", "++show i++"<"++show n
where n = length xs
infixl 1 #,<# --,@@
f # x = fmap f x
mf <# mx = ap mf mx
--m1 @@ m2 = (m1 =<<) . m2
both f (x,y) = (,) # f x <# f y
bugloc loc s = ppbug $ ppL loc s
bug msg = ppbug msg
ppbug doc = error $ render $ hang "Internal error in Compute.ConcreteNew:" 4 doc

View File

@@ -0,0 +1,168 @@
-- | Implementations of predefined functions
{-# LANGUAGE TypeSynonymInstances, FlexibleInstances #-}
module GF.Compile.Compute.Predef(predef,predefName,delta) where
import qualified Data.Map as Map
import Data.Array(array,(!))
import Data.List (isInfixOf)
import Data.Char (isUpper,toLower,toUpper)
import Control.Monad(ap)
import GF.Data.Utilities (apBoth) --mapSnd
import GF.Compile.Compute.Value
import GF.Infra.Ident (Ident,showIdent) --,varX
import GF.Data.Operations(Err) -- ,err
import GF.Grammar.Predef
--------------------------------------------------------------------------------
class Predef a where
toValue :: a -> Value
fromValue :: Value -> Err a
instance Predef Int where
toValue = VInt
fromValue (VInt i) = return i
fromValue v = verror "Int" v
instance Predef Bool where
toValue = boolV
instance Predef String where
toValue = string
fromValue v = case norm v of
VString s -> return s
_ -> verror "String" v
instance Predef Value where
toValue = id
fromValue = return
instance Predef Predefined where
toValue p = VApp p []
fromValue v = case v of
VApp p _ -> return p
_ -> fail $ "Expected a predefined constant, got something else"
{-
instance (Predef a,Predef b) => Predef (a->b) where
toValue f = VAbs Explicit (varX 0) $ Bind $ err bug (toValue . f) . fromValue
-}
verror t v =
case v of
VError e -> fail e
VGen {} -> fail $ "Expected a static value of type "++t
++", got a dynamic value"
_ -> fail $ "Expected a value of type "++t++", got "++show v
--------------------------------------------------------------------------------
predef f = maybe undef return (Map.lookup f predefs)
where
undef = fail $ "Unimplemented predfined operator: Predef."++showIdent f
predefs :: Map.Map Ident Predefined
predefs = Map.fromList predefList
predefName pre = predefNames ! pre
predefNames = array (minBound,maxBound) (map swap predefList)
predefList =
[(cDrop,Drop),(cTake,Take),(cTk,Tk),(cDp,Dp),(cEqStr,EqStr),
(cOccur,Occur),(cOccurs,Occurs),(cToUpper,ToUpper),(cToLower,ToLower),
(cIsUpper,IsUpper),(cLength,Length),(cPlus,Plus),(cEqInt,EqInt),
(cLessInt,LessInt),
-- cShow, cRead, cMapStr, cEqVal
(cError,Error),(cTrace,Trace),
-- Canonical values:
(cPBool,PBool),(cPFalse,PFalse),(cPTrue,PTrue),(cInt,Int),(cFloat,Float),
(cInts,Ints),(cNonExist,NonExist)
,(cBIND,BIND),(cSOFT_BIND,SOFT_BIND),(cSOFT_SPACE,SOFT_SPACE)
,(cCAPIT,CAPIT),(cALL_CAPIT,ALL_CAPIT)]
--- add more functions!!!
delta f vs =
case f of
Drop -> fromNonExist vs NonExist (ap2 (drop::Int->String->String))
Take -> fromNonExist vs NonExist (ap2 (take::Int->String->String))
Tk -> fromNonExist vs NonExist (ap2 tk)
Dp -> fromNonExist vs NonExist (ap2 dp)
EqStr -> fromNonExist vs PFalse (ap2 ((==)::String->String->Bool))
Occur -> fromNonExist vs PFalse (ap2 occur)
Occurs -> fromNonExist vs PFalse (ap2 occurs)
ToUpper -> fromNonExist vs NonExist (ap1 (map toUpper))
ToLower -> fromNonExist vs NonExist (ap1 (map toLower))
IsUpper -> fromNonExist vs PFalse (ap1 (all' isUpper))
Length -> fromNonExist vs (0::Int) (ap1 (length::String->Int))
Plus -> ap2 ((+)::Int->Int->Int)
EqInt -> ap2 ((==)::Int->Int->Bool)
LessInt -> ap2 ((<)::Int->Int->Bool)
{- -- | Show | Read | ToStr | MapStr | EqVal -}
Error -> ap1 VError
Trace -> ap2 vtrace
-- Canonical values:
PBool -> canonical
Int -> canonical
Float -> canonical
Ints -> canonical
PFalse -> canonical
PTrue -> canonical
NonExist-> canonical
BIND -> canonical
SOFT_BIND->canonical
SOFT_SPACE->canonical
CAPIT -> canonical
ALL_CAPIT->canonical
where
canonical = delay
delay = return (VApp f vs) -- wrong number of arguments
ap1 f = case vs of
[v1] -> (toValue . f) `fmap` fromValue v1
_ -> delay
ap2 f = case vs of
[v1,v2] -> toValue `fmap` (f `fmap` fromValue v1 `ap` fromValue v2)
_ -> delay
fromNonExist vs a b
| null [v | v@(VApp NonExist _) <- vs] = b
| otherwise = return (toValue a)
vtrace :: Value -> Value -> Value
vtrace x y = y -- tracing is implemented elsewhere
-- unimpl id = bug $ "unimplemented predefined function: "++showIdent id
-- problem id vs = bug $ "unexpected arguments: Predef."++showIdent id++" "++show vs
tk i s = take (max 0 (length s - i)) s :: String
dp i s = drop (max 0 (length s - i)) s :: String
occur s t = isInfixOf (s::String) (t::String)
occurs s t = any (`elem` (t::String)) (s::String)
all' = all :: (a->Bool) -> [a] -> Bool
boolV b = VCApp (cPredef,if b then cPTrue else cPFalse) []
norm v =
case v of
VC v1 v2 -> case apBoth norm (v1,v2) of
(VString s1,VString s2) -> VString (s1++" "++s2)
(v1,v2) -> VC v1 v2
_ -> v
{-
strict v = case v of
VError err -> Left err
_ -> Right v
-}
string s = case words s of
[] -> VString ""
ss -> foldr1 VC (map VString ss)
---
swap (x,y) = (y,x)
{-
bug msg = ppbug msg
ppbug doc = error $ render $
hang "Internal error in Compute.Predef:" 4 doc
-}

View File

@@ -0,0 +1,56 @@
module GF.Compile.Compute.Value where
import GF.Grammar.Grammar(Label,Type,MetaId,Patt,QIdent)
import PGF.Internal(BindType)
import GF.Infra.Ident(Ident)
import Text.Show.Functions()
import Data.Ix(Ix)
-- | Self-contained (not quite) representation of values
data Value
= VApp Predefined [Value] -- from Q, always Predef.x, has a built-in value
| VCApp QIdent [Value] -- from QC, constructors
| VGen Int [Value] -- for lambda bound variables, possibly applied
| VMeta MetaId Env [Value]
-- -- | VClosure Env Term -- used in Typecheck.ConcreteNew
| VAbs BindType Ident Binding -- used in Compute.ConcreteNew
| VProd BindType Value Ident Binding -- used in Compute.ConcreteNew
| VInt Int
| VFloat Double
| VString String
| VSort Ident
| VImplArg Value
| VTblType Value Value
| VRecType [(Label,Value)]
| VRec [(Label,Value)]
| VV Type [Value] [Value] -- preserve type for conversion back to Term
| VT Wild Value [(Patt,Bind Env)]
| VC Value Value
| VS Value Value
| VP Value Label
| VPatt Patt
| VPattType Value
| VFV [Value]
| VAlts Value [(Value, Value)]
| VStrs [Value]
-- -- | VGlue Value Value -- hmm
-- -- | VExtR Value Value -- hmm
| VError String
deriving (Eq,Show)
type Wild = Bool
type Binding = Bind Value
data Bind a = Bind (a->Value) deriving Show
instance Eq (Bind a) where x==y = False
type Env = [(Ident,Value)]
-- | Predefined functions
data Predefined = Drop | Take | Tk | Dp | EqStr | Occur | Occurs | ToUpper
| ToLower | IsUpper | Length | Plus | EqInt | LessInt
{- | Show | Read | ToStr | MapStr | EqVal -}
| Error | Trace
-- Canonical values below:
| PBool | PFalse | PTrue | Int | Float | Ints | NonExist
| BIND | SOFT_BIND | SOFT_SPACE | CAPIT | ALL_CAPIT
deriving (Show,Eq,Ord,Ix,Bounded,Enum)

View File

@@ -1,7 +1,5 @@
-- | Translate concrete syntax to Haskell
module GF.Compile.ConcreteToHaskell(concretes2haskell,concrete2haskell) where
import PGF2(Literal(..))
import Data.List(isPrefixOf,sort,sortOn)
import qualified Data.Map as M
import qualified Data.Set as S
@@ -9,7 +7,7 @@ import GF.Text.Pretty
--import GF.Grammar.Predef(cPredef,cInts)
--import GF.Compile.Compute.Predef(predef)
--import GF.Compile.Compute.Value(Predefined(..))
import GF.Infra.Ident(Ident,identC,identS,identW,prefixIdent,showRawIdent,rawIdentS)
import GF.Infra.Ident(Ident,identS,identW,prefixIdent)
import GF.Infra.Option
import GF.Haskell as H
import GF.Grammar.Canonical as C
@@ -18,13 +16,13 @@ import Debug.Trace(trace)
-- | Generate Haskell code for the all concrete syntaxes associated with
-- the named abstract syntax in given the grammar.
concretes2haskell opts absname gr = do
Grammar abstr cncs <- grammar2canonical opts absname gr
return [(filename,render80 $ concrete2haskell opts abstr cncmod)
| cncmod<-cncs,
let ModId name = concName cncmod
filename = showRawIdent name ++ ".hs" :: FilePath
]
concretes2haskell opts absname gr =
[(filename,render80 $ concrete2haskell opts abstr cncmod)
| let Grammar abstr cncs = grammar2canonical opts absname gr,
cncmod<-cncs,
let ModId name = concName cncmod
filename = name ++ ".hs" :: FilePath
]
-- | Generate Haskell code for the given concrete module.
-- The only options that make a difference are
@@ -55,7 +53,7 @@ concrete2haskell opts
labels = S.difference (S.unions (map S.fromList recs)) common_labels
common_records = S.fromList [[label_s]]
common_labels = S.fromList [label_s]
label_s = LabelId (rawIdentS "s")
label_s = LabelId "s"
signature (CatDef c _) = TypeSig lf (Fun abs (pure lin))
where
@@ -71,7 +69,7 @@ concrete2haskell opts
where
--funcats = S.fromList [c | FunDef f (C.Type _ (TypeApp c _))<-funs]
allcats = S.fromList [c | CatDef c _<-cats]
gId :: ToIdent i => i -> Ident
gId = (if haskellOption opts HaskellNoPrefix then id else prefixIdent "G")
. toIdent
@@ -118,7 +116,7 @@ concrete2haskell opts
where (ls,ts) = unzip $ sortOn fst [(l,t)|RecordRow l t<-rs]
StrType -> tcon0 (identS "Str")
TableType pt lt -> Fun (ppT pt) (ppT lt)
-- TupleType lts ->
-- TupleType lts ->
lincatDef (LincatDef c t) = tsyn0 (lincatName c) (convLinType t)
@@ -128,7 +126,7 @@ concrete2haskell opts
linDefs = map eqn . sortOn fst . map linDef
where eqn (cat,(f,(ps,rhs))) = (cat,Eqn (f,ps) rhs)
linDef (LinDef f xs rhs0) =
linDef (LinDef f xs rhs0) =
(cat,(linfunName cat,(lhs,rhs)))
where
lhs = [ConP (aId f) (map VarP abs_args)]
@@ -146,7 +144,7 @@ concrete2haskell opts
where
vs = [(VarValueId (Unqual x),a)|(VarId x,a)<-zip xs args]
env= [(VarValueId (Unqual x),lc)|(VarId x,lc)<-zip xs (map arglincat absctx)]
letlin a (TypeBinding _ (C.Type _ (TypeApp acat _))) =
(a,Ap (Var (linfunName acat)) (Var (abs_arg a)))
@@ -183,13 +181,13 @@ concrete2haskell opts
ppL l =
case l of
LFlt x -> pure (lit x)
LInt n -> pure (lit n)
LStr s -> pure (token s)
FloatConstant x -> pure (lit x)
IntConstant n -> pure (lit n)
StrConstant s -> pure (token s)
pId p@(ParamId s) =
if "to_R_" `isPrefixOf` unqual s then toIdent p else gId p -- !! a hack
table cs =
if all (null.patVars) ps
then lets ds (LambdaCase [(ppP p,t')|(p,t')<-zip ps ts'])
@@ -317,13 +315,13 @@ instance Records rhs => Records (TableRow rhs) where
-- | Record subtyping is converted into explicit coercions in Haskell
coerce env ty t =
case (ty,t) of
case (ty,t) of
(_,VariantValue ts) -> VariantValue (map (coerce env ty) ts)
(TableType ti tv,TableValue _ cs) ->
TableValue ti [TableRow p (coerce env tv t)|TableRow p t<-cs]
(RecordType rt,RecordValue r) ->
RecordValue [RecordRow l (coerce env ft f) |
RecordRow l f<-r,ft<-[ft | RecordRow l' ft <- rt, l'==l]]
RecordRow l f<-r,ft<-[ft|RecordRow l' ft<-rt,l'==l]]
(RecordType rt,VarValue x)->
case lookup x env of
Just ty' | ty'/=ty -> -- better to compare to normal form of ty'
@@ -336,17 +334,18 @@ coerce env ty t =
_ -> t
where
app f ts = ParamConstant (Param f ts) -- !! a hack
to_rcon = ParamId . Unqual . rawIdentS . to_rcon' . labels
to_rcon = ParamId . Unqual . to_rcon' . labels
patVars p = []
labels r = [l | RecordRow l _ <- r]
labels r = [l|RecordRow l _<-r]
proj = Var . identS . proj'
proj' (LabelId l) = "proj_" ++ showRawIdent l
proj' (LabelId l) = "proj_"++l
rcon = Var . rcon'
rcon' = identS . rcon_name
rcon_name ls = "R"++concat (sort ['_':showRawIdent l | LabelId l <- ls])
rcon_name ls = "R"++concat (sort ['_':l|LabelId l<-ls])
to_rcon' = ("to_"++) . rcon_name
recordType ls =
@@ -401,17 +400,17 @@ linfunName c = prefixIdent "lin" (toIdent c)
class ToIdent i where toIdent :: i -> Ident
instance ToIdent ParamId where toIdent (ParamId q) = qIdentC q
instance ToIdent PredefId where toIdent (PredefId s) = identC s
instance ToIdent CatId where toIdent (CatId s) = identC s
instance ToIdent C.FunId where toIdent (FunId s) = identC s
instance ToIdent VarValueId where toIdent (VarValueId q) = qIdentC q
instance ToIdent ParamId where toIdent (ParamId q) = qIdentS q
instance ToIdent PredefId where toIdent (PredefId s) = identS s
instance ToIdent CatId where toIdent (CatId s) = identS s
instance ToIdent C.FunId where toIdent (FunId s) = identS s
instance ToIdent VarValueId where toIdent (VarValueId q) = qIdentS q
qIdentC = identS . unqual
qIdentS = identS . unqual
unqual (Qual (ModId m) n) = showRawIdent m++"_"++ showRawIdent n
unqual (Unqual n) = showRawIdent n
unqual (Qual (ModId m) n) = m++"_"++n
unqual (Unqual n) = n
instance ToIdent VarId where
toIdent Anonymous = identW
toIdent (VarId s) = identC s
toIdent (VarId s) = identS s

View File

@@ -3,7 +3,11 @@ module GF.Compile.ExampleBased (
configureExBased
) where
import PGF2
import PGF
--import PGF.Probabilistic
--import PGF.Morphology
--import GF.Compile.ToAPI
import Data.List
parseExamplesInGrammar :: ExConfiguration -> FilePath -> IO (FilePath,[String])
@@ -33,38 +37,47 @@ convertFile conf src file = do
(ex, end) = break (=='"') (tail exend)
in ((unwords (words cat),ex), tail end) -- quotes ignored
pgf = resource_pgf conf
morpho = resource_morpho conf
lang = language conf
convEx (cat,ex) = do
appn "("
let typ = maybe (error "no valid cat") id $ readType cat
ws <- case parse lang typ ex of
ParseFailed _ _ -> do
ws <- case fst (parse_ pgf lang typ (Just 4) ex) of
ParseFailed _ -> do
let ws = morphoMissing morpho (words ex)
appv ("WARNING: cannot parse example " ++ ex)
case ws of
[] -> return ()
_ -> appv (" missing words: " ++ unwords ws)
return ws
TypeError _ ->
return []
ParseIncomplete ->
return []
ParseOk ts ->
case ts of
case rank ts of
(t:tt) -> do
if null tt
then return ()
else appv ("WARNING: ambiguous example " ++ ex)
appn (printExp conf (fst t))
mapM_ (appn . (" --- " ++) . printExp conf . fst) tt
appn t
mapM_ (appn . (" --- " ++)) tt
appn ")"
return []
return ws
rank ts = [printExp conf t ++ " -- " ++ show p | (t,p) <- rankTreesByProbs pgf ts]
appf = appendFile file
appn s = appf s >> appf "\n"
appv s = appn ("--- " ++ s) >> putStrLn s
data ExConfiguration = ExConf {
resource_pgf :: PGF,
resource_pgf :: PGF,
resource_morpho :: Morpho,
verbose :: Bool,
language :: Concr,
printExp :: Expr -> String
language :: Language,
printExp :: Tree -> String
}
configureExBased :: PGF -> Concr -> (Expr -> String) -> ExConfiguration
configureExBased pgf concr pr = ExConf pgf False concr pr
configureExBased :: PGF -> Morpho -> Language -> (Tree -> String) -> ExConfiguration
configureExBased pgf morpho lang pr = ExConf pgf morpho False lang pr

View File

@@ -1,10 +1,14 @@
module GF.Compile.Export where
import PGF2
import PGF
import PGF.Internal(ppPGF)
import GF.Compile.PGFtoHaskell
--import GF.Compile.PGFtoAbstract
import GF.Compile.PGFtoJava
import GF.Compile.PGFtoProlog
import GF.Compile.PGFtoJS
import GF.Compile.PGFtoJSON
import GF.Compile.PGFtoPython
import GF.Infra.Option
--import GF.Speech.CFG
import GF.Speech.PGFToCFG
@@ -18,7 +22,6 @@ import GF.Speech.SLF
import GF.Speech.PrRegExp
import Data.Maybe
import qualified Data.Map as Map
import System.FilePath
import GF.Text.Pretty
@@ -32,12 +35,15 @@ exportPGF :: Options
-> [(FilePath,String)] -- ^ List of recommended file names and contents.
exportPGF opts fmt pgf =
case fmt of
FmtPGFPretty -> multi "txt" (showPGF)
FmtPGFPretty -> multi "txt" (render . ppPGF)
FmtCanonicalGF -> [] -- canon "gf" (render80 . abstract2canonical)
FmtCanonicalJson-> []
FmtJavaScript -> multi "js" pgf2js
FmtJSON -> multi "json" pgf2json
FmtPython -> multi "py" pgf2python
FmtHaskell -> multi "hs" (grammar2haskell opts name)
FmtJava -> multi "java" (grammar2java opts name)
FmtProlog -> multi "pl" grammar2prolog
FmtBNF -> single "bnf" bnfPrinter
FmtEBNF -> single "ebnf" (ebnfPrinter opts)
FmtSRGS_XML -> single "grxml" (srgsXmlPrinter opts)
@@ -51,13 +57,20 @@ exportPGF opts fmt pgf =
FmtRegExp -> single "rexp" regexpPrinter
FmtFA -> single "dot" slfGraphvizPrinter
where
name = fromMaybe (abstractName pgf) (flag optName opts)
name = fromMaybe (showCId (abstractName pgf)) (flag optName opts)
multi :: String -> (PGF -> String) -> [(FilePath,String)]
multi ext pr = [(name <.> ext, pr pgf)]
-- canon ext pr = [("canonical"</>name<.>ext,pr pgf)]
single :: String -> (PGF -> Concr -> String) -> [(FilePath,String)]
single ext pr = [(concreteName cnc <.> ext, pr pgf cnc) | cnc <- Map.elems (languages pgf)]
single :: String -> (PGF -> CId -> String) -> [(FilePath,String)]
single ext pr = [(showCId cnc <.> ext, pr pgf cnc) | cnc <- languages pgf]
-- | Get the name of the concrete syntax to generate output from.
-- FIXME: there should be an option to change this.
outputConcr :: PGF -> CId
outputConcr pgf = case languages pgf of
[] -> error "No concrete syntax."
cnc:_ -> cnc

View File

@@ -1,11 +1,10 @@
{-# LANGUAGE CPP #-}
module GF.Compile.GenerateBC(generateByteCode) where
import GF.Grammar
import GF.Grammar.Lookup(lookupAbsDef,lookupFunType)
import GF.Data.Operations
import PGF2(Literal(..))
import PGF2.Internal(CodeLabel,Instr(..),IVal(..),TailInfo(..))
import PGF(CId,utf8CId)
import PGF.Internal(CodeLabel,Instr(..),IVal(..),TailInfo(..),Literal(..))
import qualified Data.Map as Map
import Data.List(nub,mapAccumL)
import Data.Maybe(fromMaybe)
@@ -64,7 +63,7 @@ compileEquations gr arity st (i:is) eqs fl bs = whilePP eqs Map.empty
case_instr t =
case t of
(Q (_,id)) -> CASE (showIdent id)
(Q (_,id)) -> CASE (i2i id)
(EInt n) -> CASE_LIT (LInt n)
(K s) -> CASE_LIT (LStr s)
(EFloat d) -> CASE_LIT (LFlt d)
@@ -106,7 +105,7 @@ compileFun gr eval st vs (App e1 e2) h0 bs args =
compileFun gr eval st vs (Q (m,id)) h0 bs args =
case lookupAbsDef gr m id of
Ok (_,Just _)
-> (h0,bs,eval st (GLOBAL (showIdent id)) args)
-> (h0,bs,eval st (GLOBAL (i2i id)) args)
_ -> let Ok ty = lookupFunType gr m id
(ctxt,_,_) = typeForm ty
c_arity = length ctxt
@@ -115,14 +114,14 @@ compileFun gr eval st vs (Q (m,id)) h0 bs args =
diff = c_arity-n_args
in if diff <= 0
then if n_args == 0
then (h0,bs,eval st (GLOBAL (showIdent id)) [])
then (h0,bs,eval st (GLOBAL (i2i id)) [])
else let h1 = h0 + 2 + n_args
in (h1,bs,PUT_CONSTR (showIdent id):is1++eval st (HEAP h0) [])
in (h1,bs,PUT_CONSTR (i2i id):is1++eval st (HEAP h0) [])
else let h1 = h0 + 1 + n_args
is2 = [SET (FREE_VAR i) | i <- [0..n_args-1]] ++ [SET (ARG_VAR (i+1)) | i <- [0..diff-1]]
b = CHECK_ARGS diff :
ALLOC (c_arity+2) :
PUT_CONSTR (showIdent id) :
PUT_CONSTR (i2i id) :
is2 ++
TUCK (ARG_VAR 0) diff :
EVAL (HEAP h0) (TailCall diff) :
@@ -168,16 +167,16 @@ compileFun gr eval st vs e _ _ _ = error (show e)
compileArg gr st vs (Q(m,id)) h0 bs =
case lookupAbsDef gr m id of
Ok (_,Just _) -> (h0,bs,GLOBAL (showIdent id),[])
Ok (_,Just _) -> (h0,bs,GLOBAL (i2i id),[])
_ -> let Ok ty = lookupFunType gr m id
(ctxt,_,_) = typeForm ty
c_arity = length ctxt
in if c_arity == 0
then (h0,bs,GLOBAL (showIdent id),[])
then (h0,bs,GLOBAL (i2i id),[])
else let is2 = [SET (ARG_VAR (i+1)) | i <- [0..c_arity-1]]
b = CHECK_ARGS c_arity :
ALLOC (c_arity+2) :
PUT_CONSTR (showIdent id) :
PUT_CONSTR (i2i id) :
is2 ++
TUCK (ARG_VAR 0) c_arity :
EVAL (HEAP h0) (TailCall c_arity) :
@@ -225,12 +224,12 @@ compileArg gr st vs e h0 bs =
diff = c_arity-n_args
in if diff <= 0
then let h2 = h1 + 2 + n_args
in (h2,bs1,HEAP h1,is1 ++ (PUT_CONSTR (showIdent id) : is2))
in (h2,bs1,HEAP h1,is1 ++ (PUT_CONSTR (i2i id) : is2))
else let h2 = h1 + 1 + n_args
is2 = [SET (FREE_VAR i) | i <- [0..n_args-1]] ++ [SET (ARG_VAR (i+1)) | i <- [0..diff-1]]
b = CHECK_ARGS diff :
ALLOC (c_arity+2) :
PUT_CONSTR (showIdent id) :
PUT_CONSTR (i2i id) :
is2 ++
TUCK (ARG_VAR 0) diff :
EVAL (HEAP h0) (TailCall diff) :
@@ -299,6 +298,9 @@ freeVars xs (Vr x)
| not (elem x xs) = [x]
freeVars xs e = collectOp (freeVars xs) e
i2i :: Ident -> CId
i2i = utf8CId . ident2utf8
push_is :: Int -> Int -> [IVal] -> [IVal]
push_is i 0 is = is
push_is i n is = ARG_VAR i : push_is (i-1) (n-1) is

View File

@@ -10,173 +10,631 @@
-----------------------------------------------------------------------------
module GF.Compile.GeneratePMCFG
(generatePMCFG, pgfCncCat, addPMCFG
(generatePMCFG, pgfCncCat, addPMCFG, resourceValues
) where
import GF.Grammar hiding (VApp)
import GF.Grammar.Predef
import GF.Grammar.Lookup
import GF.Infra.CheckM
--import PGF.CId
import PGF.Internal as PGF(CncCat(..),Symbol(..),fidVar)
import GF.Infra.Option
import GF.Grammar hiding (Env, mkRecord, mkTable)
import GF.Grammar.Lookup
import GF.Grammar.Predef
import GF.Grammar.Lockfield (isLockLabel)
import GF.Data.BacktrackM
import GF.Data.Operations
import GF.Infra.UseIO (ePutStr,ePutStrLn) -- IOE,
import GF.Data.Utilities (updateNthM) --updateNth
import GF.Compile.Compute.ConcreteNew(normalForm,resourceValues)
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.List as List
--import qualified Data.IntMap as IntMap
import qualified Data.IntSet as IntSet
import GF.Text.Pretty
import GF.Compile.Compute.Concrete
import GF.Data.Operations(Err(..))
import PGF2.Transactions
import qualified Data.Map.Strict as Map
import Data.Array.IArray
import Data.Array.Unboxed
--import Data.Maybe
--import Data.Char (isDigit)
import Control.Applicative(Applicative(..))
import Control.Monad
import Data.List(mapAccumL)
import Control.Monad.Identity
--import Control.Exception
--import Debug.Trace(trace)
import qualified Control.Monad.Fail as Fail
generatePMCFG :: Options -> FilePath -> SourceGrammar -> SourceModule -> Check SourceModule
generatePMCFG opts cwd gr cmo@(cm,cmi) = do
let gr' = prependModule gr cmo
js <- mapM (addPMCFG opts cwd gr' cmi) (Map.toList (jments cmi))
return (cm,cmi{jments = (Map.fromAscList js)})
----------------------------------------------------------------------
-- main conversion function
addPMCFG opts cwd gr cmi (id,CncFun mty@(Just (_,cat,ctxt,val)) mlin@(Just (L loc term)) mprn Nothing) =
checkInModule cwd cmi loc ("Happened in the PMCFG generation for" <+> id) $ do
rules <- pmcfgForm gr term ctxt val
return (id,CncFun mty mlin mprn (Just rules))
addPMCFG opts cwd gr cmi id_info = return id_info
pmcfgForm :: Grammar -> Term -> Context -> Type -> Check [PMCFGRule]
pmcfgForm gr t ctxt ty =
runEvalM gr $ do
((_,ms),args) <- mapAccumM (\(d,ms) (_,_,ty) -> do
let (ms',_,t) = type2metaTerm gr d ms 0 [] ty
tnk <- newThunk [] t
return ((d+1,ms'),tnk))
(0,Map.empty) ctxt
sequence_ [newMeta (Just ty) i | (i,ty) <- Map.toList ms]
v <- eval [] t args
(lins,params) <- flatten v ty ([],[])
lins <- mapM str2lin lins
(r,rs,_) <- compute params
args <- zipWithM tnk2pmcfgcat args ctxt
return (PMCFGRule (PMCFGCat r rs) args (reverse lins))
where
tnk2pmcfgcat tnk (_,_,ty) = do
v <- force tnk []
(_,params) <- flatten v ty ([],[])
(r,rs,_) <- compute params
return (PMCFGCat r rs)
compute [] = return (0,[],1)
compute (v:vs) = do
(r, rs ,cnt ) <- param2int v
(r',rs',cnt') <- compute vs
return (r*cnt'+r',combine cnt' rs rs',cnt*cnt')
type2metaTerm :: SourceGrammar -> Int -> Map.Map MetaId Type -> LIndex -> [(LIndex,Ident)] -> Type -> (Map.Map MetaId Type,Int,Term)
type2metaTerm gr d ms r rs (Sort s) | s == cStr =
(ms,r+1,TSymCat d r rs)
type2metaTerm gr d ms r rs (RecType lbls) =
let ((ms',r'),ass) = mapAccumL (\(ms,r) (lbl,ty) -> let (ms',r',t) = type2metaTerm gr d ms r rs ty
in ((ms',r'),(lbl,(Just ty,t))))
(ms,r) lbls
in (ms',r',R ass)
type2metaTerm gr d ms r rs (Table p q) =
let pv = identS ('p':show (length rs))
(ms',r',t) = type2metaTerm gr d ms r ((r'-r,pv):rs) q
count = case allParamValues gr p of
Ok ts -> length ts
Bad msg -> error msg
in (ms',(r'-r)*count,T (TTyped p) [(PV pv,t)])
type2metaTerm gr d ms r rs ty@(QC q) =
let i = Map.size ms + 1
in (Map.insert i ty ms,r,Meta i)
flatten (VSusp tnk env vs k) ty st = do
tnk_st <- getMeta tnk
case tnk_st of
Evaluated v -> do v <- apply v vs
flatten v ty st
Unbound (Just (QC q)) _ -> do (m,ResParam (Just (L _ ps)) _) <- getInfo q
msum [bind tnk m p | p <- ps]
v <- k tnk
flatten v ty st
--generatePMCFG :: Options -> SourceGrammar -> Maybe FilePath -> SourceModule -> IOE SourceModule
generatePMCFG opts sgr opath cmo@(cm,cmi) = do
(seqs,js) <- mapAccumWithKeyM (addPMCFG opts gr cenv opath am cm) Map.empty (jments cmi)
when (verbAtLeast opts Verbose) $ ePutStrLn ""
return (cm,cmi{mseqs = Just (mkSetArray seqs), jments = js})
where
bind tnk m (p, ctxt) = do
tnks <- mapM (\(_,_,ty) -> newMeta (Just ty) 0) ctxt
setMeta tnk (Evaluated (VApp (m,p) tnks))
flatten (VR as) (RecType lbls) st = do
foldM collect st lbls
cenv = resourceValues opts gr
gr = prependModule sgr cmo
MTConcrete am = mtype cmi
mapAccumWithKeyM :: (Monad m, Ord k) => (a -> k -> b -> m (a,c)) -> a
-> Map.Map k b -> m (a,Map.Map k c)
mapAccumWithKeyM f a m = do let xs = Map.toAscList m
(a,ys) <- mapAccumM f a xs
return (a,Map.fromAscList ys)
where
collect st (lbl,ty) =
case lookup lbl as of
Just tnk -> do v <- force tnk []
flatten v ty st
Nothing -> evalError ("Missing value for label" <+> pp lbl $$
"among" <+> hsep (punctuate (pp ',') (map fst as)))
flatten v@(VT _ env cs) (Table p q) st = do
ts <- getAllParamValues p
foldM collect st ts
mapAccumM f a [] = return (a,[])
mapAccumM f a ((k,x):kxs) = do (a,y ) <- f a k x
(a,kys) <- mapAccumM f a kxs
return (a,(k,y):kys)
--addPMCFG :: Options -> SourceGrammar -> GlobalEnv -> Maybe FilePath -> Ident -> Ident -> SeqSet -> Ident -> Info -> IOE (SeqSet, Info)
addPMCFG opts gr cenv opath am cm seqs id (GF.Grammar.CncFun mty@(Just (cat,cont,val)) mlin@(Just (L loc term)) mprn Nothing) = do
--when (verbAtLeast opts Verbose) $ ePutStr ("\n+ "++showIdent id++" ...")
let pres = protoFCat gr res val
pargs = [protoFCat gr (snd $ catSkeleton ty) lincat | ((_,_,ty),(_,_,lincat)) <- zip ctxt cont]
pmcfgEnv0 = emptyPMCFGEnv
b <- convert opts gr cenv (floc opath loc id) term (cont,val) pargs
let (seqs1,b1) = addSequencesB seqs b
pmcfgEnv1 = foldBM addRule
pmcfgEnv0
(goB b1 CNil [])
(pres,pargs)
pmcfg = getPMCFG pmcfgEnv1
stats = let PMCFG prods funs = pmcfg
(s,e) = bounds funs
!prods_cnt = length prods
!funs_cnt = e-s+1
in (prods_cnt,funs_cnt)
when (verbAtLeast opts Verbose) $
ePutStr ("\n+ "++showIdent id++" "++show (product (map catFactor pargs)))
seqs1 `seq` stats `seq` return ()
when (verbAtLeast opts Verbose) $ ePutStr (" "++show stats)
return (seqs1,GF.Grammar.CncFun mty mlin mprn (Just pmcfg))
where
collect st t = do
tnk <- newThunk [] t
let v0 = VS v tnk []
v <- patternMatch v0 (map (\(p,t) -> (env,[p],[tnk],t)) cs)
flatten v q st
flatten (VV _ tnks) (Table _ q) st = do
foldM collect st tnks
(ctxt,res,_) = err bug typeForm (lookupFunType gr am id)
addRule lins (newCat', newArgs') env0 =
let [newCat] = getFIds newCat'
!fun = mkArray lins
newArgs = map getFIds newArgs'
in addFunction env0 newCat fun newArgs
addPMCFG opts gr cenv opath am cm seqs id (GF.Grammar.CncCat mty@(Just (L _ lincat))
mdef@(Just (L loc1 def))
mref@(Just (L loc2 ref))
mprn
Nothing) = do
let pcat = protoFCat gr (am,id) lincat
pvar = protoFCat gr (MN identW,cVar) typeStr
pmcfgEnv0 = emptyPMCFGEnv
let lincont = [(Explicit, varStr, typeStr)]
b <- convert opts gr cenv (floc opath loc1 id) def (lincont,lincat) [pvar]
let (seqs1,b1) = addSequencesB seqs b
pmcfgEnv1 = foldBM addLindef
pmcfgEnv0
(goB b1 CNil [])
(pcat,[pvar])
let lincont = [(Explicit, varStr, lincat)]
b <- convert opts gr cenv (floc opath loc2 id) ref (lincont,typeStr) [pcat]
let (seqs2,b2) = addSequencesB seqs1 b
pmcfgEnv2 = foldBM addLinref
pmcfgEnv1
(goB b2 CNil [])
(pvar,[pcat])
let pmcfg = getPMCFG pmcfgEnv2
when (verbAtLeast opts Verbose) $ ePutStr ("\n+ "++showIdent id++" "++show (catFactor pcat))
seqs2 `seq` pmcfg `seq` return (seqs2,GF.Grammar.CncCat mty mdef mref mprn (Just pmcfg))
where
collect st tnk = do
v <- force tnk []
flatten v q st
flatten v (Sort s) (lins,params) | s == cStr = do
return (v:lins,params)
flatten v (QC q) (lins,params) = do
return (lins,v:params)
addLindef lins (newCat', newArgs') env0 =
let [newCat] = getFIds newCat'
!fun = mkArray lins
in addFunction env0 newCat fun [[fidVar]]
str2lin (VStr s) = return [SymKS s]
str2lin (VSymCat d r rs) = do (r, rs) <- compute r rs
return [SymCat d r rs]
addLinref lins (newCat', [newArg']) env0 =
let newArg = getFIds newArg'
!fun = mkArray lins
in addFunction env0 fidVar fun [newArg]
addPMCFG opts gr cenv opath am cm seqs id info = return (seqs, info)
floc opath loc id = maybe (L loc id) (\path->L (External path loc) id) opath
convert opts gr cenv loc term ty@(_,val) pargs =
case normalForm cenv loc (etaExpand ty term) of
Error s -> fail $ render $ ppL loc ("Predef.error: "++s)
term -> return $ runCnvMonad gr (convertTerm opts CNil val term) (pargs,[])
where
compute r' [] = return (r',[])
compute r' ((cnt',tnk):tnks) = do
(r, rs,_) <- force tnk [] >>= param2int
(r',rs' ) <- compute r' tnks
return (r*cnt'+r',combine cnt' rs rs')
str2lin (VC vs) = fmap concat (mapM str2lin vs)
str2lin v = do t <- value2term 0 v
evalError ("the term" <+> ppTerm Unqualified 0 t $$
"cannot be evaluated at compile time.")
etaExpand (context,val) = mkAbs pars . flip mkApp args
where pars = [(Explicit,v) | v <- vars]
args = map Vr vars
vars = map (\(bt,x,t) -> x) context
param2int (VApp q tnks) = do
(r , cnt ) <- getIdxCnt q
(r',rs',cnt') <- compute tnks
return (r*cnt' + r',rs',cnt*cnt')
pgfCncCat :: SourceGrammar -> Type -> Int -> CncCat
pgfCncCat gr lincat index =
let ((_,size),schema) = computeCatRange gr lincat
in PGF.CncCat index (index+size-1)
(mkArray (map (renderStyle style{mode=OneLineMode} . ppPath)
(getStrPaths schema)))
where
getIdxCnt q = do
(_,ResValue (L _ ty) idx) <- getInfo q
let QC p = valTypeCnc ty
(_,ResParam _ (Just (_,cnt))) <- getInfo p
return (idx,cnt)
getStrPaths :: Schema Identity s c -> [Path]
getStrPaths = collect CNil []
where
collect path paths (CRec rs) = foldr (\(lbl,Identity t) paths -> collect (CProj lbl path) paths t) paths rs
collect path paths (CTbl _ cs) = foldr (\(trm,Identity t) paths -> collect (CSel trm path) paths t) paths cs
collect path paths (CStr _) = reversePath path : paths
collect path paths (CPar _) = paths
compute [] = return (0,[],1)
compute (tnk:tnks) = do
(r, rs ,cnt ) <- force tnk [] >>= param2int
(r',rs',cnt') <- compute tnks
return (r*cnt'+r',combine cnt' rs rs',cnt*cnt')
param2int (VMeta tnk _ _) = do
tnk_st <- getMeta tnk
case tnk_st of
Evaluated v -> param2int v
Unbound (Just ty) j -> do let QC q = valTypeCnc ty
(_,ResParam _ (Just (_,cnt))) <- getInfo q
return (0,[(1,j)],cnt)
----------------------------------------------------------------------
-- CnvMonad monad
--
-- The branching monad provides backtracking together with
-- recording of the choices made. We have two cases
-- when we have alternative choices:
--
-- * when we have parameter type, then
-- we have to try all possible values
-- * when we have variants we have to try all alternatives
--
-- The conversion monad keeps track of the choices and they are
-- returned as 'Branch' data type.
combine cnt' [] rs' = rs'
combine cnt' rs [] = [(r*cnt',pv) | (r,pv) <- rs]
combine cnt' ((r,pv):rs) ((r',pv'):rs') =
case compare pv pv' of
LT -> (r*cnt', pv ) : combine cnt' rs ((r',pv'):rs')
EQ -> (r*cnt'+r',pv ) : combine cnt' rs ((r',pv'):rs')
GT -> ( r',pv') : combine cnt' ((r,pv):rs) rs'
data Branch a
= Case Int Path [(Term,Branch a)]
| Variant [Branch a]
| Return a
mapAccumM f a [] = return (a,[])
mapAccumM f a (x:xs) = do (a, y) <- f a x
(a,ys) <- mapAccumM f a xs
return (a,y:ys)
newtype CnvMonad a = CM {unCM :: SourceGrammar
-> forall b . (a -> ([ProtoFCat],[Symbol]) -> Branch b)
-> ([ProtoFCat],[Symbol])
-> Branch b}
pgfCncCat = error "TODO: pgfCncCat"
instance Fail.MonadFail CnvMonad where
fail = bug
instance Applicative CnvMonad where
pure = return
(<*>) = ap
instance Monad CnvMonad where
return a = CM (\gr c s -> c a s)
CM m >>= k = CM (\gr c s -> m gr (\a s -> unCM (k a) gr c s) s)
instance MonadState ([ProtoFCat],[Symbol]) CnvMonad where
get = CM (\gr c s -> c s s)
put s = CM (\gr c _ -> c () s)
instance Functor CnvMonad where
fmap f (CM m) = CM (\gr c s -> m gr (c . f) s)
runCnvMonad :: SourceGrammar -> CnvMonad a -> ([ProtoFCat],[Symbol]) -> Branch a
runCnvMonad gr (CM m) s = m gr (\v s -> Return v) s
-- | backtracking for all variants
variants :: [a] -> CnvMonad a
variants xs = CM (\gr c s -> Variant [c x s | x <- xs])
-- | backtracking for all parameter values that a variable could take
choices :: Int -> Path -> CnvMonad Term
choices nr path = do (args,_) <- get
let PFCat _ _ schema = args !! nr
descend schema path CNil
where
descend (CRec rs) (CProj lbl path) rpath = case lookup lbl rs of
Just (Identity t) -> descend t path (CProj lbl rpath)
descend (CRec rs) CNil rpath = do rs <- mapM (\(lbl,Identity t) -> fmap (assign lbl) (descend t CNil (CProj lbl rpath))) rs
return (R rs)
descend (CTbl pt cs) (CSel trm path) rpath = case lookup trm cs of
Just (Identity t) -> descend t path (CSel trm rpath)
descend (CTbl pt cs) CNil rpath = do cs <- mapM (\(trm,Identity t) -> descend t CNil (CSel trm rpath)) cs
return (V pt cs)
descend (CPar (m,vs)) CNil rpath = case vs of
[(value,index)] -> return value
values -> let path = reversePath rpath
in CM (\gr c s -> Case nr path [(value, updateEnv path value gr c s)
| (value,index) <- values])
descend schema path rpath = bug $ "descend "++show (schema,path,rpath)
updateEnv path value gr c (args,seq) =
case updateNthM (restrictProtoFCat path value) nr args of
Just args -> c value (args,seq)
Nothing -> bug "conflict in updateEnv"
-- | the argument should be a parameter type and then
-- the function returns all possible values.
getAllParamValues :: Type -> CnvMonad [Term]
getAllParamValues ty = CM (\gr c -> c (err bug id (allParamValues gr ty)))
mkRecord :: [(Label,CnvMonad (Schema Branch s c))] -> CnvMonad (Schema Branch s c)
mkRecord xs = CM (\gr c -> foldl (\c (lbl,CM m) bs s -> c ((lbl,m gr (\v s -> Return v) s) : bs) s) (c . CRec) xs [])
mkTable :: Type -> [(Term ,CnvMonad (Schema Branch s c))] -> CnvMonad (Schema Branch s c)
mkTable pt xs = CM (\gr c -> foldl (\c (trm,CM m) bs s -> c ((trm,m gr (\v s -> Return v) s) : bs) s) (c . CTbl pt) xs [])
----------------------------------------------------------------------
-- Term Schema
--
-- The term schema is a term-like structure, with records, tables,
-- strings and parameters values, but in addition we could add
-- annotations of arbitrary types
-- | Term schema
data Schema b s c
= CRec [(Label,b (Schema b s c))]
| CTbl Type [(Term, b (Schema b s c))]
| CStr s
| CPar c
--deriving Show -- doesn't work
instance Show s => Show (Schema b s c) where
showsPrec _ sch =
case sch of
CRec r -> showString "CRec " . shows (map fst r)
CTbl t _ -> showString "CTbl " . showsPrec 10 t . showString " _"
CStr s -> showString "CStr " . showsPrec 10 s
CPar c -> showString "CPar{}"
-- | Path into a term or term schema
data Path
= CProj Label Path
| CSel Term Path
| CNil
deriving (Eq,Show)
-- | The ProtoFCat represents a linearization type as term schema.
-- The annotations are as follows: the strings are annotated with
-- their index in the PMCFG tuple, the parameters are annotated
-- with their value both as term and as index.
data ProtoFCat = PFCat Ident Int (Schema Identity Int (Int,[(Term,Int)]))
type Env = (ProtoFCat, [ProtoFCat])
protoFCat :: SourceGrammar -> Cat -> Type -> ProtoFCat
protoFCat gr cat lincat =
case computeCatRange gr lincat of
((_,f),schema) -> PFCat (snd cat) f schema
getFIds :: ProtoFCat -> [FId]
getFIds (PFCat _ _ schema) =
reverse (solutions (variants schema) ())
where
variants (CRec rs) = fmap sum $ mapM (\(lbl,Identity t) -> variants t) rs
variants (CTbl _ cs) = fmap sum $ mapM (\(trm,Identity t) -> variants t) cs
variants (CStr _) = return 0
variants (CPar (m,values)) = do (value,index) <- member values
return (m*index)
catFactor :: ProtoFCat -> Int
catFactor (PFCat _ f _) = f
computeCatRange gr lincat = compute (0,1) lincat
where
compute st (RecType rs) = let (st',rs') = List.mapAccumL (\st (lbl,t) -> case lbl of
LVar _ -> let (st',t') = compute st t
in (st ,(lbl,Identity t'))
_ -> let (st',t') = compute st t
in (st',(lbl,Identity t'))) st rs
in (st',CRec rs')
compute st (Table pt vt) = let vs = err bug id (allParamValues gr pt)
(st',cs') = List.mapAccumL (\st v -> let (st',vt') = compute st vt
in (st',(v,Identity vt'))) st vs
in (st',CTbl pt cs')
compute st (Sort s)
| s == cStr = let (index,m) = st
in ((index+1,m),CStr index)
compute st t = let vs = err bug id (allParamValues gr t)
(index,m) = st
in ((index,m*length vs),CPar (m,zip vs [0..]))
ppPath (CProj lbl path) = lbl <+> ppPath path
ppPath (CSel trm path) = ppU 5 trm <+> ppPath path
ppPath CNil = empty
reversePath path = rev CNil path
where
rev path0 CNil = path0
rev path0 (CProj lbl path) = rev (CProj lbl path0) path
rev path0 (CSel trm path) = rev (CSel trm path0) path
----------------------------------------------------------------------
-- term conversion
type Value a = Schema Branch a Term
convertTerm :: Options -> Path -> Type -> Term -> CnvMonad (Value [Symbol])
convertTerm opts sel ctype (Vr x) = convertArg opts ctype (getVarIndex x) (reversePath sel)
convertTerm opts sel ctype (Abs _ _ t) = convertTerm opts sel ctype t -- there are only top-level abstractions and we ignore them !!!
convertTerm opts sel ctype (R record) = convertRec opts sel ctype record
convertTerm opts sel ctype (P term l) = convertTerm opts (CProj l sel) ctype term
convertTerm opts sel ctype (V pt ts) = convertTbl opts sel ctype pt ts
convertTerm opts sel ctype (S term p) = do v <- evalTerm CNil p
convertTerm opts (CSel v sel) ctype term
convertTerm opts sel ctype (FV vars) = do term <- variants vars
convertTerm opts sel ctype term
convertTerm opts sel ctype (C t1 t2) = do v1 <- convertTerm opts sel ctype t1
v2 <- convertTerm opts sel ctype t2
return (CStr (concat [s | CStr s <- [v1,v2]]))
convertTerm opts sel ctype (K t) = return (CStr [SymKS t])
convertTerm opts sel ctype Empty = return (CStr [])
convertTerm opts sel ctype (Alts s alts)= do CStr s <- convertTerm opts CNil ctype s
alts <- forM alts $ \(u,alt) -> do
CStr u <- convertTerm opts CNil ctype u
Strs ps <- unPatt alt
ps <- mapM (convertTerm opts CNil ctype) ps
return (u,map unSym ps)
return (CStr [SymKP s alts])
where
unSym (CStr []) = ""
unSym (CStr [SymKS t]) = t
unSym _ = ppbug $ hang ("invalid prefix in pre expression:") 4 (Alts s alts)
unPatt (EPatt p) = fmap Strs (getPatts p)
unPatt u = return u
getPatts p = case p of
PAlt a b -> liftM2 (++) (getPatts a) (getPatts b)
PString s -> return [K s]
PSeq a b -> do
as <- getPatts a
bs <- getPatts b
return [K (s ++ t) | K s <- as, K t <- bs]
_ -> fail (render ("not valid pattern in pre expression" <+> ppPatt Unqualified 0 p))
convertTerm opts sel ctype (Q (m,f))
| m == cPredef &&
f == cBIND = return (CStr [SymBIND])
| m == cPredef &&
f == cSOFT_BIND = return (CStr [SymSOFT_BIND])
| m == cPredef &&
f == cSOFT_SPACE = return (CStr [SymSOFT_SPACE])
| m == cPredef &&
f == cCAPIT = return (CStr [SymCAPIT])
| m == cPredef &&
f == cALL_CAPIT = return (CStr [SymALL_CAPIT])
| m == cPredef &&
f == cNonExist = return (CStr [SymNE])
{-
convertTerm opts sel@(CProj l _) ctype (ExtR t1 t2@(R rs2))
| l `elem` map fst rs2 = convertTerm opts sel ctype t2
| otherwise = convertTerm opts sel ctype t1
convertTerm opts sel@(CProj l _) ctype (ExtR t1@(R rs1) t2)
| l `elem` map fst rs1 = convertTerm opts sel ctype t1
| otherwise = convertTerm opts sel ctype t2
-}
convertTerm opts CNil ctype t = do v <- evalTerm CNil t
return (CPar v)
convertTerm _ sel _ t = ppbug ("convertTerm" <+> sep [parens (show sel),ppU 10 t])
convertArg :: Options -> Term -> Int -> Path -> CnvMonad (Value [Symbol])
convertArg opts (RecType rs) nr path =
mkRecord (map (\(lbl,ctype) -> (lbl,convertArg opts ctype nr (CProj lbl path))) rs)
convertArg opts (Table pt vt) nr path = do
vs <- getAllParamValues pt
mkTable pt (map (\v -> (v,convertArg opts vt nr (CSel v path))) vs)
convertArg opts (Sort _) nr path = do
(args,_) <- get
let PFCat cat _ schema = args !! nr
l = index (reversePath path) schema
sym | CProj (LVar i) CNil <- path = SymVar nr i
| isLiteralCat opts cat = SymLit nr l
| otherwise = SymCat nr l
return (CStr [sym])
where
index (CProj lbl path) (CRec rs) = case lookup lbl rs of
Just (Identity t) -> index path t
index (CSel trm path) (CTbl _ rs) = case lookup trm rs of
Just (Identity t) -> index path t
index CNil (CStr idx) = idx
convertArg opts ty nr path = do
value <- choices nr (reversePath path)
return (CPar value)
convertRec opts CNil (RecType rs) record =
mkRecord [(lbl,convertTerm opts CNil ctype (proj lbl))|(lbl,ctype)<-rs]
where proj lbl = if isLockLabel lbl then R [] else projectRec lbl record
convertRec opts (CProj lbl path) ctype record =
convertTerm opts path ctype (projectRec lbl record)
convertRec opts _ ctype _ = bug ("convertRec: "++show ctype)
convertTbl opts CNil (Table _ vt) pt ts = do
vs <- getAllParamValues pt
mkTable pt (zipWith (\v t -> (v,convertTerm opts CNil vt t)) vs ts)
convertTbl opts (CSel v sub_sel) ctype pt ts = do
vs <- getAllParamValues pt
case lookup v (zip vs ts) of
Just t -> convertTerm opts sub_sel ctype t
Nothing -> ppbug ( "convertTbl:" <+> ("missing value" <+> v $$
"among" <+> vcat vs))
convertTbl opts _ ctype _ _ = bug ("convertTbl: "++show ctype)
goB :: Branch (Value SeqId) -> Path -> [SeqId] -> BacktrackM Env [SeqId]
goB (Case nr path bs) rpath ss = do (value,b) <- member bs
restrictArg nr path value
goB b rpath ss
goB (Variant bs) rpath ss = do b <- member bs
goB b rpath ss
goB (Return v) rpath ss = goV v rpath ss
goV :: Value SeqId -> Path -> [SeqId] -> BacktrackM Env [SeqId]
goV (CRec xs) rpath ss = foldM (\ss (lbl,b) -> goB b (CProj lbl rpath) ss) ss (reverse xs)
goV (CTbl _ xs) rpath ss = foldM (\ss (trm,b) -> goB b (CSel trm rpath) ss) ss (reverse xs)
goV (CStr seqid) rpath ss = return (seqid : ss)
goV (CPar t) rpath ss = restrictHead (reversePath rpath) t >> return ss
----------------------------------------------------------------------
-- SeqSet
type SeqSet = Map.Map Sequence SeqId
addSequencesB :: SeqSet -> Branch (Value [Symbol]) -> (SeqSet, Branch (Value SeqId))
addSequencesB seqs (Case nr path bs) = let !(seqs1,bs1) = mapAccumL' (\seqs (trm,b) -> let !(seqs',b') = addSequencesB seqs b
in (seqs',(trm,b'))) seqs bs
in (seqs1,Case nr path bs1)
addSequencesB seqs (Variant bs) = let !(seqs1,bs1) = mapAccumL' addSequencesB seqs bs
in (seqs1,Variant bs1)
addSequencesB seqs (Return v) = let !(seqs1,v1) = addSequencesV seqs v
in (seqs1,Return v1)
addSequencesV :: SeqSet -> Value [Symbol] -> (SeqSet, Value SeqId)
addSequencesV seqs (CRec vs) = let !(seqs1,vs1) = mapAccumL' (\seqs (lbl,b) -> let !(seqs',b') = addSequencesB seqs b
in (seqs',(lbl,b'))) seqs vs
in (seqs1,CRec vs1)
addSequencesV seqs (CTbl pt vs)=let !(seqs1,vs1) = mapAccumL' (\seqs (trm,b) -> let !(seqs',b') = addSequencesB seqs b
in (seqs',(trm,b'))) seqs vs
in (seqs1,CTbl pt vs1)
addSequencesV seqs (CStr lin) = let !(seqs1,seqid) = addSequence seqs lin
in (seqs1,CStr seqid)
addSequencesV seqs (CPar i) = (seqs,CPar i)
-- a strict version of Data.List.mapAccumL
mapAccumL' f s [] = (s,[])
mapAccumL' f s (x:xs) = (s'',y:ys)
where !(s', y ) = f s x
!(s'',ys) = mapAccumL' f s' xs
addSequence :: SeqSet -> [Symbol] -> (SeqSet,SeqId)
addSequence seqs lst =
case Map.lookup seq seqs of
Just id -> (seqs,id)
Nothing -> let !last_seq = Map.size seqs
in (Map.insert seq last_seq seqs, last_seq)
where
seq = mkArray lst
------------------------------------------------------------
-- eval a term to ground terms
evalTerm :: Path -> Term -> CnvMonad Term
evalTerm CNil (QC f) = return (QC f)
evalTerm CNil (App x y) = do x <- evalTerm CNil x
y <- evalTerm CNil y
return (App x y)
evalTerm path (Vr x) = choices (getVarIndex x) path
evalTerm path (R rs) =
case path of
CProj lbl path -> evalTerm path (projectRec lbl rs)
CNil -> R `fmap` mapM (\(lbl,(_,t)) -> assign lbl `fmap` evalTerm path t) rs
evalTerm path (P term lbl) = evalTerm (CProj lbl path) term
evalTerm path (V pt ts) =
case path of
CNil -> V pt `fmap` mapM (evalTerm path) ts
CSel trm path ->
do vs <- getAllParamValues pt
case lookup trm (zip vs ts) of
Just t -> evalTerm path t
Nothing -> ppbug $ "evalTerm: missing value:"<+>trm
$$ "among:" <+>fsep (map (ppU 10) vs)
evalTerm path (S term sel) = do v <- evalTerm CNil sel
evalTerm (CSel v path) term
evalTerm path (FV terms) = variants terms >>= evalTerm path
evalTerm path (EInt n) = return (EInt n)
evalTerm path t = ppbug ("evalTerm" <+> parens t)
--evalTerm path t = ppbug (text "evalTerm" <+> sep [parens (text (show path)),parens (text (show t))])
getVarIndex x = maybe err id $ getArgIndex x
where err = bug ("getVarIndex "++show x)
----------------------------------------------------------------------
-- GrammarEnv
data PMCFGEnv = PMCFGEnv !ProdSet !FunSet
type ProdSet = Set.Set Production
type FunSet = Map.Map (UArray LIndex SeqId) FunId
emptyPMCFGEnv =
PMCFGEnv Set.empty Map.empty
addFunction :: PMCFGEnv -> FId -> UArray LIndex SeqId -> [[FId]] -> PMCFGEnv
addFunction (PMCFGEnv prodSet funSet) !fid fun args =
case Map.lookup fun funSet of
Just !funid -> PMCFGEnv (Set.insert (Production fid funid args) prodSet)
funSet
Nothing -> let !funid = Map.size funSet
in PMCFGEnv (Set.insert (Production fid funid args) prodSet)
(Map.insert fun funid funSet)
getPMCFG :: PMCFGEnv -> PMCFG
getPMCFG (PMCFGEnv prodSet funSet) =
PMCFG (optimize prodSet) (mkSetArray funSet)
where
optimize ps = Map.foldrWithKey ff [] (Map.fromListWith (++) [((fid,funid),[args]) | (Production fid funid args) <- Set.toList ps])
where
ff :: (FId,FunId) -> [[[FId]]] -> [Production] -> [Production]
ff (fid,funid) xs prods
| product (map IntSet.size ys) == count
= (Production fid funid (map IntSet.toList ys)) : prods
| otherwise = map (Production fid funid) xs ++ prods
where
count = sum (map (product . map length) xs)
ys = foldl (zipWith (foldr IntSet.insert)) (repeat IntSet.empty) xs
------------------------------------------------------------
-- updating the MCF rule
restrictArg :: LIndex -> Path -> Term -> BacktrackM Env ()
restrictArg nr path index = do
(head, args) <- get
args <- updateNthM (restrictProtoFCat path index) nr args
put (head, args)
restrictHead :: Path -> Term -> BacktrackM Env ()
restrictHead path term = do
(head, args) <- get
head <- restrictProtoFCat path term head
put (head, args)
restrictProtoFCat :: (Functor m, MonadPlus m) => Path -> Term -> ProtoFCat -> m ProtoFCat
restrictProtoFCat path v (PFCat cat f schema) = do
schema <- addConstraint path v schema
return (PFCat cat f schema)
where
addConstraint (CProj lbl path) v (CRec rs) = fmap CRec $ update lbl (addConstraint path v) rs
addConstraint (CSel trm path) v (CTbl pt cs) = fmap (CTbl pt) $ update trm (addConstraint path v) cs
addConstraint CNil v (CPar (m,vs)) = case lookup v vs of
Just index -> return (CPar (m,[(v,index)]))
Nothing -> mzero
addConstraint CNil v (CStr _) = bug "restrictProtoFCat: string path"
update k0 f [] = return []
update k0 f (x@(k,Identity v):xs)
| k0 == k = do v <- f v
return ((k,Identity v):xs)
| otherwise = do xs <- update k0 f xs
return (x:xs)
mkArray lst = listArray (0,length lst-1) lst
mkSetArray map = array (0,Map.size map-1) [(v,k) | (k,v) <- Map.toList map]
bug msg = ppbug msg
ppbug msg = error completeMsg
where
originalMsg = render $ hang "Internal error in GeneratePMCFG:" 4 msg
completeMsg =
case render msg of -- the error message for pattern matching a runtime string
"descend (CStr 0,CNil,CProj (LIdent (Id {rawId2utf8 = \"s\"})) CNil)"
-> unlines [originalMsg -- add more helpful output
,""
,"1) Check that you are not trying to pattern match a /runtime string/."
," These are illegal:"
," lin Test foo = case foo.s of {"
," \"str\" => … } ; <- explicit matching argument of a lin"
," lin Test foo = opThatMatches foo <- calling an oper that pattern matches"
,""
,"2) Not about pattern matching? Submit a bug report and we update the error message."
," https://github.com/GrammaticalFramework/gf-core/issues"
]
_ -> originalMsg -- any other message: just print it as is
ppU = ppTerm Unqualified

View File

@@ -50,13 +50,20 @@ getSourceModule opts file0 =
Right (i,mi0) ->
do liftIO $ removeTemp tmp
let mi =mi0 {mflags=mflags mi0 `addOptions` opts, msrc=file0}
case renameEncoding `fmap` flag optEncoding (mflags mi0) of
Just coding' ->
when (coding/=coding') $
optCoding' = renameEncoding `fmap` flag optEncoding (mflags mi0)
case (optCoding,optCoding') of
{-
(Nothing,Nothing) ->
unless (BS.all isAscii raw) $
ePutStrLn $ file0++":\n Warning: default encoding has changed from Latin-1 to UTF-8"
-}
(_,Just coding') ->
when (coding/=coding') $
raise $ "Encoding mismatch: "++coding++" /= "++coding'
where coding = maybe defaultEncoding renameEncoding optCoding
_ -> return ()
return (i,mi)
--liftIO $ transcodeModule' (i,mi) -- old lexer
return (i,mi) -- new lexer
getBNFCRules :: Options -> FilePath -> IOE [BNFCRule]
getBNFCRules opts fpath = do

View File

@@ -6,38 +6,33 @@ module GF.Compile.GrammarToCanonical(
) where
import Data.List(nub,partition)
import qualified Data.Map as M
import Data.Maybe(fromMaybe)
import qualified Data.Set as S
import GF.Data.ErrM
import GF.Text.Pretty
import GF.Grammar.Grammar as G
import GF.Grammar.Grammar
import GF.Grammar.Lookup(lookupOrigInfo,allOrigInfos,allParamValues)
import GF.Grammar.Macros(typeForm,collectOp,collectPattOp,composSafeOp,mkAbs,mkApp,term2patt,sortRec)
import GF.Grammar.Macros(typeForm,collectOp,collectPattOp,mkAbs,mkApp,term2patt)
import GF.Grammar.Lockfield(isLockLabel)
import GF.Grammar.Predef(cPredef,cInts)
-- import GF.Compile.Compute.Value(Predefined(..))
import GF.Infra.Ident(ModuleName(..),Ident,ident2raw,rawIdentS,showIdent,isWildIdent)
import GF.Infra.Option(Options,optionsPGF)
import GF.Infra.CheckM
import PGF2(Literal(..))
import GF.Compile.Compute.Concrete(normalForm)
import GF.Compile.Compute.Predef(predef)
import GF.Compile.Compute.Value(Predefined(..))
import GF.Infra.Ident(ModuleName(..),Ident,prefixIdent,showIdent,isWildIdent)
import GF.Infra.Option(Options, optionsPGF)
import PGF.Internal(Literal(..))
import GF.Compile.Compute.ConcreteNew(normalForm,resourceValues)
import GF.Grammar.Canonical as C
import System.FilePath ((</>), (<.>))
import qualified Debug.Trace as T
import Debug.Trace
-- | Generate Canonical code for the named abstract syntax and all associated
-- concrete syntaxes
grammar2canonical :: Options -> ModuleName -> G.Grammar -> Check C.Grammar
grammar2canonical opts absname gr = do
abs <- abstract2canonical absname gr
cncs <- concretes2canonical opts absname gr
return (Grammar abs (map snd cncs))
grammar2canonical :: Options -> ModuleName -> SourceGrammar -> C.Grammar
grammar2canonical opts absname gr =
Grammar (abstract2canonical absname gr)
(map snd (concretes2canonical opts absname gr))
-- | Generate Canonical code for the named abstract syntax
abstract2canonical :: ModuleName -> G.Grammar -> Check Abstract
abstract2canonical absname gr =
return (Abstract (modId absname) (convFlags gr absname) cats funs)
Abstract (modId absname) (convFlags gr absname) cats funs
where
cats = [CatDef (gId c) (convCtx ctx) | ((_,c),AbsCat ctx) <- adefs]
@@ -50,7 +45,6 @@ abstract2canonical absname gr =
convHypo (bt,name,t) =
case typeForm t of
([],(_,cat),[]) -> gId cat -- !!
tf -> error ("abstract2canonical convHypo: " ++ show tf)
convType t =
case typeForm t of
@@ -61,26 +55,28 @@ abstract2canonical absname gr =
convHypo' (bt,name,t) = TypeBinding (gId name) (convType t)
-- | Generate Canonical code for the all concrete syntaxes associated with
-- the named abstract syntax in given the grammar.
concretes2canonical :: Options -> ModuleName -> G.Grammar -> Check [(FilePath, Concrete)]
concretes2canonical opts absname gr =
sequence
[fmap ((,) cncname) (concrete2canonical gr absname cnc cncmod)
| cnc<-allConcretes gr absname,
let cncname = "canonical" </> render cnc <.> "gf"
Ok cncmod = lookupModule gr cnc
]
[(cncname,concrete2canonical gr cenv absname cnc cncmod)
| let cenv = resourceValues opts gr,
cnc<-allConcretes gr absname,
let cncname = "canonical/"++render cnc ++ ".gf" :: FilePath
Ok cncmod = lookupModule gr cnc
]
-- | Generate Canonical GF for the given concrete module.
concrete2canonical :: G.Grammar -> ModuleName -> ModuleName -> ModuleInfo -> Check Concrete
concrete2canonical gr absname cnc modinfo = do
defs <- fmap concat $ mapM (toCanonical gr absname) (M.toList (jments modinfo))
return (Concrete (modId cnc) (modId absname) (convFlags gr cnc)
(neededParamTypes S.empty (params defs))
[lincat | (_,Left lincat) <- defs]
[lin | (_,Right lin) <- defs])
concrete2canonical gr cenv absname cnc modinfo =
Concrete (modId cnc) (modId absname) (convFlags gr cnc)
(neededParamTypes S.empty (params defs))
[lincat|(_,Left lincat)<-defs]
[lin|(_,Right lin)<-defs]
where
defs = concatMap (toCanonical gr absname cenv) .
M.toList $
jments modinfo
params = S.toList . S.unions . map fst
neededParamTypes have [] = []
@@ -90,30 +86,35 @@ concrete2canonical gr absname cnc modinfo = do
else let ((got,need),def) = paramType gr q
in def++neededParamTypes (S.union got have) (S.toList need++qs)
-- toCanonical :: G.Grammar -> ModuleName -> (Ident, Info) -> [(S.Set QIdent, Either LincatDef LinDef)]
toCanonical gr absname (name,jment) =
toCanonical gr absname cenv (name,jment) =
case jment of
CncCat (Just (L loc typ)) _ _ pprn _ -> do
ntyp <- normalForm gr typ
let pts = paramTypes gr ntyp
return [(pts,Left (LincatDef (gId name) (convType ntyp)))]
CncFun (Just r@(_,cat,ctx,lincat)) (Just (L loc def)) pprn _ -> do
let params = [(b,x)|(b,x,_)<-ctx]
args = map snd params
e0 <- normalForm gr (mkAbs params (mkApp def (map Vr args)))
let e = cleanupRecordFields lincat (unAbs (length params) e0)
tts = tableTypes gr [e]
return [(tts,Right (LinDef (gId name) (map gId args) (convert gr e)))]
CncCat (Just (L loc typ)) _ _ pprn _ ->
[(pts,Left (LincatDef (gId name) (convType ntyp)))]
where
pts = paramTypes gr ntyp
ntyp = nf loc typ
CncFun (Just r@(cat,ctx,lincat)) (Just (L loc def)) pprn _ ->
[(tts,Right (LinDef (gId name) (map gId args) (convert gr e')))]
where
tts = tableTypes gr [e']
e' = unAbs (length params) $
nf loc (mkAbs params (mkApp def (map Vr args)))
params = [(b,x)|(b,x,_)<-ctx]
args = map snd params
AnyInd _ m -> case lookupOrigInfo gr (m,name) of
Ok (m,jment) -> toCanonical gr absname (name,jment)
_ -> return []
_ -> return []
Ok (m,jment) -> toCanonical gr absname cenv (name,jment)
_ -> []
_ -> []
where
nf loc = normalForm cenv (L loc name)
-- aId n = prefixIdent "A." (gId n)
unAbs 0 t = t
unAbs n (Abs _ _ t) = unAbs (n-1) t
unAbs _ t = t
tableTypes :: G.Grammar -> [Term] -> S.Set QIdent
tableTypes gr ts = S.unions (map tabtys ts)
where
tabtys t =
@@ -122,7 +123,6 @@ tableTypes gr ts = S.unions (map tabtys ts)
T (TTyped t) cs -> S.union (paramTypes gr t) (tableTypes gr (map snd cs))
_ -> collectOp tabtys t
paramTypes :: G.Grammar -> G.Type -> S.Set QIdent
paramTypes gr t =
case t of
RecType fs -> S.unions (map (paramTypes gr.snd) fs)
@@ -141,26 +141,11 @@ paramTypes gr t =
Ok (_,ResParam {}) -> S.singleton q
_ -> ignore
ignore = T.trace ("Ignore: " ++ show t) S.empty
ignore = trace ("Ignore: "++show t) S.empty
-- | Filter out record fields from definitions which don't appear in lincat.
cleanupRecordFields :: G.Type -> Term -> Term
cleanupRecordFields (RecType ls) (R as) =
let defnFields = M.fromList ls
in R
[ (lbl, (mty, t'))
| (lbl, (mty, t)) <- as
, M.member lbl defnFields
, let Just ty = M.lookup lbl defnFields
, let t' = cleanupRecordFields ty t
]
cleanupRecordFields ty t@(FV _) = composSafeOp (cleanupRecordFields ty) t
cleanupRecordFields _ t = t
convert :: G.Grammar -> Term -> LinValue
convert gr = convert' gr []
convert' :: G.Grammar -> [Ident] -> Term -> LinValue
convert' gr vs = ppT
where
ppT0 = convert' gr vs
@@ -178,24 +163,24 @@ convert' gr vs = ppT
S t p -> selection (ppT t) (ppT p)
C t1 t2 -> concatValue (ppT t1) (ppT t2)
App f a -> ap (ppT f) (ppT a)
R r -> RecordValue (fields (sortRec r))
R r -> RecordValue (fields r)
P t l -> projection (ppT t) (lblId l)
Vr x -> VarValue (gId x)
Cn x -> VarValue (gId x) -- hmm
Con c -> ParamConstant (Param (gId c) [])
Sort k -> VarValue (gId k)
EInt n -> LiteralValue (LInt n)
Q (m,n) -> if m==cPredef then ppPredef n else VarValue (gQId m n)
QC (m,n) -> ParamConstant (Param (gQId m n) [])
K s -> LiteralValue (LStr s)
Empty -> LiteralValue (LStr "")
EInt n -> LiteralValue (IntConstant n)
Q (m,n) -> if m==cPredef then ppPredef n else VarValue ((gQId m n))
QC (m,n) -> ParamConstant (Param ((gQId m n)) [])
K s -> LiteralValue (StrConstant s)
Empty -> LiteralValue (StrConstant "")
FV ts -> VariantValue (map ppT ts)
Alts t' vs -> alts vs (ppT t')
_ -> error $ "convert' ppT: " ++ show t
_ -> error $ "convert' "++show t
ppCase (p,t) = TableRow (ppP p) (ppTv (patVars p++vs) t)
ppPredef n = error "TODO: ppPredef" {-
ppPredef n =
case predef n of
Ok BIND -> p "BIND"
Ok SOFT_BIND -> p "SOFT_BIND"
@@ -204,12 +189,12 @@ convert' gr vs = ppT
Ok ALL_CAPIT -> p "ALL_CAPIT"
_ -> VarValue (gQId cPredef n) -- hmm
where
p = PredefValue . PredefId . rawIdentS
-}
p = PredefValue . PredefId
ppP p =
case p of
PC c ps -> ParamPattern (Param (gId c) (map ppP ps))
PP (m,c) ps -> ParamPattern (Param (gQId m c) (map ppP ps))
PP (m,c) ps -> ParamPattern (Param ((gQId m c)) (map ppP ps))
PR r -> RecordPattern (fields r) {-
PW -> WildPattern
PV x -> VarP x
@@ -218,7 +203,6 @@ convert' gr vs = ppT
PFloat x -> Lit (show x)
PT _ p -> ppP p
PAs x p -> AsP x (ppP p) -}
_ -> error $ "convert' ppP: " ++ show p
where
fields = map field . filter (not.isLockLabel.fst)
field (l,p) = RecordRow (lblId l) (ppP p)
@@ -234,13 +218,13 @@ convert' gr vs = ppT
pre (K s) = [s]
pre Empty = [""] -- Empty == K ""
pre (Strs ts) = concatMap pre ts
pre (EPatt _ _ p) = pat p
pre t = error $ "convert' alts pre: " ++ show t
pre (EPatt p) = pat p
pre t = error $ "pre "++show t
pat (PString s) = [s]
pat (PAlt p1 p2) = pat p1++pat p2
pat (PSeq _ _ p1 _ _ p2) = [s1++s2 | s1<-pat p1, s2<-pat p2]
pat p = error $ "convert' alts pat: "++show p
pat (PSeq p1 p2) = [s1++s2 | s1<-pat p1, s2<-pat p2]
pat p = error $ "pat "++show p
fields = map field . filter (not.isLockLabel.fst)
field (l,(_,t)) = RecordRow (lblId l) (ppT t)
@@ -253,32 +237,28 @@ convert' gr vs = ppT
ParamConstant (Param p (ps++[a]))
_ -> error $ "convert' ap: "++render (ppA f <+> ppA a)
concatValue :: LinValue -> LinValue -> LinValue
concatValue v1 v2 =
case (v1,v2) of
(LiteralValue (LStr ""),_) -> v2
(_,LiteralValue (LStr "")) -> v1
(LiteralValue (StrConstant ""),_) -> v2
(_,LiteralValue (StrConstant "")) -> v1
_ -> ConcatValue v1 v2
-- | Smart constructor for projections
projection :: LinValue -> LabelId -> LinValue
projection r l = fromMaybe (Projection r l) (proj r l)
projection r l = maybe (Projection r l) id (proj r l)
proj :: LinValue -> LabelId -> Maybe LinValue
proj r l =
case r of
RecordValue r -> case [v | RecordRow l' v <- r, l'==l] of
RecordValue r -> case [v|RecordRow l' v<-r,l'==l] of
[v] -> Just v
_ -> Nothing
_ -> Nothing
-- | Smart constructor for selections
selection :: LinValue -> LinValue -> LinValue
selection t v =
-- Note: impossible cases can become possible after grammar transformation
case t of
TableValue tt r ->
case nub [rv | TableRow _ rv <- keep] of
case nub [rv|TableRow _ rv<-keep] of
[rv] -> rv
_ -> Selection (TableValue tt r') v
where
@@ -297,16 +277,13 @@ selection t v =
(keep,discard) = partition (mightMatchRow v) r
_ -> Selection t v
impossible :: LinValue -> LinValue
impossible = CommentedValue "impossible"
mightMatchRow :: LinValue -> TableRow rhs -> Bool
mightMatchRow v (TableRow p _) =
case p of
WildPattern -> True
_ -> mightMatch v p
mightMatch :: LinValue -> LinPattern -> Bool
mightMatch v p =
case v of
ConcatValue _ _ -> False
@@ -318,18 +295,16 @@ mightMatch v p =
RecordValue rv ->
case p of
RecordPattern rp ->
and [maybe False (`mightMatch` p) (proj v l) | RecordRow l p<-rp]
and [maybe False (flip mightMatch p) (proj v l) | RecordRow l p<-rp]
_ -> False
_ -> True
patVars :: Patt -> [Ident]
patVars p =
case p of
PV x -> [x]
PAs x p -> x:patVars p
_ -> collectPattOp patVars p
convType :: Term -> LinType
convType = ppT
where
ppT t =
@@ -341,9 +316,9 @@ convType = ppT
Sort k -> convSort k
-- EInt n -> tcon0 (identS ("({-"++show n++"-})")) -- type level numeric literal
FV (t:ts) -> ppT t -- !!
QC (m,n) -> ParamType (ParamTypeId (gQId m n))
Q (m,n) -> ParamType (ParamTypeId (gQId m n))
_ -> error $ "convType ppT: " ++ show t
QC (m,n) -> ParamType (ParamTypeId ((gQId m n)))
Q (m,n) -> ParamType (ParamTypeId ((gQId m n)))
_ -> error $ "Missing case in convType for: "++show t
convFields = map convField . filter (not.isLockLabel.fst)
convField (l,r) = RecordRow (lblId l) (ppT r)
@@ -352,20 +327,15 @@ convType = ppT
"Float" -> FloatType
"Int" -> IntType
"Str" -> StrType
_ -> error $ "convType convSort: " ++ show k
_ -> error ("convSort "++show k)
toParamType :: Term -> ParamType
toParamType t = case convType t of
ParamType pt -> pt
_ -> error $ "toParamType: " ++ show t
_ -> error ("toParamType "++show t)
toParamId :: Term -> ParamId
toParamId t = case toParamType t of
ParamTypeId p -> p
paramType :: G.Grammar
-> (ModuleName, Ident)
-> ((S.Set (ModuleName, Ident), S.Set QIdent), [ParamDef])
paramType gr q@(_,n) =
case lookupOrigInfo gr q of
Ok (m,ResParam (Just (L _ ps)) _)
@@ -373,7 +343,7 @@ paramType gr q@(_,n) =
((S.singleton (m,n),argTypes ps),
[ParamDef name (map (param m) ps)]
)
where name = gQId m n
where name = (gQId m n)
Ok (m,ResOper _ (Just (L _ t)))
| m==cPredef && n==cInts ->
((S.empty,S.empty),[]) {-
@@ -381,44 +351,40 @@ paramType gr q@(_,n) =
[Type (ConAp ((gQId m n)) [identS "n"]) (TId (identS "Int"))])-}
| otherwise ->
((S.singleton (m,n),paramTypes gr t),
[ParamAliasDef (gQId m n) (convType t)])
[ParamAliasDef ((gQId m n)) (convType t)])
_ -> ((S.empty,S.empty),[])
where
param m (n,ctx) = Param (gQId m n) [toParamId t|(_,_,t)<-ctx]
param m (n,ctx) = Param ((gQId m n)) [toParamId t|(_,_,t)<-ctx]
argTypes = S.unions . map argTypes1
argTypes1 (n,ctx) = S.unions [paramTypes gr t|(_,_,t)<-ctx]
lblId :: Label -> C.LabelId
lblId (LIdent ri) = LabelId ri
lblId (LVar i) = LabelId (rawIdentS (show i)) -- hmm
lblId = LabelId . render -- hmm
modId (MN m) = ModId (showIdent m)
modId :: ModuleName -> C.ModId
modId (MN m) = ModId (ident2raw m)
class FromIdent i where
gId :: Ident -> i
class FromIdent i where gId :: Ident -> i
instance FromIdent VarId where
gId i = if isWildIdent i then Anonymous else VarId (ident2raw i)
gId i = if isWildIdent i then Anonymous else VarId (showIdent i)
instance FromIdent C.FunId where gId = C.FunId . ident2raw
instance FromIdent CatId where gId = CatId . ident2raw
instance FromIdent C.FunId where gId = C.FunId . showIdent
instance FromIdent CatId where gId = CatId . showIdent
instance FromIdent ParamId where gId = ParamId . unqual
instance FromIdent VarValueId where gId = VarValueId . unqual
class FromIdent i => QualIdent i where
gQId :: ModuleName -> Ident -> i
class FromIdent i => QualIdent i where gQId :: ModuleName -> Ident -> i
instance QualIdent ParamId where gQId m n = ParamId (qual m n)
instance QualIdent ParamId where gQId m n = ParamId (qual m n)
instance QualIdent VarValueId where gQId m n = VarValueId (qual m n)
qual :: ModuleName -> Ident -> QualId
qual m n = Qual (modId m) (ident2raw n)
qual m n = Qual (modId m) (showIdent n)
unqual n = Unqual (showIdent n)
unqual :: Ident -> QualId
unqual n = Unqual (ident2raw n)
convFlags :: G.Grammar -> ModuleName -> Flags
convFlags gr mn =
Flags [(rawIdentS n,v) |
Flags [(n,convLit v) |
(n,v)<-err (const []) (optionsPGF.mflags) (lookupModule gr mn)]
where
convLit l =
case l of
LStr s -> Str s
LInt i -> C.Int i
LFlt d -> Flt d

View File

@@ -0,0 +1,447 @@
module GF.Compile.GrammarToLPGF (mkCanon2lpgf) where
import LPGF (LPGF (..))
import qualified LPGF as L
import PGF.CId
import GF.Grammar.Grammar
import qualified GF.Grammar.Canonical as C
import GF.Compile.GrammarToCanonical (grammar2canonical)
import GF.Data.Operations (ErrorMonad (..))
import qualified GF.Data.IntMapBuilder as IntMapBuilder
import GF.Infra.Option (Options)
import GF.Infra.UseIO (IOE)
import GF.Text.Pretty (pp, render)
import Control.Applicative ((<|>))
import Control.Monad (when, unless, forM, forM_)
import qualified Control.Monad.State as CMS
import Data.Either (lefts, rights)
import qualified Data.IntMap as IntMap
import Data.List (elemIndex)
import qualified Data.List as L
import qualified Data.Map.Strict as Map
import Data.Maybe (fromJust, isJust)
import System.Environment (lookupEnv)
import System.FilePath ((</>), (<.>))
import Text.Printf (printf)
import qualified Debug.Trace
trace x = Debug.Trace.trace ("> " ++ show x) (return ())
mkCanon2lpgf :: Options -> SourceGrammar -> ModuleName -> IOE LPGF
mkCanon2lpgf opts gr am = do
debug <- isJust <$> lookupEnv "DEBUG"
when debug $ do
ppCanonical debugDir canon
dumpCanonical debugDir canon
(an,abs) <- mkAbstract ab
cncs <- mapM (mkConcrete debug) cncs
let lpgf = LPGF {
L.absname = an,
L.abstract = abs,
L.concretes = Map.fromList cncs
}
when debug $ ppLPGF debugDir lpgf
return lpgf
where
canon@(C.Grammar ab cncs) = grammar2canonical opts am gr
mkAbstract :: (ErrorMonad err) => C.Abstract -> err (CId, L.Abstract)
mkAbstract (C.Abstract modId flags cats funs) = return (mdi2i modId, L.Abstract {})
mkConcrete :: (ErrorMonad err) => Bool -> C.Concrete -> err (CId, L.Concrete)
mkConcrete debug (C.Concrete modId absModId flags params' lincats lindefs) = do
let
(C.Abstract _ _ _ funs) = ab
params = inlineParamAliases params'
-- Builds maps for lookups
paramValueMap :: Map.Map C.ParamId C.ParamDef -- constructor -> definition
paramValueMap = Map.fromList [ (v,d) | d@(C.ParamDef _ vs) <- params, (C.Param v _) <- vs ]
lincatMap :: Map.Map C.CatId C.LincatDef
lincatMap = Map.fromList [ (cid,d) | d@(C.LincatDef cid _) <- lincats ]
funMap :: Map.Map C.FunId C.FunDef
funMap = Map.fromList [ (fid,d) | d@(C.FunDef fid _) <- funs ]
-- | Lookup paramdef, providing dummy fallback when not found
-- Workaround for https://github.com/GrammaticalFramework/gf-core/issues/100
lookupParamDef :: C.ParamId -> Either String C.ParamDef
lookupParamDef pid = case Map.lookup pid paramValueMap of
Just d -> Right d
Nothing ->
-- Left $ printf "Cannot find param definition: %s" (show pid)
Right $ C.ParamDef (C.ParamId (C.Unqual "DUMMY")) [C.Param pid []]
-- | Lookup lintype for a function
lookupLinType :: C.FunId -> Either String C.LinType
lookupLinType funId = do
fun <- m2e (printf "Cannot find type for: %s" (show funId)) (Map.lookup funId funMap)
let (C.FunDef _ (C.Type _ (C.TypeApp catId _))) = fun
lincat <- m2e (printf "Cannot find lincat for: %s" (show catId)) (Map.lookup catId lincatMap)
let (C.LincatDef _ lt) = lincat
return lt
-- | Lookup lintype for a function's argument
lookupLinTypeArg :: C.FunId -> Int -> Either String C.LinType
lookupLinTypeArg funId argIx = do
fun <- m2e (printf "Cannot find type for: %s" (show funId)) (Map.lookup funId funMap)
let (C.FunDef _ (C.Type args _)) = fun
let (C.TypeBinding _ (C.Type _ (C.TypeApp catId _))) = args !! argIx
lincat <- m2e (printf "Cannot find lincat for: %s" (show catId)) (Map.lookup catId lincatMap)
let (C.LincatDef _ lt) = lincat
return lt
-- Filter out record fields from definitions which don't appear in lincat.
-- Workaround for https://github.com/GrammaticalFramework/gf-core/issues/101
cleanupRecordFields :: C.LinValue -> C.LinType -> C.LinValue
cleanupRecordFields (C.RecordValue rrvs) (C.RecordType rrs) =
let defnFields = Map.fromList [ (lid, lt) | (C.RecordRow lid lt) <- rrs ]
in C.RecordValue
[ C.RecordRow lid lv'
| C.RecordRow lid lv <- rrvs
, Map.member lid defnFields
, let Just lt = Map.lookup lid defnFields
, let lv' = cleanupRecordFields lv lt
]
cleanupRecordFields lv _ = lv
lindefs' =
[ C.LinDef funId varIds linValue'
| (C.LinDef funId varIds linValue) <- lindefs
, let Right linType = lookupLinType funId
, let linValue' = cleanupRecordFields linValue linType
]
es = map mkLin lindefs'
lins = Map.fromList $ rights es
-- | Main code generation function
mkLin :: C.LinDef -> Either String (CId, L.LinFun)
mkLin (C.LinDef funId varIds linValue) = do
-- when debug $ trace funId
(lf, _) <- val2lin linValue
return (fi2i funId, lf)
where
val2lin :: C.LinValue -> Either String (L.LinFun, Maybe C.LinType)
val2lin lv = case lv of
C.ConcatValue v1 v2 -> do
(v1',t1) <- val2lin v1
(v2',t2) <- val2lin v2
return (L.Concat v1' v2', t1 <|> t2) -- t1 else t2
C.LiteralValue ll -> case ll of
C.FloatConstant f -> return (L.Token $ show f, Just C.FloatType)
C.IntConstant i -> return (L.Token $ show i, Just C.IntType)
C.StrConstant s -> return (L.Token s, Just C.StrType)
C.ErrorValue err -> return (L.Error err, Nothing)
C.ParamConstant (C.Param pid lvs) -> do
let
collectProjections :: C.LinValue -> Either String [L.LinFun]
collectProjections (C.ParamConstant (C.Param pid lvs)) = do
def <- lookupParamDef pid
let (C.ParamDef tpid defpids) = def
pidIx <- eitherElemIndex pid [ p | C.Param p _ <- defpids ]
rest <- mapM collectProjections lvs
return $ L.Ix (pidIx+1) : concat rest
collectProjections lv = do
(lf,_) <- val2lin lv
return [lf]
lfs <- collectProjections lv
let term = L.Tuple lfs
def <- lookupParamDef pid
let (C.ParamDef tpid _) = def
return (term, Just $ C.ParamType (C.ParamTypeId tpid))
C.PredefValue (C.PredefId pid) -> case pid of
"BIND" -> return (L.Bind, Nothing)
"SOFT_BIND" -> return (L.Bind, Nothing)
"SOFT_SPACE" -> return (L.Space, Nothing)
"CAPIT" -> return (L.Capit, Nothing)
"ALL_CAPIT" -> return (L.AllCapit, Nothing)
_ -> Left $ printf "Unknown predef function: %s" pid
C.RecordValue rrvs -> do
let rrvs' = sortRecordRows rrvs
ts <- sequence [ val2lin lv | C.RecordRow lid lv <- rrvs' ]
return (L.Tuple (map fst ts), Just $ C.RecordType [ C.RecordRow lid lt | (C.RecordRow lid _, (_, Just lt)) <- zip rrvs' ts])
C.TableValue lt trvs -> do
-- group the rows by "left-most" value
let
groupRow :: C.TableRowValue -> C.TableRowValue -> Bool
groupRow (C.TableRow p1 _) (C.TableRow p2 _) = groupPattern p1 p2
groupPattern :: C.LinPattern -> C.LinPattern -> Bool
groupPattern p1 p2 = case (p1,p2) of
(C.ParamPattern (C.Param pid1 _), C.ParamPattern (C.Param pid2 _)) -> pid1 == pid2 -- compare only constructors
(C.RecordPattern (C.RecordRow lid1 patt1:_), C.RecordPattern (C.RecordRow lid2 patt2:_)) -> groupPattern patt1 patt2 -- lid1 == lid2 necessarily
_ -> error $ printf "Mismatched patterns in grouping:\n%s\n%s" (show p1) (show p2)
grps :: [[C.TableRowValue]]
grps = L.groupBy groupRow trvs
-- remove one level of depth and recurse
let
handleGroup :: [C.TableRowValue] -> Either String (L.LinFun, Maybe C.LinType)
handleGroup [C.TableRow patt lv] =
case reducePattern patt of
Just patt' -> do
(lf,lt) <- handleGroup [C.TableRow patt' lv]
return (L.Tuple [lf],lt)
Nothing -> val2lin lv
handleGroup rows = do
let rows' = map reduceRow rows
val2lin (C.TableValue lt rows') -- lt is wrong here, but is unused
reducePattern :: C.LinPattern -> Maybe C.LinPattern
reducePattern patt =
case patt of
C.ParamPattern (C.Param _ []) -> Nothing
C.ParamPattern (C.Param _ patts) -> Just $ C.ParamPattern (C.Param pid' patts')
where
C.ParamPattern (C.Param pid1 patts1) = head patts
pid' = pid1
patts' = patts1 ++ tail patts
C.RecordPattern [] -> Nothing
C.RecordPattern (C.RecordRow lid patt:rrs) ->
case reducePattern patt of
Just patt' -> Just $ C.RecordPattern (C.RecordRow lid patt':rrs)
Nothing -> if null rrs then Nothing else Just $ C.RecordPattern rrs
_ -> error $ printf "Unhandled pattern in reducing: %s" (show patt)
reduceRow :: C.TableRowValue -> C.TableRowValue
reduceRow (C.TableRow patt lv) =
let Just patt' = reducePattern patt
in C.TableRow patt' lv
-- ts :: [(L.LinFun, Maybe C.LinType)]
ts <- mapM handleGroup grps
-- return
let typ = case ts of
(_, Just tst):_ -> Just $ C.TableType lt tst
_ -> Nothing
return (L.Tuple (map fst ts), typ)
-- TODO TuplePattern, WildPattern?
C.TupleValue lvs -> do
ts <- mapM val2lin lvs
return (L.Tuple (map fst ts), Just $ C.TupleType (map (fromJust.snd) ts))
C.VariantValue [] -> return (L.Empty, Nothing) -- TODO Just C.StrType ?
C.VariantValue (vr:_) -> val2lin vr -- NOTE variants not supported, just pick first
C.VarValue (C.VarValueId (C.Unqual v)) -> do
ix <- eitherElemIndex (C.VarId v) varIds
lt <- lookupLinTypeArg funId ix
return (L.Argument (ix+1), Just lt)
C.PreValue pts df -> do
pts' <- forM pts $ \(pfxs, lv) -> do
(lv', _) <- val2lin lv
return (pfxs, lv')
(df', lt) <- val2lin df
return (L.Pre pts' df', lt)
C.Projection v1 lblId -> do
(v1', mtyp) <- val2lin v1
-- find label index in argument type
let Just (C.RecordType rrs) = mtyp
let rrs' = [ lid | C.RecordRow lid _ <- rrs ]
-- lblIx <- eitherElemIndex lblId rrs'
let
lblIx = case eitherElemIndex lblId rrs' of
Right x -> x
Left _ -> 0 -- corresponds to Prelude.False
-- lookup lintype for record row
let C.RecordRow _ lt = rrs !! lblIx
return (L.Projection v1' (L.Ix (lblIx+1)), Just lt)
C.Selection v1 v2 -> do
(v1', t1) <- val2lin v1
(v2', t2) <- val2lin v2
let Just (C.TableType t11 t12) = t1 -- t11 == t2
return (L.Projection v1' v2', Just t12)
-- C.CommentedValue cmnt lv -> val2lin lv
C.CommentedValue cmnt lv -> case cmnt of
"impossible" -> val2lin lv >>= \(_, typ) -> return (L.Empty, typ)
_ -> val2lin lv
v -> Left $ printf "val2lin not implemented for: %s" (show v)
unless (null $ lefts es) (raise $ unlines (lefts es))
let maybeOptimise = if debug then id else extractStrings
let concr = maybeOptimise $ L.Concrete {
L.toks = IntMap.empty,
L.lins = lins
}
return (mdi2i modId, concr)
-- | Remove ParamAliasDefs by inlining their definitions
inlineParamAliases :: [C.ParamDef] -> [C.ParamDef]
inlineParamAliases defs = if null aliases then defs else map rp' pdefs
where
(aliases,pdefs) = L.partition isParamAliasDef defs
rp' :: C.ParamDef -> C.ParamDef
rp' (C.ParamDef pid pids) = C.ParamDef pid (map rp'' pids)
rp' (C.ParamAliasDef _ _) = error "inlineParamAliases called on ParamAliasDef" -- impossible
rp'' :: C.ParamValueDef -> C.ParamValueDef
rp'' (C.Param pid pids) = C.Param pid (map rp''' pids)
rp''' :: C.ParamId -> C.ParamId
rp''' pid = case L.find (\(C.ParamAliasDef p _) -> p == pid) aliases of
Just (C.ParamAliasDef _ (C.ParamType (C.ParamTypeId p))) -> p
_ -> pid
-- | Always put 's' reocord field first, then sort alphabetically.
-- Workaround for https://github.com/GrammaticalFramework/gf-core/issues/102
-- Based on GF.Granmar.Macros.sortRec
sortRecordRows :: [C.RecordRowValue] -> [C.RecordRowValue]
sortRecordRows = L.sortBy ordLabel
where
ordLabel (C.RecordRow (C.LabelId l1) _) (C.RecordRow (C.LabelId l2) _) =
case (l1,l2) of
("s",_) -> LT
(_,"s") -> GT
(s1,s2) -> compare s1 s2
-- sortRecord :: C.LinValue -> C.LinValue
-- sortRecord (C.RecordValue rrvs) = C.RecordValue (sortRecordRows rrvs)
-- sortRecord lv = lv
isParamAliasDef :: C.ParamDef -> Bool
isParamAliasDef (C.ParamAliasDef _ _) = True
isParamAliasDef _ = False
isParamType :: C.LinType -> Bool
isParamType (C.ParamType _) = True
isParamType _ = False
isRecordType :: C.LinType -> Bool
isRecordType (C.RecordType _) = True
isRecordType _ = False
-- | Find all token strings, put them in a map and replace with token indexes
extractStrings :: L.Concrete -> L.Concrete
extractStrings concr = L.Concrete { L.toks = toks', L.lins = lins' }
where
imb = IntMapBuilder.fromIntMap (L.toks concr)
(lins',imb') = CMS.runState (go0 (L.lins concr)) imb
toks' = IntMapBuilder.toIntMap imb'
go0 :: Map.Map CId L.LinFun -> CMS.State (IntMapBuilder.IMB String) (Map.Map CId L.LinFun)
go0 mp = do
xs <- mapM (\(cid,lin) -> go lin >>= \lin' -> return (cid,lin')) (Map.toList mp)
return $ Map.fromList xs
go :: L.LinFun -> CMS.State (IntMapBuilder.IMB String) L.LinFun
go lf = case lf of
L.Token str -> do
imb <- CMS.get
let (ix,imb') = IntMapBuilder.insert' str imb
CMS.put imb'
return $ L.TokenIx ix
L.Pre pts df -> do
-- pts' <- mapM (\(pfxs,lv) -> go lv >>= \lv' -> return (pfxs,lv')) pts
pts' <- forM pts $ \(pfxs,lv) -> do
imb <- CMS.get
let str = show pfxs
let (ix,imb') = IntMapBuilder.insert' str imb
CMS.put imb'
lv' <- go lv
return (ix,lv')
df' <- go df
return $ L.PreIx pts' df'
L.Concat s t -> do
s' <- go s
t' <- go t
return $ L.Concat s' t'
L.Tuple ts -> do
ts' <- mapM go ts
return $ L.Tuple ts'
L.Projection t u -> do
t' <- go t
u' <- go u
return $ L.Projection t' u'
t -> return t
-- | Convert Maybe to Either value with error
m2e :: String -> Maybe a -> Either String a
m2e err = maybe (Left err) Right
-- | Wrap elemIndex into Either value
eitherElemIndex :: (Eq a, Show a) => a -> [a] -> Either String Int
eitherElemIndex x xs = m2e (printf "Cannot find: %s in %s" (show x) (show xs)) (elemIndex x xs)
mdi2s :: C.ModId -> String
mdi2s (C.ModId i) = i
mdi2i :: C.ModId -> CId
mdi2i (C.ModId i) = mkCId i
fi2i :: C.FunId -> CId
fi2i (C.FunId i) = mkCId i
-- Debugging
debugDir :: FilePath
debugDir = "DEBUG"
-- | Pretty-print canonical grammars to file
ppCanonical :: FilePath -> C.Grammar -> IO ()
ppCanonical path (C.Grammar ab cncs) = do
let (C.Abstract modId flags cats funs) = ab
writeFile (path </> mdi2s modId <.> "canonical.gf") (render $ pp ab)
forM_ cncs $ \cnc@(C.Concrete modId absModId flags params lincats lindefs) ->
writeFile' (path </> mdi2s modId <.> "canonical.gf") (render $ pp cnc)
-- | Dump canonical grammars to file
dumpCanonical :: FilePath -> C.Grammar -> IO ()
dumpCanonical path (C.Grammar ab cncs) = do
let (C.Abstract modId flags cats funs) = ab
let body = unlines $ map show cats ++ [""] ++ map show funs
writeFile' (path </> mdi2s modId <.> "canonical.dump") body
forM_ cncs $ \(C.Concrete modId absModId flags params lincats lindefs) -> do
let body = unlines $ concat [
map show params,
[""],
map show lincats,
[""],
map show lindefs
]
writeFile' (path </> mdi2s modId <.> "canonical.dump") body
-- | Pretty-print LPGF to file
ppLPGF :: FilePath -> LPGF -> IO ()
ppLPGF path lpgf =
forM_ (Map.toList $ L.concretes lpgf) $ \(cid,concr) ->
writeFile' (path </> showCId cid <.> "lpgf.txt") (L.render $ L.pp concr)
-- | Dump LPGF to file
dumpLPGF :: FilePath -> LPGF -> IO ()
dumpLPGF path lpgf =
forM_ (Map.toList $ L.concretes lpgf) $ \(cid,concr) -> do
let body = unlines $ map show (Map.toList $ L.lins concr)
writeFile' (path </> showCId cid <.> "lpgf.dump") body
-- | Write a file and report it to console
writeFile' :: FilePath -> String -> IO ()
writeFile' p b = do
writeFile p b
putStrLn $ "Wrote " ++ p

View File

@@ -1,14 +1,17 @@
{-# LANGUAGE BangPatterns, FlexibleContexts, MagicHash #-}
module GF.Compile.GrammarToPGF (grammar2PGF) where
{-# LANGUAGE BangPatterns, FlexibleContexts #-}
module GF.Compile.GrammarToPGF (mkCanon2pgf) where
--import GF.Compile.Export
import GF.Compile.GeneratePMCFG
import GF.Compile.GenerateBC
import GF.Compile.OptimizePGF
import PGF2 hiding (mkType)
import PGF2.Transactions
import PGF(CId,mkCId,utf8CId)
import PGF.Internal(fidInt,fidFloat,fidString,fidVar)
import PGF.Internal(updateProductionIndices)
import qualified PGF.Internal as C
import qualified PGF.Internal as D
import GF.Grammar.Predef
import GF.Grammar.Grammar hiding (Production)
import GF.Grammar.Grammar
import qualified GF.Grammar.Lookup as Look
import qualified GF.Grammar as A
import qualified GF.Grammar.Macros as GM
@@ -19,162 +22,111 @@ import GF.Infra.UseIO (IOE)
import GF.Data.Operations
import Data.List
import Data.Char
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import Data.Array.IArray
import Data.Maybe(fromMaybe)
import System.FilePath
import System.Directory
import GHC.Prim
import GHC.Base(getTag)
grammar2PGF :: Options -> SourceGrammar -> ModuleName -> Map.Map PGF2.Fun Double -> IO PGF
grammar2PGF opts gr am probs = do
gr <- mkAbstr am probs
return gr {-do
cnc_infos <- getConcreteInfos gr am
return $
build (let gflags = if flag optSplitPGF opts
then [("split", LStr "true")]
else []
(an,abs) = mkAbstr am probs
cncs = map (mkConcr opts abs) cnc_infos
in newPGF gflags an abs cncs)-}
mkCanon2pgf :: Options -> SourceGrammar -> ModuleName -> IOE D.PGF
mkCanon2pgf opts gr am = do
(an,abs) <- mkAbstr am
cncs <- mapM mkConcr (allConcretes gr am)
return $ updateProductionIndices (D.PGF Map.empty an abs (Map.fromList cncs))
where
aflags = err (const noOptions) mflags (lookupModule gr am)
cenv = resourceValues opts gr
mkAbstr :: ModuleName -> Map.Map PGF2.Fun Double -> IO PGF
mkAbstr am probs = do
let abs_name = mi2i am
mb_ngf_path <-
if snd (flag optLinkTargets opts)
then do let fname = maybe id (</>)
(flag optOutputDir opts)
(fromMaybe abs_name (flag optName opts)<.>"ngf")
exists <- doesFileExist fname
if exists
then removeFile fname
else return ()
putStr ("(Boot image "++fname++") ")
return (Just fname)
else do return Nothing
gr <- newNGF abs_name mb_ngf_path
modifyPGF gr $ do
sequence_ [setAbstractFlag name value | (name,value) <- flags]
sequence_ [createCategory c ctxt p | (c,ctxt,p) <- cats]
sequence_ [createFunction f ty arity p | (f,ty,arity,_,p) <- funs]
mkAbstr am = return (mi2i am, D.Abstr flags funs cats)
where
aflags = err (const noOptions) mflags (lookupModule gr am)
adefs =
[((cPredefAbs,c), AbsCat (Just (L NoLoc []))) | c <- [cFloat,cInt,cString]] ++
Look.allOrigInfos gr am
flags = optionsPGF aflags
flags = Map.fromList [(mkCId f,x) | (f,x) <- optionsPGF aflags]
toLogProb = realToFrac . negate . log
cats = [(c', snd (mkContext [] cont), toLogProb (fromMaybe 0 (Map.lookup c' probs))) |
((m,c),AbsCat (Just (L _ cont))) <- adefs, let c' = i2i c]
funs = [(f', mkType [] ty, arity, bcode, toLogProb (fromMaybe 0 (Map.lookup f' funs_probs))) |
funs = Map.fromList [(i2i f, (mkType [] ty, arity, mkDef gr arity mdef, 0)) |
((m,f),AbsFun (Just (L _ ty)) ma mdef _) <- adefs,
let arity = mkArity ma mdef ty,
let bcode = mkDef gr arity mdef,
let f' = i2i f]
funs_probs = (Map.fromList . concat . Map.elems . fmap pad . Map.fromListWith (++))
[(i2i cat,[(i2i f,Map.lookup f' probs)]) | ((m,f),AbsFun (Just (L _ ty)) _ _ _) <- adefs,
let (_,(_,cat),_) = GM.typeForm ty,
let f' = i2i f]
where
pad :: [(a,Maybe Double)] -> [(a,Double)]
pad pfs = [(f,fromMaybe deflt mb_p) | (f,mb_p) <- pfs]
where
deflt = case length [f | (f,Nothing) <- pfs] of
0 -> 0
n -> max 0 ((1 - sum [d | (f,Just d) <- pfs]) / fromIntegral n)
{-
mkConcr opts abs (cm,ex_seqs,cdefs) =
let arity = mkArity ma mdef ty]
cats = Map.fromList [(i2i c, (snd (mkContext [] cont),catfuns c, 0)) |
((m,c),AbsCat (Just (L _ cont))) <- adefs]
catfuns cat =
[(0,i2i f) | ((m,f),AbsFun (Just (L _ ty)) _ _ (Just True)) <- adefs, snd (GM.valCat ty) == cat]
mkConcr cm = do
let cflags = err (const noOptions) mflags (lookupModule gr cm)
ciCmp | flag optCaseSensitive cflags = compare
| otherwise = compareCaseInsensitive
| otherwise = C.compareCaseInsensitve
flags = optionsPGF aflags
(ex_seqs,cdefs) <- addMissingPMCFGs
Map.empty
([((cPredefAbs,c), CncCat (Just (L NoLoc GM.defLinType)) Nothing Nothing Nothing Nothing) | c <- [cInt,cFloat,cString]] ++
Look.allOrigInfos gr cm)
seqs = (mkSetArray . Set.fromList . concat) $
(elems (ex_seqs :: Array SeqId [Symbol]) : [maybe [] elems (mseqs mi) | (m,mi) <- allExtends gr cm])
let flags = Map.fromList [(mkCId f,x) | (f,x) <- optionsPGF cflags]
seqs = (mkArray . C.sortNubBy ciCmp . concat) $
(Map.keys ex_seqs : [maybe [] elems (mseqs mi) | (m,mi) <- allExtends gr cm])
ex_seqs_arr = mkMapArray ex_seqs :: Array SeqId Sequence
!(!fid_cnt1,!cnccats) = genCncCats gr am cm cdefs
cnccat_ranges = Map.fromList (map (\(cid,s,e,_) -> (cid,(s,e))) cnccats)
!(!fid_cnt2,!productions,!lindefs,!linrefs,!cncfuns)
= genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt1 cnccat_ranges
= genCncFuns gr am cm ex_seqs_arr ciCmp seqs cdefs fid_cnt1 cnccats
printnames = genPrintNames cdefs
startCat = (fromMaybe "S" (flag optStartCat aflags))
(lindefs',linrefs',productions',cncfuns',sequences',cnccats') =
(if flag optOptimizePGF opts then optimizePGF startCat else id)
(lindefs,linrefs,productions,cncfuns,elems seqs,cnccats)
in (mi2i cm, newConcr abs
flags
printnames
lindefs'
linrefs'
productions'
cncfuns'
sequences'
cnccats'
fid_cnt2)
getConcreteInfos gr am = mapM flatten (allConcretes gr am)
return (mi2i cm, D.Concr flags
printnames
cncfuns
lindefs
linrefs
seqs
productions
IntMap.empty
Map.empty
cnccats
IntMap.empty
fid_cnt2)
where
flatten cm = do
(seqs,infos) <- addMissingPMCFGs cm Map.empty
(lit_infos ++ Look.allOrigInfos gr cm)
return (cm,mkMapArray seqs :: Array SeqId [Symbol],infos)
lit_infos = [((cPredefAbs,c), CncCat (Just (L NoLoc GM.defLinType)) Nothing Nothing Nothing Nothing) | c <- [cInt,cFloat,cString]]
-- if some module was compiled with -no-pmcfg, then
-- we have to create the PMCFG code just before linking
addMissingPMCFGs cm seqs [] = return (seqs,[])
addMissingPMCFGs cm seqs (((m,id), info):is) = do
(seqs,info) <- addPMCFG opts gr cenv Nothing am cm seqs id info
(seqs,infos) <- addMissingPMCFGs cm seqs is
return (seqs, ((m,id), info) : infos)
-}
i2i :: Ident -> String
i2i = showIdent
addMissingPMCFGs seqs [] = return (seqs,[])
addMissingPMCFGs seqs (((m,id), info):is) = do
(seqs,info) <- addPMCFG opts gr cenv Nothing am cm seqs id info
(seqs,is ) <- addMissingPMCFGs seqs is
return (seqs, ((m,id), info) : is)
mi2i :: ModuleName -> String
i2i :: Ident -> CId
i2i = utf8CId . ident2utf8
mi2i :: ModuleName -> CId
mi2i (MN i) = i2i i
mkType :: [Ident] -> A.Type -> PGF2.Type
mkType :: [Ident] -> A.Type -> C.Type
mkType scope t =
case GM.typeForm t of
(hyps,(_,cat),args) -> let (scope',hyps') = mkContext scope hyps
in DTyp hyps' (i2i cat) (map (mkExp scope') args)
in C.DTyp hyps' (i2i cat) (map (mkExp scope') args)
mkExp :: [Ident] -> A.Term -> Expr
mkExp scope t =
mkExp :: [Ident] -> A.Term -> C.Expr
mkExp scope t =
case t of
Q (_,c) -> EFun (i2i c)
QC (_,c) -> EFun (i2i c)
Q (_,c) -> C.EFun (i2i c)
QC (_,c) -> C.EFun (i2i c)
Vr x -> case lookup x (zip scope [0..]) of
Just i -> EVar i
Nothing -> EMeta 0
Abs b x t-> EAbs b (i2i x) (mkExp (x:scope) t)
App t1 t2-> EApp (mkExp scope t1) (mkExp scope t2)
EInt i -> ELit (LInt (fromIntegral i))
EFloat f -> ELit (LFlt f)
K s -> ELit (LStr s)
Meta i -> EMeta i
_ -> EMeta 0
{-
Just i -> C.EVar i
Nothing -> C.EMeta 0
Abs b x t-> C.EAbs b (i2i x) (mkExp (x:scope) t)
App t1 t2-> C.EApp (mkExp scope t1) (mkExp scope t2)
EInt i -> C.ELit (C.LInt (fromIntegral i))
EFloat f -> C.ELit (C.LFlt f)
K s -> C.ELit (C.LStr s)
Meta i -> C.EMeta i
_ -> C.EMeta 0
mkPatt scope p =
case p of
A.PP (_,c) ps->let (scope',ps') = mapAccumL mkPatt scope ps
@@ -189,65 +141,67 @@ mkPatt scope p =
A.PImplArg p-> let (scope',p') = mkPatt scope p
in (scope',C.PImplArg p')
A.PTilde t -> ( scope,C.PTilde (mkExp scope t))
-}
mkContext :: [Ident] -> A.Context -> ([Ident],[PGF2.Hypo])
mkContext :: [Ident] -> A.Context -> ([Ident],[C.Hypo])
mkContext scope hyps = mapAccumL (\scope (bt,x,ty) -> let ty' = mkType scope ty
in if x == identW
then ( scope,(bt,i2i x,ty'))
else (x:scope,(bt,i2i x,ty'))) scope hyps
mkDef gr arity (Just eqs) = generateByteCode gr arity eqs
mkDef gr arity Nothing = []
mkDef gr arity (Just eqs) = Just ([C.Equ ps' (mkExp scope' e) | L _ (ps,e) <- eqs, let (scope',ps') = mapAccumL mkPatt [] ps]
,generateByteCode gr arity eqs
)
mkDef gr arity Nothing = Nothing
mkArity (Just a) _ ty = a -- known arity, i.e. defined function
mkArity Nothing (Just _) ty = 0 -- defined function with no arity - must be an axiom
mkArity Nothing _ ty = let (ctxt, _, _) = GM.typeForm ty -- constructor
in length ctxt
{-
genCncCats gr am cm cdefs = mkCncCats 0 cdefs
genCncCats gr am cm cdefs =
let (index,cats) = mkCncCats 0 cdefs
in (index, Map.fromList cats)
where
mkCncCats index [] = (index,[])
mkCncCats index (((m,id),CncCat (Just (L _ lincat)) _ _ _ _):cdefs)
| id == cInt =
let cc = pgfCncCat gr (i2i id) lincat fidInt
let cc = pgfCncCat gr lincat fidInt
(index',cats) = mkCncCats index cdefs
in (index', cc : cats)
in (index', (i2i id,cc) : cats)
| id == cFloat =
let cc = pgfCncCat gr (i2i id) lincat fidFloat
let cc = pgfCncCat gr lincat fidFloat
(index',cats) = mkCncCats index cdefs
in (index', cc : cats)
in (index', (i2i id,cc) : cats)
| id == cString =
let cc = pgfCncCat gr (i2i id) lincat fidString
let cc = pgfCncCat gr lincat fidString
(index',cats) = mkCncCats index cdefs
in (index', cc : cats)
in (index', (i2i id,cc) : cats)
| otherwise =
let cc@(_, _s, e, _) = pgfCncCat gr (i2i id) lincat index
(index',cats) = mkCncCats (e+1) cdefs
in (index', cc : cats)
mkCncCats index (_ :cdefs) = mkCncCats index cdefs
let cc@(C.CncCat _s e _) = pgfCncCat gr lincat index
(index',cats) = mkCncCats (e+1) cdefs
in (index', (i2i id,cc) : cats)
mkCncCats index (_ :cdefs) = mkCncCats index cdefs
genCncFuns :: Grammar
-> ModuleName
-> ModuleName
-> Array SeqId [Symbol]
-> ([Symbol] -> [Symbol] -> Ordering)
-> Array SeqId [Symbol]
-> Array SeqId Sequence
-> (Sequence -> Sequence -> Ordering)
-> Array SeqId Sequence
-> [(QIdent, Info)]
-> FId
-> Map.Map PGF2.Cat (Int,Int)
-> Map.Map CId D.CncCat
-> (FId,
[(FId, [Production])],
[(FId, [FunId])],
[(FId, [FunId])],
[(PGF2.Fun,[SeqId])])
genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
IntMap.IntMap (Set.Set D.Production),
IntMap.IntMap [FunId],
IntMap.IntMap [FunId],
Array FunId D.CncFun)
genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccats =
let (fid_cnt1,funs_cnt1,funs1,lindefs,linrefs) = mkCncCats cdefs fid_cnt 0 [] IntMap.empty IntMap.empty
(fid_cnt2,funs_cnt2,funs2,prods0) = mkCncFuns cdefs fid_cnt1 funs_cnt1 funs1 lindefs Map.empty IntMap.empty
prods = [(fid,Set.toList prodSet) | (fid,prodSet) <- IntMap.toList prods0]
in (fid_cnt2,prods,IntMap.toList lindefs,IntMap.toList linrefs,reverse funs2)
(fid_cnt2,funs_cnt2,funs2,prods) = mkCncFuns cdefs fid_cnt1 funs_cnt1 funs1 lindefs Map.empty IntMap.empty
in (fid_cnt2,prods,lindefs,linrefs,array (0,funs_cnt2-1) funs2)
where
mkCncCats [] fid_cnt funs_cnt funs lindefs linrefs =
mkCncCats [] fid_cnt funs_cnt funs lindefs linrefs =
(fid_cnt,funs_cnt,funs,lindefs,linrefs)
mkCncCats (((m,id),CncCat _ _ _ _ (Just (PMCFG prods0 funs0))):cdefs) fid_cnt funs_cnt funs lindefs linrefs =
let !funs_cnt' = let (s_funid, e_funid) = bounds funs0
@@ -256,16 +210,17 @@ genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
linrefs' = foldl' (toLinRef (am,id) funs_cnt) linrefs prods0
funs' = foldl' (toCncFun funs_cnt (m,mkLinDefId id)) funs (assocs funs0)
in mkCncCats cdefs fid_cnt funs_cnt' funs' lindefs' linrefs'
mkCncCats (_ :cdefs) fid_cnt funs_cnt funs lindefs linrefs =
mkCncCats (_ :cdefs) fid_cnt funs_cnt funs lindefs linrefs =
mkCncCats cdefs fid_cnt funs_cnt funs lindefs linrefs
mkCncFuns [] fid_cnt funs_cnt funs lindefs crc prods =
(fid_cnt,funs_cnt,funs,prods)
mkCncFuns (((m,id),CncFun _ _ _ (Just (PMCFG prods0 funs0))):cdefs) fid_cnt funs_cnt funs lindefs crc prods =
let ty_C = err error (\x -> x) $ fmap GM.typeForm (Look.lookupFunType gr am id)
let ---Ok ty_C = fmap GM.typeForm (Look.lookupFunType gr am id)
ty_C = err error (\x -> x) $ fmap GM.typeForm (Look.lookupFunType gr am id)
!funs_cnt' = let (s_funid, e_funid) = bounds funs0
in funs_cnt+(e_funid-s_funid+1)
!(fid_cnt',crc',prods')
!(fid_cnt',crc',prods')
= foldl' (toProd lindefs ty_C funs_cnt)
(fid_cnt,crc,prods) prods0
funs' = foldl' (toCncFun funs_cnt (m,id)) funs (assocs funs0)
@@ -273,23 +228,23 @@ genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
mkCncFuns (_ :cdefs) fid_cnt funs_cnt funs lindefs crc prods =
mkCncFuns cdefs fid_cnt funs_cnt funs lindefs crc prods
toProd lindefs (ctxt_C,res_C,_) offs st (A.Production fid0 funid0 args0) =
let !((fid_cnt,crc,prods),args) = mapAccumL mkArg st (zip ctxt_C args0)
set0 = Set.fromList (map (PApply (offs+funid0)) (sequence args))
toProd lindefs (ctxt_C,res_C,_) offs st (Production fid0 funid0 args0) =
let !((fid_cnt,crc,prods),args) = mapAccumL mkArg st (zip ctxt_C args0)
set0 = Set.fromList (map (C.PApply (offs+funid0)) (sequence args))
fid = mkFId res_C fid0
!prods' = case IntMap.lookup fid prods of
Just set -> IntMap.insert fid (Set.union set0 set) prods
Nothing -> IntMap.insert fid set0 prods
in (fid_cnt,crc,prods')
where
mkArg st@(fid_cnt,crc,prods) ((_,_,ty),fid0s) =
mkArg st@(fid_cnt,crc,prods) ((_,_,ty),fid0s ) =
case fid0s of
[fid0] -> (st,map (flip PArg (mkFId arg_C fid0)) ctxt)
[fid0] -> (st,map (flip C.PArg (mkFId arg_C fid0)) ctxt)
fid0s -> case Map.lookup fids crc of
Just fid -> (st,map (flip PArg fid) ctxt)
Just fid -> (st,map (flip C.PArg fid) ctxt)
Nothing -> let !crc' = Map.insert fids fid_cnt crc
!prods' = IntMap.insert fid_cnt (Set.fromList (map PCoerce fids)) prods
in ((fid_cnt+1,crc',prods'),map (flip PArg fid_cnt) ctxt)
!prods' = IntMap.insert fid_cnt (Set.fromList (map C.PCoerce fids)) prods
in ((fid_cnt+1,crc',prods'),map (flip C.PArg fid_cnt) ctxt)
where
(hargs_C,arg_C) = GM.catSkeleton ty
ctxt = mapM (mkCtxt lindefs) hargs_C
@@ -297,14 +252,14 @@ genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
mkLinDefId id = prefixIdent "lindef " id
toLinDef res offs lindefs (A.Production fid0 funid0 args) =
toLinDef res offs lindefs (Production fid0 funid0 args) =
if args == [[fidVar]]
then IntMap.insertWith (++) fid [offs+funid0] lindefs
else lindefs
where
fid = mkFId res fid0
toLinRef res offs linrefs (A.Production fid0 funid0 [fargs]) =
toLinRef res offs linrefs (Production fid0 funid0 [fargs]) =
if fid0 == fidVar
then foldr (\fid -> IntMap.insertWith (++) fid [offs+funid0]) linrefs fids
else linrefs
@@ -312,20 +267,20 @@ genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
fids = map (mkFId res) fargs
mkFId (_,cat) fid0 =
case Map.lookup (i2i cat) cnccat_ranges of
Just (s,e) -> s+fid0
Nothing -> error ("GrammarToPGF.mkFId: missing category "++showIdent cat)
case Map.lookup (i2i cat) cnccats of
Just (C.CncCat s e _) -> s+fid0
Nothing -> error ("GrammarToPGF.mkFId: missing category "++showIdent cat)
mkCtxt lindefs (_,cat) =
case Map.lookup (i2i cat) cnccat_ranges of
Just (s,e) -> [(fid,fid) | fid <- [s..e], Just _ <- [IntMap.lookup fid lindefs]]
Nothing -> error "GrammarToPGF.mkCtxt failed"
case Map.lookup (i2i cat) cnccats of
Just (C.CncCat s e _) -> [(C.fidVar,fid) | fid <- [s..e], Just _ <- [IntMap.lookup fid lindefs]]
Nothing -> error "GrammarToPGF.mkCtxt failed"
toCncFun offs (m,id) funs (funid0,lins0) =
let mseqs = case lookupModule gr m of
Ok (ModInfo{mseqs=Just mseqs}) -> mseqs
_ -> ex_seqs
in (i2i id, map (newIndex mseqs) (elems lins0)):funs
in (offs+funid0,C.CncFun (i2i id) (amap (newIndex mseqs) lins0)):funs
where
newIndex mseqs i = binSearch (mseqs ! i) seqs (bounds seqs)
@@ -338,9 +293,8 @@ genCncFuns gr am cm ex_seqs ciCmp seqs cdefs fid_cnt cnccat_ranges =
where
k = (i+j) `div` 2
genPrintNames cdefs =
[(i2i id, name) | ((m,id),info) <- cdefs, name <- prn info]
Map.fromAscList [(i2i id, name) | ((m,id),info) <- cdefs, name <- prn info]
where
prn (CncFun _ _ (Just (L _ tr)) _) = [flatten tr]
prn (CncCat _ _ _ (Just (L _ tr)) _) = [flatten tr]
@@ -352,119 +306,3 @@ genPrintNames cdefs =
mkArray lst = listArray (0,length lst-1) lst
mkMapArray map = array (0,Map.size map-1) [(v,k) | (k,v) <- Map.toList map]
mkSetArray set = listArray (0,Set.size set-1) (Set.toList set)
-- The following is a version of Data.List.sortBy which together
-- with the sorting also eliminates duplicate values
sortNubBy cmp = mergeAll . sequences
where
sequences (a:b:xs) =
case cmp a b of
GT -> descending b [a] xs
EQ -> sequences (b:xs)
LT -> ascending b (a:) xs
sequences xs = [xs]
descending a as [] = [a:as]
descending a as (b:bs) =
case cmp a b of
GT -> descending b (a:as) bs
EQ -> descending a as bs
LT -> (a:as) : sequences (b:bs)
ascending a as [] = let !x = as [a]
in [x]
ascending a as (b:bs) =
case cmp a b of
GT -> let !x = as [a]
in x : sequences (b:bs)
EQ -> ascending a as bs
LT -> ascending b (\ys -> as (a:ys)) bs
mergeAll [x] = x
mergeAll xs = mergeAll (mergePairs xs)
mergePairs (a:b:xs) = let !x = merge a b
in x : mergePairs xs
mergePairs xs = xs
merge as@(a:as') bs@(b:bs') =
case cmp a b of
GT -> b:merge as bs'
EQ -> a:merge as' bs'
LT -> a:merge as' bs
merge [] bs = bs
merge as [] = as
-- The following function does case-insensitive comparison of sequences.
-- This is used to allow case-insensitive parsing, while
-- the linearizer still has access to the original cases.
compareCaseInsensitive [] [] = EQ
compareCaseInsensitive [] _ = LT
compareCaseInsensitive _ [] = GT
compareCaseInsensitive (x:xs) (y:ys) =
case compareSym x y of
EQ -> compareCaseInsensitive xs ys
x -> x
where
compareSym s1 s2 =
case s1 of
SymCat d1 r1
-> case s2 of
SymCat d2 r2
-> case compare d1 d2 of
EQ -> r1 `compare` r2
x -> x
_ -> LT
SymLit d1 r1
-> case s2 of
SymCat {} -> GT
SymLit d2 r2
-> case compare d1 d2 of
EQ -> r1 `compare` r2
x -> x
_ -> LT
SymVar d1 r1
-> if tagToEnum# (getTag s2 ># 2#)
then LT
else case s2 of
SymVar d2 r2
-> case compare d1 d2 of
EQ -> r1 `compare` r2
x -> x
_ -> GT
SymKS t1
-> if tagToEnum# (getTag s2 ># 3#)
then LT
else case s2 of
SymKS t2 -> t1 `compareToken` t2
_ -> GT
SymKP a1 b1
-> if tagToEnum# (getTag s2 ># 4#)
then LT
else case s2 of
SymKP a2 b2
-> case compare a1 a2 of
EQ -> b1 `compare` b2
x -> x
_ -> GT
_ -> let t1 = getTag s1
t2 = getTag s2
in if tagToEnum# (t1 <# t2)
then LT
else if tagToEnum# (t1 ==# t2)
then EQ
else GT
compareToken [] [] = EQ
compareToken [] _ = LT
compareToken _ [] = GT
compareToken (x:xs) (y:ys)
| x == y = compareToken xs ys
| otherwise = case compare (toLower x) (toLower y) of
EQ -> case compareToken xs ys of
EQ -> compare x y
x -> x
x -> x
-}

View File

@@ -0,0 +1,232 @@
{-# LANGUAGE PatternGuards #-}
----------------------------------------------------------------------
-- |
-- Module : Optimize
-- Maintainer : AR
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/09/16 13:56:13 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.18 $
--
-- Top-level partial evaluation for GF source modules.
-----------------------------------------------------------------------------
module GF.Compile.Optimize (optimizeModule) where
import GF.Grammar.Grammar
import GF.Infra.Ident
import GF.Grammar.Printer
import GF.Grammar.Macros
import GF.Grammar.Lookup
import GF.Grammar.Predef
import GF.Compile.Compute.ConcreteNew(GlobalEnv,normalForm,resourceValues)
import GF.Data.Operations
import GF.Infra.Option
import Control.Monad
import qualified Data.Set as Set
import qualified Data.Map as Map
import GF.Text.Pretty
import Debug.Trace
-- | partial evaluation of concrete syntax. AR 6\/2001 -- 16\/5\/2003 -- 5\/2\/2005.
optimizeModule :: Options -> SourceGrammar -> SourceModule -> Err SourceModule
optimizeModule opts sgr m@(name,mi)
| mstatus mi == MSComplete = do
ids <- topoSortJments m
mi <- foldM updateEvalInfo mi ids
return (name,mi)
| otherwise = return m
where
oopts = opts `addOptions` mflags mi
resenv = resourceValues oopts sgr
updateEvalInfo mi (i,info) = do
info <- evalInfo oopts resenv sgr (name,mi) i info
return (mi{jments=Map.insert i info (jments mi)})
evalInfo :: Options -> GlobalEnv -> SourceGrammar -> SourceModule -> Ident -> Info -> Err Info
evalInfo opts resenv sgr m c info = do
(if verbAtLeast opts Verbose then trace (" " ++ showIdent c) else id) return ()
errIn ("optimizing " ++ showIdent c) $ case info of
CncCat ptyp pde pre ppr mpmcfg -> do
pde' <- case (ptyp,pde) of
(Just (L _ typ), Just (L loc de)) -> do
de <- partEval opts gr ([(Explicit, varStr, typeStr)], typ) de
return (Just (L loc (factor param c 0 de)))
(Just (L loc typ), Nothing) -> do
de <- mkLinDefault gr typ
de <- partEval opts gr ([(Explicit, varStr, typeStr)], typ) de
return (Just (L loc (factor param c 0 de)))
_ -> return pde -- indirection
pre' <- case (ptyp,pre) of
(Just (L _ typ), Just (L loc re)) -> do
re <- partEval opts gr ([(Explicit, varStr, typ)], typeStr) re
return (Just (L loc (factor param c 0 re)))
(Just (L loc typ), Nothing) -> do
re <- mkLinReference gr typ
re <- partEval opts gr ([(Explicit, varStr, typ)], typeStr) re
return (Just (L loc (factor param c 0 re)))
_ -> return pre -- indirection
let ppr' = fmap (evalPrintname resenv c) ppr
return (CncCat ptyp pde' pre' ppr' mpmcfg)
CncFun (mt@(Just (_,cont,val))) pde ppr mpmcfg -> --trace (prt c) $
eIn ("linearization in type" <+> mkProd cont val [] $$ "of function") $ do
pde' <- case pde of
Just (L loc de) -> do de <- partEval opts gr (cont,val) de
return (Just (L loc (factor param c 0 de)))
Nothing -> return pde
let ppr' = fmap (evalPrintname resenv c) ppr
return $ CncFun mt pde' ppr' mpmcfg -- only cat in type actually needed
{-
ResOper pty pde
| not new && OptExpand `Set.member` optim -> do
pde' <- case pde of
Just (L loc de) -> do de <- computeConcrete gr de
return (Just (L loc (factor param c 0 de)))
Nothing -> return Nothing
return $ ResOper pty pde'
-}
_ -> return info
where
-- new = flag optNewComp opts -- computations moved to GF.Compile.GeneratePMCFG
gr = prependModule sgr m
optim = flag optOptimizations opts
param = OptParametrize `Set.member` optim
eIn cat = errIn (render ("Error optimizing" <+> cat <+> c <+> ':'))
-- | the main function for compiling linearizations
partEval :: Options -> SourceGrammar -> (Context,Type) -> Term -> Err Term
partEval opts = {-if flag optNewComp opts
then-} partEvalNew opts
{-else partEvalOld opts-}
partEvalNew opts gr (context, val) trm =
errIn (render ("partial evaluation" <+> ppTerm Qualified 0 trm)) $
checkPredefError trm
{-
partEvalOld opts gr (context, val) trm = errIn (render (text "partial evaluation" <+> ppTerm Qualified 0 trm)) $ do
let vars = map (\(bt,x,t) -> x) context
args = map Vr vars
subst = [(v, Vr v) | v <- vars]
trm1 = mkApp trm args
trm2 <- computeTerm gr subst trm1
trm3 <- if rightType trm2
then computeTerm gr subst trm2 -- compute twice??
else recordExpand val trm2 >>= computeTerm gr subst
trm4 <- checkPredefError trm3
return $ mkAbs [(Explicit,v) | v <- vars] trm4
where
-- don't eta expand records of right length (correct by type checking)
rightType (R rs) = case val of
RecType ts -> length rs == length ts
_ -> False
rightType _ = False
-- here we must be careful not to reduce
-- variants {{s = "Auto" ; g = N} ; {s = "Wagen" ; g = M}}
-- {s = variants {"Auto" ; "Wagen"} ; g = variants {N ; M}} ;
recordExpand :: Type -> Term -> Err Term
recordExpand typ trm = case typ of
RecType tys -> case trm of
FV rs -> return $ FV [R [assign lab (P r lab) | (lab,_) <- tys] | r <- rs]
_ -> return $ R [assign lab (P trm lab) | (lab,_) <- tys]
_ -> return trm
-}
-- | auxiliaries for compiling the resource
mkLinDefault :: SourceGrammar -> Type -> Err Term
mkLinDefault gr typ = liftM (Abs Explicit varStr) $ mkDefField typ
where
mkDefField typ = case typ of
Table p t -> do
t' <- mkDefField t
let T _ cs = mkWildCases t'
return $ T (TWild p) cs
Sort s | s == cStr -> return $ Vr varStr
QC p -> do vs <- lookupParamValues gr p
case vs of
v:_ -> return v
_ -> Bad (render ("no parameter values given to type" <+> ppQIdent Qualified p))
RecType r -> do
let (ls,ts) = unzip r
ts <- mapM mkDefField ts
return $ R (zipWith assign ls ts)
_ | Just _ <- isTypeInts typ -> return $ EInt 0 -- exists in all as first val
_ -> Bad (render ("linearization type field cannot be" <+> typ))
mkLinReference :: SourceGrammar -> Type -> Err Term
mkLinReference gr typ =
liftM (Abs Explicit varStr) $
case mkDefField typ (Vr varStr) of
Bad "no string" -> return Empty
x -> x
where
mkDefField ty trm =
case ty of
Table pty ty -> do ps <- allParamValues gr pty
case ps of
[] -> Bad "no string"
(p:ps) -> mkDefField ty (S trm p)
Sort s | s == cStr -> return trm
QC p -> Bad "no string"
RecType [] -> Bad "no string"
RecType rs -> do
msum (map (\(l,ty) -> mkDefField ty (P trm l)) (sortRec rs))
`mplus` Bad "no string"
_ | Just _ <- isTypeInts typ -> Bad "no string"
_ -> Bad (render ("linearization type field cannot be" <+> typ))
evalPrintname :: GlobalEnv -> Ident -> L Term -> L Term
evalPrintname resenv c (L loc pr) = L loc (normalForm resenv (L loc c) pr)
-- do even more: factor parametric branches
factor :: Bool -> Ident -> Int -> Term -> Term
factor param c i t =
case t of
T (TComp ty) cs -> factors ty [(p, factor param c (i+1) v) | (p, v) <- cs]
_ -> composSafeOp (factor param c i) t
where
factors ty pvs0
| not param = V ty (map snd pvs0)
factors ty [] = V ty []
factors ty pvs0@[(p,v)] = V ty [v]
factors ty pvs0@(pv:pvs) =
let t = mkFun pv
ts = map mkFun pvs
in if all (==t) ts
then T (TTyped ty) (mkCases t)
else V ty (map snd pvs0)
--- we hope this will be fresh and don't check... in GFC would be safe
qvar = identS ("q_" ++ showIdent c ++ "__" ++ show i)
mkFun (patt, val) = replace (patt2term patt) (Vr qvar) val
mkCases t = [(PV qvar, t)]
-- we need to replace subterms
replace :: Term -> Term -> Term -> Term
replace old new trm =
case trm of
-- these are the important cases, since they can correspond to patterns
QC _ | trm == old -> new
App _ _ | trm == old -> new
R _ | trm == old -> new
App x y -> App (replace old new x) (replace old new y)
_ -> composSafeOp (replace old new) trm

View File

@@ -1,191 +0,0 @@
{-# LANGUAGE BangPatterns #-}
module GF.Compile.OptimizePGF(optimizePGF) where
import PGF2(Cat,Fun)
import PGF2.Transactions
import Data.Array.ST
import Data.Array.Unboxed
import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.IntSet as IntSet
import qualified Data.IntMap as IntMap
import qualified Data.List as List
import Control.Monad.ST
type ConcrData = ()
{-([(FId,[FunId])], -- ^ Lindefs
[(FId,[FunId])], -- ^ Linrefs
[(FId,[Production])], -- ^ Productions
[(Fun,[SeqId])], -- ^ Concrete functions (must be sorted by Fun)
[[Symbol]], -- ^ Sequences (must be sorted)
[(Cat,FId,FId,[String])]) -- ^ Concrete categories
-}
optimizePGF :: Cat -> ConcrData -> ConcrData
optimizePGF startCat = error "TODO: optimizePGF" {- topDownFilter startCat . bottomUpFilter
catString = "String"
catInt = "Int"
catFloat = "Float"
catVar = "__gfVar"
topDownFilter :: Cat -> ConcrData -> ConcrData
topDownFilter startCat (lindefs,linrefs,prods,cncfuns,sequences,cnccats) =
let env0 = (Map.empty,Map.empty)
(env1,lindefs') = List.mapAccumL (\env (fid,funids) -> let (env',funids') = List.mapAccumL (optimizeFun fid [PArg [] fidVar]) env funids in (env',(fid,funids')))
env0
lindefs
(env2,linrefs') = List.mapAccumL (\env (fid,funids) -> let (env',funids') = List.mapAccumL (optimizeFun fidVar [PArg [] fid]) env funids in (env',(fid,funids')))
env1
linrefs
(env3,prods') = List.mapAccumL (\env (fid,set) -> let (env',set') = List.mapAccumL (optimizeProd fid) env set in (env',(fid,set')))
env2
prods
cnccats' = map filterCatLabels cnccats
(sequences',cncfuns') = env3
in (lindefs',linrefs',prods',mkSetArray cncfuns',mkSetArray sequences',cnccats')
where
cncfuns_array = listArray (0,length cncfuns-1) cncfuns :: Array FunId (Fun, [SeqId])
sequences_array = listArray (0,length sequences-1) sequences :: Array SeqId [Symbol]
prods_map = IntMap.fromList prods
fid2catMap = IntMap.fromList ((fidVar,catVar) : [(fid,cat) | (cat,start,end,lbls) <- cnccats,
fid <- [start..end]])
fid2cat fid =
case IntMap.lookup fid fid2catMap of
Just cat -> cat
Nothing -> case [fid | Just set <- [IntMap.lookup fid prods_map], PCoerce fid <- set] of
(fid:_) -> fid2cat fid
_ -> error "unknown forest id"
starts =
[(startCat,lbl) | (cat,_,_,lbls) <- cnccats, cat==startCat, lbl <- [0..length lbls-1]]
allRelations =
Map.unionsWith Set.union
[rel fid prod | (fid,set) <- prods, prod <- set]
where
rel fid (PApply funid args) = Map.fromList [((fid2cat fid,lbl),deps args seqid) | (lbl,seqid) <- zip [0..] lin]
where
(_,lin) = cncfuns_array ! funid
rel fid _ = Map.empty
deps args seqid = Set.fromList [let PArg _ fid = args !! r in (fid2cat fid,d) | SymCat r d <- seq]
where
seq = sequences_array ! seqid
-- here we create a mapping from a category to an array of indices.
-- An element of the array is equal to -1 if the corresponding index
-- is not going to be used in the optimized grammar, or the new index
-- if it will be used
closure :: Map.Map Cat [Int]
closure = runST $ do
set <- initSet
addLitCat catString set
addLitCat catInt set
addLitCat catFloat set
addLitCat catVar set
closureSet set starts
doneSet set
where
initSet :: ST s (Map.Map Cat (STUArray s Int Int))
initSet =
fmap Map.fromList $ sequence
[fmap ((,) cat) (newArray (0,length lbls-1) (-1))
| (cat,_,_,lbls) <- cnccats]
addLitCat cat set =
case Map.lookup cat set of
Just indices -> writeArray indices 0 0
Nothing -> return ()
closureSet set [] = return ()
closureSet set (x@(cat,index):xs) =
case Map.lookup cat set of
Just indices -> do v <- readArray indices index
writeArray indices index 0
if v < 0
then case Map.lookup x allRelations of
Just ys -> closureSet set (Set.toList ys++xs)
Nothing -> closureSet set xs
else closureSet set xs
Nothing -> error "unknown cat"
doneSet :: Map.Map Cat (STUArray s Int Int) -> ST s (Map.Map Cat [Int])
doneSet set =
fmap Map.fromAscList $ mapM done (Map.toAscList set)
where
done (cat,indices) = do
indices <- fmap (reindex 0) (getElems indices)
return (cat,indices)
reindex k [] = []
reindex k (v:vs)
| v < 0 = v : reindex k vs
| otherwise = k : reindex (k+1) vs
optimizeProd res env (PApply funid args) =
let (env',funid') = optimizeFun res args env funid
in (env', PApply funid' args)
optimizeProd res env prod = (env,prod)
optimizeFun res args (seqs,funs) funid =
let (seqs',lin') = List.mapAccumL addUnique seqs [map updateSymbol (sequences_array ! seqid) |
(idx,seqid) <- zip (indicesOf res) lin, idx >= 0]
(funs',funid') = addUnique funs (fun, lin')
in ((seqs',funs'), funid')
where
(fun,lin) = cncfuns_array ! funid
indicesOf fid
| fid < 0 = [0]
| otherwise =
case Map.lookup (fid2cat fid) closure of
Just indices -> indices
Nothing -> error "unknown category"
addUnique seqs seq =
case Map.lookup seq seqs of
Just seqid -> (seqs,seqid)
Nothing -> let seqid = Map.size seqs
in (Map.insert seq seqid seqs, seqid)
updateSymbol (SymCat r d) = let PArg _ fid = args !! r in SymCat r (indicesOf fid !! d)
updateSymbol s = s
filterCatLabels (cat,start,end,lbls) =
case Map.lookup cat closure of
Just indices -> let lbls' = [lbl | (idx,lbl) <- zip indices lbls, idx >= 0]
in (cat,start,end,lbls')
Nothing -> error ("unknown category")
mkSetArray map = sortSnd (Map.toList map)
where
sortSnd = List.map fst . List.sortBy (\(_,i) (_,j) -> compare i j)
bottomUpFilter :: ConcrData -> ConcrData
bottomUpFilter (lindefs,linrefs,prods,cncfuns,sequences,cnccats) =
(lindefs,linrefs,filterProductions IntMap.empty IntSet.empty prods,cncfuns,sequences,cnccats)
filterProductions prods0 hoc0 prods
| prods0 == prods1 = IntMap.toList prods0
| otherwise = filterProductions prods1 hoc1 prods
where
(prods1,hoc1) = foldl foldProdSet (IntMap.empty,IntSet.empty) prods
foldProdSet (!prods,!hoc) (fid,set)
| null set1 = (prods,hoc)
| otherwise = (IntMap.insert fid set1 prods,hoc1)
where
set1 = filter filterRule set
hoc1 = foldl accumHOC hoc set1
filterRule (PApply funid args) = all (\(PArg _ fid) -> isLive fid) args
filterRule (PCoerce fid) = isLive fid
filterRule _ = True
isLive fid = isPredefFId fid || IntMap.member fid prods0 || IntSet.member fid hoc0
accumHOC hoc (PApply funid args) = List.foldl' (\hoc (PArg hypos _) -> List.foldl' (\hoc fid -> IntSet.insert fid hoc) hoc (map snd hypos)) hoc args
accumHOC hoc _ = hoc
-}

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/06/17 12:39:07 $
-- > CVS $Date: 2005/06/17 12:39:07 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.8 $
--
@@ -16,14 +16,13 @@
module GF.Compile.PGFtoHaskell (grammar2haskell) where
import PGF2
import PGF2.Internal
import PGF(showCId)
import PGF.Internal
import GF.Data.Operations
import GF.Infra.Option
import Data.List(isPrefixOf,find,intercalate,intersperse,groupBy,sortBy)
import Data.Maybe(mapMaybe)
import Data.List --(isPrefixOf, find, intersperse)
import qualified Data.Map as Map
type Prefix = String -> String
@@ -35,12 +34,11 @@ grammar2haskell :: Options
-> PGF
-> String
grammar2haskell opts name gr = foldr (++++) [] $
pragmas ++ haskPreamble gadt name derivingClause (extraImports ++ pgfImports) ++
pragmas ++ haskPreamble gadt name derivingClause extraImports ++
[types, gfinstances gId lexical gr'] ++ compos
where gr' = hSkeleton gr
gadt = haskellOption opts HaskellGADT
dataExt = haskellOption opts HaskellData
pgf2 = haskellOption opts HaskellPGF2
lexical cat = haskellOption opts HaskellLexical && isLexicalCat opts cat
gId | haskellOption opts HaskellNoPrefix = rmForbiddenChars
| otherwise = ("G"++) . rmForbiddenChars
@@ -52,23 +50,21 @@ grammar2haskell opts name gr = foldr (++++) [] $
derivingClause
| dataExt = "deriving (Show,Data)"
| otherwise = "deriving Show"
extraImports | gadt = ["import Control.Monad.Identity", "import Data.Monoid"]
extraImports | gadt = ["import Control.Monad.Identity",
"import Data.Monoid"]
| dataExt = ["import Data.Data"]
| otherwise = []
pgfImports | pgf2 = ["import PGF2 hiding (Tree)", "", "showCId :: CId -> String", "showCId = id"]
| otherwise = ["import PGF hiding (Tree)"]
types | gadt = datatypesGADT gId lexical gr'
| otherwise = datatypes gId derivingClause lexical gr'
compos | gadt = prCompos gId lexical gr' ++ composClass
| otherwise = []
haskPreamble :: Bool -> String -> String -> [String] -> [String]
haskPreamble gadt name derivingClause imports =
haskPreamble gadt name derivingClause extraImports =
[
"module " ++ name ++ " where",
""
] ++ imports ++ [
"",
] ++ extraImports ++ [
"import PGF hiding (Tree)",
"----------------------------------------------------",
"-- automatic translation from GF to Haskell",
"----------------------------------------------------",
@@ -89,11 +85,10 @@ haskPreamble gadt name derivingClause imports =
""
]
predefInst :: Bool -> String -> String -> String -> String -> String -> String
predefInst gadt derivingClause gtyp typ destr consr =
(if gadt
then []
else "newtype" +++ gtyp +++ "=" +++ gtyp +++ typ +++ derivingClause ++ "\n\n"
else ("newtype" +++ gtyp +++ "=" +++ gtyp +++ typ +++ derivingClause ++ "\n\n")
)
++
"instance Gf" +++ gtyp +++ "where" ++++
@@ -108,10 +103,10 @@ type OIdent = String
type HSkeleton = [(OIdent, [(OIdent, [OIdent])])]
datatypes :: Prefix -> DerivingClause -> (OIdent -> Bool) -> (String,HSkeleton) -> String
datatypes gId derivingClause lexical = foldr (+++++) "" . filter (/="") . map (hDatatype gId derivingClause lexical) . snd
datatypes gId derivingClause lexical = (foldr (+++++) "") . (filter (/="")) . (map (hDatatype gId derivingClause lexical)) . snd
gfinstances :: Prefix -> (OIdent -> Bool) -> (String,HSkeleton) -> String
gfinstances gId lexical (m,g) = foldr (+++++) "" $ filter (/="") $ map (gfInstance gId lexical m) g
gfinstances gId lexical (m,g) = (foldr (+++++) "") $ (filter (/="")) $ (map (gfInstance gId lexical m)) g
hDatatype :: Prefix -> DerivingClause -> (OIdent -> Bool) -> (OIdent, [(OIdent, [OIdent])]) -> String
@@ -136,17 +131,16 @@ nonLexicalRules True rules = [r | r@(f,t) <- rules, not (null t)]
lexicalConstructor :: OIdent -> String
lexicalConstructor cat = "Lex" ++ cat
predefTypeSkel :: HSkeleton
predefTypeSkel = [(c,[]) | c <- ["String", "Int", "Float"]]
-- GADT version of data types
datatypesGADT :: Prefix -> (OIdent -> Bool) -> (String,HSkeleton) -> String
datatypesGADT gId lexical (_,skel) = unlines $
datatypesGADT gId lexical (_,skel) = unlines $
concatMap (hCatTypeGADT gId) (skel ++ predefTypeSkel) ++
[
"",
[
"",
"data Tree :: * -> * where"
] ++
] ++
concatMap (map (" "++) . hDatatypeGADT gId lexical) skel ++
[
" GString :: String -> Tree GString_",
@@ -170,23 +164,23 @@ hCatTypeGADT gId (cat,rules)
"data"+++gId cat++"_"]
hDatatypeGADT :: Prefix -> (OIdent -> Bool) -> (OIdent, [(OIdent, [OIdent])]) -> [String]
hDatatypeGADT gId lexical (cat, rules)
hDatatypeGADT gId lexical (cat, rules)
| isListCat (cat,rules) = [gId cat+++"::"+++"["++gId (elemCat cat)++"]" +++ "->" +++ t]
| otherwise =
[ gId f +++ "::" +++ concatMap (\a -> gId a +++ "-> ") args ++ t
[ gId f +++ "::" +++ concatMap (\a -> gId a +++ "-> ") args ++ t
| (f,args) <- nonLexicalRules (lexical cat) rules ]
++ if lexical cat then [lexicalConstructor cat +++ ":: String ->"+++ t] else []
where t = "Tree" +++ gId cat ++ "_"
hEqGADT :: Prefix -> (OIdent -> Bool) -> (OIdent, [(OIdent, [OIdent])]) -> [String]
hEqGADT gId lexical (cat, rules)
| isListCat (cat,rules) = let r = listr cat in ["(" ++ patt "x" r ++ "," ++ patt "y" r ++ ") -> " ++ listeqs]
| isListCat (cat,rules) = let r = listr cat in ["(" ++ patt "x" r ++ "," ++ patt "y" r ++ ") -> " ++ listeqs]
| otherwise = ["(" ++ patt "x" r ++ "," ++ patt "y" r ++ ") -> " ++ eqs r | r <- nonLexicalRules (lexical cat) rules]
++ if lexical cat then ["(" ++ lexicalConstructor cat +++ "x" ++ "," ++ lexicalConstructor cat +++ "y" ++ ") -> x == y"] else []
where
patt s (f,xs) = unwords (gId f : mkSVars s (length xs))
eqs (_,xs) = unwords ("and" : "[" : intersperse "," [x ++ " == " ++ y |
eqs (_,xs) = unwords ("and" : "[" : intersperse "," [x ++ " == " ++ y |
(x,y) <- zip (mkSVars "x" (length xs)) (mkSVars "y" (length xs)) ] ++ ["]"])
listr c = (c,["foo"]) -- foo just for length = 1
listeqs = "and [x == y | (x,y) <- zip x1 y1]"
@@ -195,26 +189,25 @@ prCompos :: Prefix -> (OIdent -> Bool) -> (String,HSkeleton) -> [String]
prCompos gId lexical (_,catrules) =
["instance Compos Tree where",
" compos r a f t = case t of"]
++
++
[" " ++ prComposCons (gId f) xs | (c,rs) <- catrules, not (isListCat (c,rs)),
(f,xs) <- rs, not (null xs)]
++
(f,xs) <- rs, not (null xs)]
++
[" " ++ prComposCons (gId c) ["x1"] | (c,rs) <- catrules, isListCat (c,rs)]
++
++
[" _ -> r t"]
where
prComposCons f xs = let vs = mkVars (length xs) in
prComposCons f xs = let vs = mkVars (length xs) in
f +++ unwords vs +++ "->" +++ rhs f (zip vs xs)
rhs f vcs = "r" +++ f +++ unwords (map (prRec f) vcs)
prRec f (v,c)
prRec f (v,c)
| isList f = "`a` foldr (a . a (r (:)) . f) (r [])" +++ v
| otherwise = "`a`" +++ "f" +++ v
isList f = gId "List" `isPrefixOf` f
isList f = (gId "List") `isPrefixOf` f
gfInstance :: Prefix -> (OIdent -> Bool) -> String -> (OIdent, [(OIdent, [OIdent])]) -> String
gfInstance gId lexical m crs = hInstance gId lexical m crs ++++ fInstance gId lexical m crs
hInstance :: (String -> String) -> (String -> Bool) -> String -> (String, [(OIdent, [OIdent])]) -> String
----hInstance m ("Cn",_) = "" --- seems to belong to an old applic. AR 18/5/2004
hInstance gId _ m (cat,[]) = unlines [
"instance Show" +++ gId cat,
@@ -223,15 +216,15 @@ hInstance gId _ m (cat,[]) = unlines [
" gf _ = undefined",
" fg _ = undefined"
]
hInstance gId lexical m (cat,rules)
hInstance gId lexical m (cat,rules)
| isListCat (cat,rules) =
"instance Gf" +++ gId cat +++ "where" ++++
" gf (" ++ gId cat +++ "[" ++ intercalate "," baseVars ++ "])"
" gf (" ++ gId cat +++ "[" ++ concat (intersperse "," baseVars) ++ "])"
+++ "=" +++ mkRHS ("Base"++ec) baseVars ++++
" gf (" ++ gId cat +++ "(x:xs)) = "
++ mkRHS ("Cons"++ec) ["x",prParenth (gId cat+++"xs")]
" gf (" ++ gId cat +++ "(x:xs)) = "
++ mkRHS ("Cons"++ec) ["x",prParenth (gId cat+++"xs")]
-- no show for GADTs
-- ++++ " gf (" ++ gId cat +++ "xs) = error (\"Bad " ++ cat ++ " value: \" ++ show xs)"
-- ++++ " gf (" ++ gId cat +++ "xs) = error (\"Bad " ++ cat ++ " value: \" ++ show xs)"
| otherwise =
"instance Gf" +++ gId cat +++ "where\n" ++
unlines ([mkInst f xx | (f,xx) <- nonLexicalRules (lexical cat) rules]
@@ -240,26 +233,23 @@ hInstance gId lexical m (cat,rules)
ec = elemCat cat
baseVars = mkVars (baseSize (cat,rules))
mkInst f xx = let xx' = mkVars (length xx) in " gf " ++
(if null xx then gId f else prParenth (gId f +++ foldr1 (+++) xx')) +++
(if length xx == 0 then gId f else prParenth (gId f +++ foldr1 (+++) xx')) +++
"=" +++ mkRHS f xx'
mkRHS f vars = "mkApp (mkCId \"" ++ f ++ "\")" +++
"[" ++ prTList ", " ["gf" +++ x | x <- vars] ++ "]"
mkRHS f vars = "mkApp (mkCId \"" ++ f ++ "\")" +++
"[" ++ prTList ", " ["gf" +++ x | x <- vars] ++ "]"
mkVars :: Int -> [String]
mkVars = mkSVars "x"
mkSVars :: String -> Int -> [String]
mkSVars s n = [s ++ show i | i <- [1..n]]
----fInstance m ("Cn",_) = "" ---
fInstance _ _ m (cat,[]) = ""
fInstance gId lexical m (cat,rules) =
" fg t =" ++++
(if isList
(if isList
then " " ++ gId cat ++ " (fgs t) where\n fgs t = case unApp t of"
else " case unApp t of") ++++
unlines [mkInst f xx | (f,xx) <- nonLexicalRules (lexical cat) rules] ++++
(if lexical cat then " Just (i,[]) -> " ++ lexicalConstructor cat +++ "i" else "") ++++
(if lexical cat then " Just (i,[]) -> " ++ lexicalConstructor cat +++ "(showCId i)" else "") ++++
" _ -> error (\"no" +++ cat ++ " \" ++ show t)"
where
isList = isListCat (cat,rules)
@@ -267,35 +257,31 @@ fInstance gId lexical m (cat,rules) =
" Just (i," ++
"[" ++ prTList "," xx' ++ "])" +++
"| i == mkCId \"" ++ f ++ "\" ->" +++ mkRHS f xx'
where
xx' = ["x" ++ show i | (_,i) <- zip xx [1..]]
mkRHS f vars
| isList =
if "Base" `isPrefixOf` f
then "[" ++ prTList ", " [ "fg" +++ x | x <- vars ] ++ "]"
else "fg" +++ (vars !! 0) +++ ":" +++ "fgs" +++ (vars !! 1)
| otherwise =
gId f +++
prTList " " [prParenth ("fg" +++ x) | x <- vars]
where xx' = ["x" ++ show i | (_,i) <- zip xx [1..]]
mkRHS f vars
| isList =
if "Base" `isPrefixOf` f
then "[" ++ prTList ", " [ "fg" +++ x | x <- vars ] ++ "]"
else "fg" +++ (vars !! 0) +++ ":" +++ "fgs" +++ (vars !! 1)
| otherwise =
gId f +++
prTList " " [prParenth ("fg" +++ x) | x <- vars]
--type HSkeleton = [(OIdent, [(OIdent, [OIdent])])]
hSkeleton :: PGF -> (String,HSkeleton)
hSkeleton gr =
(abstractName gr,
(showCId (absname gr),
let fs =
[(c, [(f, cs) | (f, cs,_) <- fs]) |
fs@((_, _,c):_) <- fns]
in fs ++ [(c, []) | c <- cts, notElem c (["Int", "Float", "String"] ++ map fst fs)]
[(showCId c, [(showCId f, map showCId cs) | (f, (cs,_)) <- fs]) |
fs@((_, (_,c)):_) <- fns]
in fs ++ [(sc, []) | c <- cts, let sc = showCId c, notElem sc (["Int", "Float", "String"] ++ map fst fs)]
)
where
cts = categories gr
fns = groupBy valtypg (sortBy valtyps (mapMaybe jty (functions gr)))
valtyps (_,_,x) (_,_,y) = compare x y
valtypg (_,_,x) (_,_,y) = x == y
jty f = case functionType gr f of
Just ty -> let (hypos,valcat,_) = unType ty
in Just (f,[argcat | (_,_,ty) <- hypos, let (_,argcat,_) = unType ty],valcat)
Nothing -> Nothing
cts = Map.keys (cats (abstract gr))
fns = groupBy valtypg (sortBy valtyps (map jty (Map.assocs (funs (abstract gr)))))
valtyps (_, (_,x)) (_, (_,y)) = compare x y
valtypg (_, (_,x)) (_, (_,y)) = x == y
jty (f,(ty,_,_,_)) = (f,catSkeleton ty)
{-
updateSkeleton :: OIdent -> HSkeleton -> (OIdent, [OIdent]) -> HSkeleton
updateSkeleton cat skel rule =
@@ -305,10 +291,9 @@ updateSkeleton cat skel rule =
-}
isListCat :: (OIdent, [(OIdent, [OIdent])]) -> Bool
isListCat (cat,rules) = "List" `isPrefixOf` cat && length rules == 2
&& ("Base"++c) `elem` fs && ("Cons"++c) `elem` fs
where
c = elemCat cat
fs = map fst rules
&& ("Base"++c) `elem` fs && ("Cons"++c) `elem` fs
where c = elemCat cat
fs = map fst rules
-- | Gets the element category of a list category.
elemCat :: OIdent -> OIdent
@@ -325,7 +310,7 @@ baseSize (_,rules) = length bs
where Just (_,bs) = find (("Base" `isPrefixOf`) . fst) rules
composClass :: [String]
composClass =
composClass =
[
"",
"class Compos t where",
@@ -352,3 +337,4 @@ composClass =
"",
"newtype C b a = C { unC :: b }"
]

View File

@@ -0,0 +1,105 @@
module GF.Compile.PGFtoJS (pgf2js) where
import PGF(showCId)
import PGF.Internal as M
import qualified GF.JavaScript.AbsJS as JS
import qualified GF.JavaScript.PrintJS as JS
--import GF.Data.ErrM
--import GF.Infra.Option
--import Control.Monad (mplus)
--import Data.Array.Unboxed (UArray)
import qualified Data.Array.IArray as Array
--import Data.Maybe (fromMaybe)
import Data.Map (Map)
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
pgf2js :: PGF -> String
pgf2js pgf =
JS.printTree $ JS.Program [JS.ElStmt $ JS.SDeclOrExpr $ JS.Decl [JS.DInit (JS.Ident n) grammar]]
where
n = showCId $ absname pgf
as = abstract pgf
cs = Map.assocs (concretes pgf)
start = showCId $ M.lookStartCat pgf
grammar = new "GFGrammar" [js_abstract, js_concrete]
js_abstract = abstract2js start as
js_concrete = JS.EObj $ map concrete2js cs
abstract2js :: String -> Abstr -> JS.Expr
abstract2js start ds = new "GFAbstract" [JS.EStr start, JS.EObj $ map absdef2js (Map.assocs (funs ds))]
absdef2js :: (CId,(Type,Int,Maybe ([Equation],[[M.Instr]]),Double)) -> JS.Property
absdef2js (f,(typ,_,_,_)) =
let (args,cat) = M.catSkeleton typ in
JS.Prop (JS.IdentPropName (JS.Ident (showCId f))) (new "Type" [JS.EArray [JS.EStr (showCId x) | x <- args], JS.EStr (showCId cat)])
lit2js (LStr s) = JS.EStr s
lit2js (LInt n) = JS.EInt n
lit2js (LFlt d) = JS.EDbl d
concrete2js :: (CId,Concr) -> JS.Property
concrete2js (c,cnc) =
JS.Prop l (new "GFConcrete" [mapToJSObj (lit2js) $ cflags cnc,
JS.EObj $ [JS.Prop (JS.IntPropName cat) (JS.EArray (map frule2js (Set.toList set))) | (cat,set) <- IntMap.toList (productions cnc)],
JS.EArray $ (map ffun2js (Array.elems (cncfuns cnc))),
JS.EArray $ (map seq2js (Array.elems (sequences cnc))),
JS.EObj $ map cats (Map.assocs (cnccats cnc)),
JS.EInt (totalCats cnc)])
where
l = JS.IdentPropName (JS.Ident (showCId c))
{-
litslins = [JS.Prop (JS.StringPropName "Int") (JS.EFun [children] [JS.SReturn $ new "Arr" [JS.EIndex (JS.EVar children) (JS.EInt 0)]]),
JS.Prop (JS.StringPropName "Float") (JS.EFun [children] [JS.SReturn $ new "Arr" [JS.EIndex (JS.EVar children) (JS.EInt 0)]]),
JS.Prop (JS.StringPropName "String") (JS.EFun [children] [JS.SReturn $ new "Arr" [JS.EIndex (JS.EVar children) (JS.EInt 0)]])]
-}
cats (c,CncCat start end _) = JS.Prop (JS.IdentPropName (JS.Ident (showCId c))) (JS.EObj [JS.Prop (JS.IdentPropName (JS.Ident "s")) (JS.EInt start)
,JS.Prop (JS.IdentPropName (JS.Ident "e")) (JS.EInt end)])
{-
mkStr :: String -> JS.Expr
mkStr s = new "Str" [JS.EStr s]
mkSeq :: [JS.Expr] -> JS.Expr
mkSeq [x] = x
mkSeq xs = new "Seq" xs
argIdent :: Integer -> JS.Ident
argIdent n = JS.Ident ("x" ++ show n)
-}
children :: JS.Ident
children = JS.Ident "cs"
frule2js :: Production -> JS.Expr
frule2js (PApply funid args) = new "Apply" [JS.EInt funid, JS.EArray (map farg2js args)]
frule2js (PCoerce arg) = new "Coerce" [JS.EInt arg]
farg2js (PArg hypos fid) = new "PArg" (map (JS.EInt . snd) hypos ++ [JS.EInt fid])
ffun2js (CncFun f lins) = new "CncFun" [JS.EStr (showCId f), JS.EArray (map JS.EInt (Array.elems lins))]
seq2js :: Array.Array DotPos Symbol -> JS.Expr
seq2js seq = JS.EArray [sym2js s | s <- Array.elems seq]
sym2js :: Symbol -> JS.Expr
sym2js (SymCat n l) = new "SymCat" [JS.EInt n, JS.EInt l]
sym2js (SymLit n l) = new "SymLit" [JS.EInt n, JS.EInt l]
sym2js (SymVar n l) = new "SymVar" [JS.EInt n, JS.EInt l]
sym2js (SymKS t) = new "SymKS" [JS.EStr t]
sym2js (SymKP ts alts) = new "SymKP" [JS.EArray (map sym2js ts), JS.EArray (map alt2js alts)]
sym2js SymBIND = new "SymKS" [JS.EStr "&+"]
sym2js SymSOFT_BIND = new "SymKS" [JS.EStr "&+"]
sym2js SymSOFT_SPACE = new "SymKS" [JS.EStr "&+"]
sym2js SymCAPIT = new "SymKS" [JS.EStr "&|"]
sym2js SymALL_CAPIT = new "SymKS" [JS.EStr "&|"]
sym2js SymNE = new "SymNE" []
alt2js (ps,ts) = new "Alt" [JS.EArray (map sym2js ps), JS.EArray (map JS.EStr ts)]
new :: String -> [JS.Expr] -> JS.Expr
new f xs = JS.ENew (JS.Ident f) xs
mapToJSObj :: (a -> JS.Expr) -> Map CId a -> JS.Expr
mapToJSObj f m = JS.EObj [ JS.Prop (JS.IdentPropName (JS.Ident (showCId k))) (f v) | (k,v) <- Map.toList m ]

View File

@@ -1,111 +1,156 @@
module GF.Compile.PGFtoJSON (pgf2json) where
import PGF2
import PGF2.Internal
import Text.JSON
import PGF (showCId)
import qualified PGF.Internal as M
import PGF.Internal (
Abstr,
CId,
CncCat(..),
CncFun(..),
Concr,
DotPos,
Equation(..),
Literal(..),
PArg(..),
PGF,
Production(..),
Symbol(..),
Type,
absname,
abstract,
cflags,
cnccats,
cncfuns,
concretes,
funs,
productions,
sequences,
totalCats
)
import qualified Text.JSON as JSON
import Text.JSON (JSValue(..))
import qualified Data.Array.IArray as Array
import Data.Map (Map)
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
pgf2json :: PGF -> String
pgf2json pgf = error "TODO: pgf2json"
{- encode $ makeObj
[ ("abstract", abstract2json pgf)
, ("concretes", makeObj $ map concrete2json
(Map.toList (languages pgf)))
pgf2json pgf =
JSON.encode $ JSON.makeObj
[ ("abstract", json_abstract)
, ("concretes", json_concretes)
]
where
n = showCId $ absname pgf
as = abstract pgf
cs = Map.assocs (concretes pgf)
start = showCId $ M.lookStartCat pgf
json_abstract = abstract2json n start as
json_concretes = JSON.makeObj $ map concrete2json cs
abstract2json :: String -> String -> Abstr -> JSValue
abstract2json name start ds =
JSON.makeObj
[ ("name", mkJSStr name)
, ("startcat", mkJSStr start)
, ("funs", JSON.makeObj $ map absdef2json (Map.assocs (funs ds)))
]
abstract2json :: PGF -> JSValue
abstract2json pgf =
makeObj
[ ("name", showJSON (abstractName pgf))
, ("startcat", showJSON (showType [] (startCat pgf)))
, ("funs", makeObj $ map (absdef2json pgf) (functions pgf))
]
absdef2json :: PGF -> Fun -> (String,JSValue)
absdef2json pgf f = (f,sig)
absdef2json :: (CId,(Type,Int,Maybe ([Equation],[[M.Instr]]),Double)) -> (String,JSValue)
absdef2json (f,(typ,_,_,_)) = (showCId f,sig)
where
Just (hypos,cat,_) = fmap unType (functionType pgf f)
sig = makeObj
[ ("args", showJSON $ map (\(_,_,ty) -> showType [] ty) hypos)
, ("cat", showJSON cat)
(args,cat) = M.catSkeleton typ
sig = JSON.makeObj
[ ("args", JSArray $ map (mkJSStr.showCId) args)
, ("cat", mkJSStr $ showCId cat)
]
lit2json :: Literal -> JSValue
lit2json (LStr s) = showJSON s
lit2json (LInt n) = showJSON n
lit2json (LFlt d) = showJSON d
lit2json (LStr s) = mkJSStr s
lit2json (LInt n) = mkJSInt n
lit2json (LFlt d) = JSRational True (toRational d)
concrete2json :: (ConcName,Concr) -> (String,JSValue)
concrete2json (c,cnc) = (c,obj)
concrete2json :: (CId,Concr) -> (String,JSValue)
concrete2json (c,cnc) = (showCId c,obj)
where
obj = makeObj
[ ("flags", makeObj [(k, lit2json v) | (k,v) <- concrFlags cnc])
, ("productions", makeObj [(show fid, showJSON (map frule2json (concrProductions cnc fid))) | (_,start,end,_) <- concrCategories cnc, fid <- [start..end]])
, ("functions", showJSON [ffun2json funid (concrFunction cnc funid) | funid <- [0..concrTotalFuns cnc-1]])
, ("sequences", showJSON [seq2json seqid (concrSequence cnc seqid) | seqid <- [0..concrTotalSeqs cnc-1]])
, ("categories", makeObj $ map cat2json (concrCategories cnc))
, ("totalfids", showJSON (concrTotalCats cnc))
obj = JSON.makeObj
[ ("flags", JSON.makeObj [ (showCId k, lit2json v) | (k,v) <- Map.toList (cflags cnc) ])
, ("productions", JSON.makeObj [ (show cat, JSArray (map frule2json (Set.toList set))) | (cat,set) <- IntMap.toList (productions cnc)])
, ("functions", JSArray (map ffun2json (Array.elems (cncfuns cnc))))
, ("sequences", JSArray (map seq2json (Array.elems (sequences cnc))))
, ("categories", JSON.makeObj $ map cats2json (Map.assocs (cnccats cnc)))
, ("totalfids", mkJSInt (totalCats cnc))
]
cat2json :: (Cat,FId,FId,[String]) -> (String,JSValue)
cat2json (cat,start,end,_) = (cat, ixs)
cats2json :: (CId, CncCat) -> (String,JSValue)
cats2json (c,CncCat start end _) = (showCId c, ixs)
where
ixs = makeObj
[ ("start", showJSON start)
, ("end", showJSON end)
ixs = JSON.makeObj
[ ("start", mkJSInt start)
, ("end", mkJSInt end)
]
frule2json :: Production -> JSValue
frule2json (PApply fid args) =
makeObj
[ ("type", showJSON "Apply")
, ("fid", showJSON fid)
, ("args", showJSON (map farg2json args))
JSON.makeObj
[ ("type", mkJSStr "Apply")
, ("fid", mkJSInt fid)
, ("args", JSArray (map farg2json args))
]
frule2json (PCoerce arg) =
makeObj
[ ("type", showJSON "Coerce")
, ("arg", showJSON arg)
JSON.makeObj
[ ("type", mkJSStr "Coerce")
, ("arg", mkJSInt arg)
]
farg2json :: PArg -> JSValue
farg2json (PArg hypos fid) =
makeObj
[ ("type", showJSON "PArg")
, ("hypos", JSArray $ map (showJSON . snd) hypos)
, ("fid", showJSON fid)
JSON.makeObj
[ ("type", mkJSStr "PArg")
, ("hypos", JSArray $ map (mkJSInt . snd) hypos)
, ("fid", mkJSInt fid)
]
ffun2json :: FunId -> (Fun,[SeqId]) -> JSValue
ffun2json funid (fun,seqids) =
makeObj
[ ("name", showJSON fun)
, ("lins", showJSON seqids)
ffun2json :: CncFun -> JSValue
ffun2json (CncFun f lins) =
JSON.makeObj
[ ("name", mkJSStr $ showCId f)
, ("lins", JSArray (map mkJSInt (Array.elems lins)))
]
seq2json :: SeqId -> [Symbol] -> JSValue
seq2json seqid seq = showJSON [sym2json sym | sym <- seq]
seq2json :: Array.Array DotPos Symbol -> JSValue
seq2json seq = JSArray [sym2json s | s <- Array.elems seq]
sym2json :: Symbol -> JSValue
sym2json (SymCat n l) = new "SymCat" [showJSON n, showJSON l]
sym2json (SymLit n l) = new "SymLit" [showJSON n, showJSON l]
sym2json (SymVar n l) = new "SymVar" [showJSON n, showJSON l]
sym2json (SymKS t) = new "SymKS" [showJSON t]
sym2json (SymCat n l) = new "SymCat" [mkJSInt n, mkJSInt l]
sym2json (SymLit n l) = new "SymLit" [mkJSInt n, mkJSInt l]
sym2json (SymVar n l) = new "SymVar" [mkJSInt n, mkJSInt l]
sym2json (SymKS t) = new "SymKS" [mkJSStr t]
sym2json (SymKP ts alts) = new "SymKP" [JSArray (map sym2json ts), JSArray (map alt2json alts)]
sym2json SymBIND = new "SymKS" [showJSON "&+"]
sym2json SymSOFT_BIND = new "SymKS" [showJSON "&+"]
sym2json SymSOFT_SPACE = new "SymKS" [showJSON "&+"]
sym2json SymCAPIT = new "SymKS" [showJSON "&|"]
sym2json SymALL_CAPIT = new "SymKS" [showJSON "&|"]
sym2json SymBIND = new "SymKS" [mkJSStr "&+"]
sym2json SymSOFT_BIND = new "SymKS" [mkJSStr "&+"]
sym2json SymSOFT_SPACE = new "SymKS" [mkJSStr "&+"]
sym2json SymCAPIT = new "SymKS" [mkJSStr "&|"]
sym2json SymALL_CAPIT = new "SymKS" [mkJSStr "&|"]
sym2json SymNE = new "SymNE" []
alt2json :: ([Symbol],[String]) -> JSValue
alt2json (ps,ts) = new "Alt" [showJSON (map sym2json ps), showJSON ts]
alt2json (ps,ts) = new "Alt" [JSArray (map sym2json ps), JSArray (map mkJSStr ts)]
new :: String -> [JSValue] -> JSValue
new f xs =
makeObj
[ ("type", showJSON f)
, ("args", showJSON xs)
JSON.makeObj
[ ("type", mkJSStr f)
, ("args", JSArray xs)
]
-}
-- | Make JSON value from string
mkJSStr :: String -> JSValue
mkJSStr = JSString . JSON.toJSString
-- | Make JSON value from integer
mkJSInt :: Integral a => a -> JSValue
mkJSInt = JSRational False . toRational

View File

@@ -1,6 +1,6 @@
module GF.Compile.PGFtoJava (grammar2java) where
import PGF2
import PGF
import Data.Maybe(maybe)
import Data.List(intercalate)
import GF.Infra.Option
@@ -24,8 +24,9 @@ javaPreamble name =
]
javaMethod gr fun =
" public static Expr "++fun++"("++arg_decls++") { return new Expr("++show fun++args++"); }"
" public static Expr "++name++"("++arg_decls++") { return new Expr("++show name++args++"); }"
where
name = showCId fun
arity = maybe 0 getArrity (functionType gr fun)
vars = ['e':show i | i <- [1..arity]]

View File

@@ -0,0 +1,262 @@
----------------------------------------------------------------------
-- |
-- Module : PGFtoProlog
-- Maintainer : Peter Ljunglöf
--
-- exports a GF grammar into a Prolog module
-----------------------------------------------------------------------------
module GF.Compile.PGFtoProlog (grammar2prolog) where
import PGF(mkCId,wildCId,showCId)
import PGF.Internal
--import PGF.Macros
import GF.Data.Operations
import qualified Data.Array.IArray as Array
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
import Data.Char (isAlphaNum, isAscii, isAsciiLower, isAsciiUpper, ord)
import Data.List (isPrefixOf, mapAccumL)
grammar2prolog :: PGF -> String
grammar2prolog pgf
= ("%% This file was automatically generated by GF" +++++
":- style_check(-singleton)." +++++
plFacts wildCId "abstract" 1 "(?AbstractName)"
[[plp name]] ++++
plFacts wildCId "concrete" 2 "(?AbstractName, ?ConcreteName)"
[[plp name, plp cncname] |
cncname <- Map.keys (concretes pgf)] ++++
plFacts wildCId "flag" 2 "(?Flag, ?Value): global flags"
[[plp f, plp v] |
(f, v) <- Map.assocs (gflags pgf)] ++++
plAbstract name (abstract pgf) ++++
unlines (map plConcrete (Map.assocs (concretes pgf)))
)
where name = absname pgf
----------------------------------------------------------------------
-- abstract syntax
plAbstract :: CId -> Abstr -> String
plAbstract name abs
= (plHeader "Abstract syntax" ++++
plFacts name "flag" 2 "(?Flag, ?Value): flags for abstract syntax"
[[plp f, plp v] |
(f, v) <- Map.assocs (aflags abs)] ++++
plFacts name "cat" 2 "(?Type, ?[X:Type,...])"
[[plType cat args, plHypos hypos'] |
(cat, (hypos,_,_)) <- Map.assocs (cats abs),
let ((_, subst), hypos') = mapAccumL alphaConvertHypo emptyEnv hypos,
let args = reverse [EFun x | (_,x) <- subst]] ++++
plFacts name "fun" 3 "(?Fun, ?Type, ?[X:Type,...])"
[[plp fun, plType cat args, plHypos hypos] |
(fun, (typ, _, _, _)) <- Map.assocs (funs abs),
let (_, DTyp hypos cat args) = alphaConvert emptyEnv typ] ++++
plFacts name "def" 2 "(?Fun, ?Expr)"
[[plp fun, plp expr] |
(fun, (_, _, Just (eqs,_), _)) <- Map.assocs (funs abs),
let (_, expr) = alphaConvert emptyEnv eqs]
)
where plType cat args = plTerm (plp cat) (map plp args)
plHypos hypos = plList [plOper ":" (plp x) (plp ty) | (_, x, ty) <- hypos]
----------------------------------------------------------------------
-- concrete syntax
plConcrete :: (CId, Concr) -> String
plConcrete (name, cnc)
= (plHeader ("Concrete syntax: " ++ plp name) ++++
plFacts name "flag" 2 "(?Flag, ?Value): flags for concrete syntax"
[[plp f, plp v] |
(f, v) <- Map.assocs (cflags cnc)] ++++
plFacts name "printname" 2 "(?AbsFun/AbsCat, ?Atom)"
[[plp f, plp n] |
(f, n) <- Map.assocs (printnames cnc)] ++++
plFacts name "lindef" 2 "(?CncCat, ?CncFun)"
[[plCat cat, plFun fun] |
(cat, funs) <- IntMap.assocs (lindefs cnc),
fun <- funs] ++++
plFacts name "prod" 3 "(?CncCat, ?CncFun, ?[CncCat])"
[[plCat cat, fun, plTerm "c" (map plCat args)] |
(cat, set) <- IntMap.toList (productions cnc),
(fun, args) <- map plProduction (Set.toList set)] ++++
plFacts name "cncfun" 3 "(?CncFun, ?[Seq,...], ?AbsFun)"
[[plFun fun, plTerm "s" (map plSeq (Array.elems lins)), plp absfun] |
(fun, CncFun absfun lins) <- Array.assocs (cncfuns cnc)] ++++
plFacts name "seq" 2 "(?Seq, ?[Term])"
[[plSeq seq, plp (Array.elems symbols)] |
(seq, symbols) <- Array.assocs (sequences cnc)] ++++
plFacts name "cnccat" 2 "(?AbsCat, ?[CnCCat])"
[[plp cat, plList (map plCat [start..end])] |
(cat, CncCat start end _) <- Map.assocs (cnccats cnc)]
)
where plProduction (PCoerce arg) = ("-", [arg])
plProduction (PApply funid args) = (plFun funid, [fid | PArg hypos fid <- args])
----------------------------------------------------------------------
-- prolog-printing pgf datatypes
instance PLPrint Type where
plp (DTyp hypos cat args)
| null hypos = result
| otherwise = plOper " -> " plHypos result
where result = plTerm (plp cat) (map plp args)
plHypos = plList [plOper ":" (plp x) (plp ty) | (_,x,ty) <- hypos]
instance PLPrint Expr where
plp (EFun x) = plp x
plp (EAbs _ x e)= plOper "^" (plp x) (plp e)
plp (EApp e e') = plOper " * " (plp e) (plp e')
plp (ELit lit) = plp lit
plp (EMeta n) = "Meta_" ++ show n
instance PLPrint Patt where
plp (PVar x) = plp x
plp (PApp f ps) = plOper " * " (plp f) (plp ps)
plp (PLit lit) = plp lit
instance PLPrint Equation where
plp (Equ patterns result) = plOper ":" (plp patterns) (plp result)
instance PLPrint CId where
plp cid | isLogicalVariable str || cid == wildCId = plVar str
| otherwise = plAtom str
where str = showCId cid
instance PLPrint Literal where
plp (LStr s) = plp s
plp (LInt n) = plp (show n)
plp (LFlt f) = plp (show f)
instance PLPrint Symbol where
plp (SymCat n l) = plOper ":" (show n) (show l)
plp (SymLit n l) = plTerm "lit" [show n, show l]
plp (SymVar n l) = plTerm "var" [show n, show l]
plp (SymKS t) = plAtom t
plp (SymKP ts alts) = plTerm "pre" [plList (map plp ts), plList (map plAlt alts)]
where plAlt (ps,ts) = plOper "/" (plList (map plp ps)) (plList (map plAtom ts))
class PLPrint a where
plp :: a -> String
plps :: [a] -> String
plps = plList . map plp
instance PLPrint Char where
plp c = plAtom [c]
plps s = plAtom s
instance PLPrint a => PLPrint [a] where
plp = plps
----------------------------------------------------------------------
-- other prolog-printing functions
plCat :: Int -> String
plCat n = plAtom ('c' : show n)
plFun :: Int -> String
plFun n = plAtom ('f' : show n)
plSeq :: Int -> String
plSeq n = plAtom ('s' : show n)
plHeader :: String -> String
plHeader hdr = "%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%\n%% " ++ hdr ++ "\n"
plFacts :: CId -> String -> Int -> String -> [[String]] -> String
plFacts mod pred arity comment facts = "%% " ++ pred ++ comment ++++ clauses
where clauses = (if facts == [] then ":- dynamic " ++ pred ++ "/" ++ show arity ++ ".\n"
else unlines [mod' ++ plTerm pred args ++ "." | args <- facts])
mod' = if mod == wildCId then "" else plp mod ++ ": "
plTerm :: String -> [String] -> String
plTerm fun args = plAtom fun ++ prParenth (prTList ", " args)
plList :: [String] -> String
plList xs = prBracket (prTList "," xs)
plOper :: String -> String -> String -> String
plOper op a b = prParenth (a ++ op ++ b)
plVar :: String -> String
plVar = varPrefix . concatMap changeNonAlphaNum
where varPrefix var@(c:_) | isAsciiUpper c || c=='_' = var
| otherwise = "_" ++ var
changeNonAlphaNum c | isAlphaNumUnderscore c = [c]
| otherwise = "_" ++ show (ord c) ++ "_"
plAtom :: String -> String
plAtom "" = "''"
plAtom atom@(c:cs) | isAsciiLower c && all isAlphaNumUnderscore cs
|| c == '\'' && cs /= "" && last cs == '\'' = atom
| otherwise = "'" ++ changeQuote atom ++ "'"
where changeQuote ('\'':cs) = '\\' : '\'' : changeQuote cs
changeQuote ('\\':cs) = '\\' : '\\' : changeQuote cs
changeQuote (c:cs) = c : changeQuote cs
changeQuote "" = ""
isAlphaNumUnderscore :: Char -> Bool
isAlphaNumUnderscore c = (isAscii c && isAlphaNum c) || c == '_'
----------------------------------------------------------------------
-- prolog variables
createLogicalVariable :: Int -> CId
createLogicalVariable n = mkCId (logicalVariablePrefix ++ show n)
isLogicalVariable :: String -> Bool
isLogicalVariable = isPrefixOf logicalVariablePrefix
logicalVariablePrefix :: String
logicalVariablePrefix = "X"
----------------------------------------------------------------------
-- alpha convert variables to (unique) logical variables
-- * this is needed if we want to translate variables to Prolog variables
-- * used for abstract syntax, not concrete
-- * not (yet?) used for variables bound in pattern equations
type ConvertEnv = (Int, [(CId,CId)])
emptyEnv :: ConvertEnv
emptyEnv = (0, [])
class AlphaConvert a where
alphaConvert :: ConvertEnv -> a -> (ConvertEnv, a)
instance AlphaConvert a => AlphaConvert [a] where
alphaConvert env [] = (env, [])
alphaConvert env (a:as) = (env'', a':as')
where (env', a') = alphaConvert env a
(env'', as') = alphaConvert env' as
instance AlphaConvert Type where
alphaConvert env@(_,subst) (DTyp hypos cat args)
= ((ctr,subst), DTyp hypos' cat args')
where (env', hypos') = mapAccumL alphaConvertHypo env hypos
((ctr,_), args') = alphaConvert env' args
alphaConvertHypo env (b,x,typ) = ((ctr+1,(x,x'):subst), (b,x',typ'))
where ((ctr,subst), typ') = alphaConvert env typ
x' = createLogicalVariable ctr
instance AlphaConvert Expr where
alphaConvert (ctr,subst) (EAbs b x e) = ((ctr',subst), EAbs b x' e')
where ((ctr',_), e') = alphaConvert (ctr+1,(x,x'):subst) e
x' = createLogicalVariable ctr
alphaConvert env (EApp e1 e2) = (env'', EApp e1' e2')
where (env', e1') = alphaConvert env e1
(env'', e2') = alphaConvert env' e2
alphaConvert env expr@(EFun i) = (env, maybe expr EFun (lookup i (snd env)))
alphaConvert env expr = (env, expr)
-- pattern variables are not alpha converted
-- (but they probably should be...)
instance AlphaConvert Equation where
alphaConvert env@(_,subst) (Equ patterns result)
= ((ctr,subst), Equ patterns result')
where ((ctr,_), result') = alphaConvert env result

View File

@@ -0,0 +1,122 @@
----------------------------------------------------------------------
-- |
-- Module : PGFtoPython
-- Maintainer : Peter Ljunglöf
--
-- exports a GF grammar into a Python module
-----------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
module GF.Compile.PGFtoPython (pgf2python) where
import PGF(showCId)
import PGF.Internal as M
import GF.Data.Operations
import qualified Data.Array.IArray as Array
import qualified Data.Set as Set
import qualified Data.Map as Map
import qualified Data.IntMap as IntMap
--import Data.List (intersperse)
pgf2python :: PGF -> String
pgf2python pgf = ("# -*- coding: utf-8 -*-" ++++
"# This file was automatically generated by GF" +++++
showCId name +++ "=" +++
pyDict 1 pyStr id [
("flags", pyDict 2 pyCId pyLiteral (Map.assocs (gflags pgf))),
("abstract", pyDict 2 pyStr id [
("name", pyCId name),
("start", pyCId start),
("flags", pyDict 3 pyCId pyLiteral (Map.assocs (aflags abs))),
("funs", pyDict 3 pyCId pyAbsdef (Map.assocs (funs abs)))
]),
("concretes", pyDict 2 pyCId pyConcrete (Map.assocs cncs))
] ++ "\n")
where
name = absname pgf
start = M.lookStartCat pgf
abs = abstract pgf
cncs = concretes pgf
pyAbsdef :: (Type, Int, Maybe ([Equation], [[M.Instr]]), Double) -> String
pyAbsdef (typ, _, _, _) = pyTuple 0 id [pyCId cat, pyList 0 pyCId args]
where (args, cat) = M.catSkeleton typ
pyLiteral :: Literal -> String
pyLiteral (LStr s) = pyStr s
pyLiteral (LInt n) = show n
pyLiteral (LFlt d) = show d
pyConcrete :: Concr -> String
pyConcrete cnc = pyDict 3 pyStr id [
("flags", pyDict 0 pyCId pyLiteral (Map.assocs (cflags cnc))),
("printnames", pyDict 4 pyCId pyStr (Map.assocs (printnames cnc))),
("lindefs", pyDict 4 pyCat (pyList 0 pyFun) (IntMap.assocs (lindefs cnc))),
("productions", pyDict 4 pyCat pyProds (IntMap.assocs (productions cnc))),
("cncfuns", pyDict 4 pyFun pyCncFun (Array.assocs (cncfuns cnc))),
("sequences", pyDict 4 pySeq pySymbols (Array.assocs (sequences cnc))),
("cnccats", pyDict 4 pyCId pyCncCat (Map.assocs (cnccats cnc))),
("size", show (totalCats cnc))
]
where pyProds prods = pyList 5 pyProduction (Set.toList prods)
pyCncCat (CncCat start end _) = pyList 0 pyCat [start..end]
pyCncFun (CncFun f lins) = pyTuple 0 id [pyList 0 pySeq (Array.elems lins), pyCId f]
pySymbols syms = pyList 0 pySymbol (Array.elems syms)
pyProduction :: Production -> String
pyProduction (PCoerce arg) = pyTuple 0 id [pyStr "", pyList 0 pyCat [arg]]
pyProduction (PApply funid args) = pyTuple 0 id [pyFun funid, pyList 0 pyPArg args]
where pyPArg (PArg [] fid) = pyCat fid
pyPArg (PArg hypos fid) = pyTuple 0 pyCat (fid : map snd hypos)
pySymbol :: Symbol -> String
pySymbol (SymCat n l) = pyTuple 0 show [n, l]
pySymbol (SymLit n l) = pyDict 0 pyStr id [("lit", pyTuple 0 show [n, l])]
pySymbol (SymVar n l) = pyDict 0 pyStr id [("var", pyTuple 0 show [n, l])]
pySymbol (SymKS t) = pyStr t
pySymbol (SymKP ts alts) = pyDict 0 pyStr id [("pre", pyList 0 pySymbol ts), ("alts", pyList 0 alt2py alts)]
where alt2py (ps,ts) = pyTuple 0 (pyList 0 pyStr) [map pySymbol ps, ts]
pySymbol SymBIND = pyStr "&+"
pySymbol SymSOFT_BIND = pyStr "&+"
pySymbol SymSOFT_SPACE = pyStr "&+"
pySymbol SymCAPIT = pyStr "&|"
pySymbol SymALL_CAPIT = pyStr "&|"
pySymbol SymNE = pyDict 0 pyStr id [("nonExist", pyTuple 0 id [])]
----------------------------------------------------------------------
-- python helpers
pyDict :: Int -> (k -> String) -> (v -> String) -> [(k, v)] -> String
pyDict n pk pv [] = "{}"
pyDict n pk pv kvlist = prCurly (pyIndent n ++ prTList ("," ++ pyIndent n) (map pyKV kvlist) ++ pyIndent n)
where pyKV (k, v) = pk k ++ ":" ++ pv v
pyList :: Int -> (v -> String) -> [v] -> String
pyList n pv [] = "[]"
pyList n pv xs = prBracket (pyIndent n ++ prTList ("," ++ pyIndent n) (map pv xs) ++ pyIndent n)
pyTuple :: Int -> (v -> String) -> [v] -> String
pyTuple n pv [] = "()"
pyTuple n pv [x] = prParenth (pyIndent n ++ pv x ++ "," ++ pyIndent n)
pyTuple n pv xs = prParenth (pyIndent n ++ prTList ("," ++ pyIndent n) (map pv xs) ++ pyIndent n)
pyCat :: Int -> String
pyCat n = pyStr ('C' : show n)
pyFun :: Int -> String
pyFun n = pyStr ('F' : show n)
pySeq :: Int -> String
pySeq n = pyStr ('S' : show n)
pyStr :: String -> String
pyStr s = 'u' : prQuotedString s
pyCId :: CId -> String
pyCId = pyStr . showCId
pyIndent :: Int -> String
pyIndent n | n > 0 = "\n" ++ replicate n ' '
| otherwise = ""

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/05/30 18:39:44 $
-- > CVS $Date: 2005/05/30 18:39:44 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.19 $
--
@@ -23,9 +23,9 @@
-----------------------------------------------------------------------------
module GF.Compile.Rename (
renameSourceTerm,
renameModule
) where
renameSourceTerm,
renameModule
) where
import GF.Infra.Ident
import GF.Infra.CheckM
@@ -39,7 +39,6 @@ import GF.Data.Operations
import Control.Monad
import Data.List (nub,(\\))
import qualified Data.List as L
import qualified Data.Map as Map
import Data.Maybe(mapMaybe)
import GF.Text.Pretty
@@ -68,7 +67,7 @@ renameIdentTerm env = accumulateError (renameIdentTerm' env)
-- Fails immediately on error, makes it possible to try other possibilities
renameIdentTerm' :: Status -> Term -> Check Term
renameIdentTerm' env@(act,imps) t0 =
renameIdentTerm' env@(act,imps) t0 =
case t0 of
Vr c -> ident predefAbs c
Cn c -> ident (\_ s -> checkError s) c
@@ -85,8 +84,8 @@ renameIdentTerm' env@(act,imps) t0 =
_ -> return t0
where
opens = [st | (OSimple _,st) <- imps]
qualifs = [(m, st) | (OQualif m _, st) <- imps] ++
[(m, st) | (OQualif _ m, st) <- imps] ++
qualifs = [(m, st) | (OQualif m _, st) <- imps] ++
[(m, st) | (OQualif _ m, st) <- imps] ++
[(m, st) | (OSimple m, st) <- imps] -- qualif is always possible
-- this facility is mainly for BWC with GF1: you need not import PredefAbs
@@ -94,7 +93,7 @@ renameIdentTerm' env@(act,imps) t0 =
| isPredefCat c = return (Q (cPredefAbs,c))
| otherwise = checkError s
ident alt c =
ident alt c =
case Map.lookup c act of
Just f -> return (f c)
_ -> case mapMaybe (Map.lookup c) opens of
@@ -106,32 +105,13 @@ renameIdentTerm' env@(act,imps) t0 =
ts@(t:_) -> do checkWarn ("atomic term" <+> ppTerm Qualified 0 t0 $$
"conflict" <+> hsep (punctuate ',' (map (ppTerm Qualified 0) ts)) $$
"given" <+> fsep (punctuate ',' (map fst qualifs)))
return (bestTerm ts) -- Heuristic for resource grammar. Returns t for all others.
where
-- Hotfix for https://github.com/GrammaticalFramework/gf-core/issues/56
-- Real bug is probably somewhere deeper in recognising excluded functions. /IL 2020-06-06
notFromCommonModule :: Term -> Bool
notFromCommonModule term =
let t = render $ ppTerm Qualified 0 term :: String
in not $ any (\moduleName -> moduleName `L.isPrefixOf` t)
["CommonX", "ConstructX", "ExtendFunctor"
,"MarkHTMLX", "ParamX", "TenseX", "TextX"]
-- If one of the terms comes from the common modules,
-- we choose the other one, because that's defined in the grammar.
bestTerm :: [Term] -> Term
bestTerm [] = error "constant not found" -- not reached: bestTerm is only called for case ts@(t:_)
bestTerm ts@(t:_) =
let notCommon = [t | t <- ts, notFromCommonModule t]
in case notCommon of
[] -> t -- All terms are from common modules, return first of original list
(u:_) -> u -- ≥1 terms are not from common modules, return first of those
return t
info2status :: Maybe ModuleName -> Ident -> Info -> StatusInfo
info2status mq c i = case i of
AbsFun _ _ Nothing _ -> maybe Con (curry QC) mq
ResValue _ _ -> maybe Con (curry QC) mq
ResParam _ _ -> maybe Con (curry QC) mq
ResValue _ -> maybe Con (curry QC) mq
ResParam _ _ -> maybe Con (curry QC) mq
AnyInd True m -> maybe Con (const (curry QC m)) mq
AnyInd False m -> maybe Cn (const (curry Q m)) mq
_ -> maybe Cn (curry Q) mq
@@ -157,7 +137,7 @@ modInfo2status (o,mo) = (o,tree2status o (jments mo))
self2status :: ModuleName -> ModuleInfo -> StatusMap
self2status c m = Map.mapWithKey (info2status (Just c)) (jments m)
renameInfo :: FilePath -> Status -> Module -> Ident -> Info -> Check Info
renameInfo cwd status (m,mi) i info =
case info of
@@ -168,9 +148,9 @@ renameInfo cwd status (m,mi) i info =
ResParam (Just pp) m -> do
pp' <- renLoc (mapM (renParam status)) pp
return (ResParam (Just pp') m)
ResValue t i -> do
ResValue t -> do
t <- renLoc (renameTerm status []) t
return (ResValue t i)
return (ResValue t)
CncCat mcat mdef mref mpr mpmcfg -> liftM5 CncCat (renTerm mcat) (renTerm mdef) (renTerm mref) (renTerm mpr) (return mpmcfg)
CncFun mty mtr mpr mpmcfg -> liftM3 (CncFun mty) (renTerm mtr) (renTerm mpr) (return mpmcfg)
_ -> return info
@@ -208,7 +188,7 @@ renameTerm env vars = ren vars where
Abs b x t -> liftM (Abs b x) (ren (x:vs) t)
Prod bt x a b -> liftM2 (Prod bt x) (ren vs a) (ren (x:vs) b)
Typed a b -> liftM2 Typed (ren vs a) (ren vs b)
Vr x
Vr x
| elem x vs -> return trm
| otherwise -> renid trm
Cn _ -> renid trm
@@ -219,7 +199,7 @@ renameTerm env vars = ren vars where
i' <- case i of
TTyped ty -> liftM TTyped $ ren vs ty -- the only annotation in source
_ -> return i
liftM (T i') $ mapM (renCase vs) cs
liftM (T i') $ mapM (renCase vs) cs
Let (x,(m,a)) b -> do
m' <- case m of
@@ -229,7 +209,7 @@ renameTerm env vars = ren vars where
b' <- ren (x:vs) b
return $ Let (x,(m',a')) b'
P t@(Vr r) l -- Here we have $r.l$ and this is ambiguous it could be either
P t@(Vr r) l -- Here we have $r.l$ and this is ambiguous it could be either
-- record projection from variable or constant $r$ or qualified expression with module $r$
| elem r vs -> return trm -- try var proj first ..
| otherwise -> checks [ renid' (Q (MN r,label2ident l)) -- .. and qualified expression second.
@@ -237,9 +217,9 @@ renameTerm env vars = ren vars where
, checkError ("unknown qualified constant" <+> trm)
]
EPatt minp maxp p -> do
EPatt p -> do
(p',_) <- renpatt p
return $ EPatt minp maxp p'
return $ EPatt p'
_ -> composOp (ren vs) trm
@@ -306,14 +286,14 @@ renamePattern env patt =
(q',ws) <- renp q
return (PAlt p' q', vs ++ ws)
PSeq minp maxp p minq maxq q -> do
PSeq p q -> do
(p',vs) <- renp p
(q',ws) <- renp q
return (PSeq minp maxp p' minq maxq q', vs ++ ws)
return (PSeq p' q', vs ++ ws)
PRep minp maxp p -> do
PRep p -> do
(p',vs) <- renp p
return (PRep minp maxp p', vs)
return (PRep p', vs)
PNeg p -> do
(p',vs) <- renp p
@@ -331,7 +311,7 @@ renamePattern env patt =
renameContext :: Status -> Context -> Check Context
renameContext b = renc [] where
renc vs cont = case cont of
(bt,x,t) : xts
(bt,x,t) : xts
| isWildIdent x -> do
t' <- ren vs t
xts' <- renc vs xts

View File

@@ -31,7 +31,7 @@ getLocalTags x (m,mi) =
getLocations (AbsFun mb_type _ mb_eqs _) = maybe (ltype "fun") mb_type ++
maybe (list (loc "def")) mb_eqs
getLocations (ResParam mb_params _) = maybe (loc "param") mb_params
getLocations (ResValue mb_type _) = ltype "param-value" mb_type
getLocations (ResValue mb_type) = ltype "param-value" mb_type
getLocations (ResOper mb_type mb_def) = maybe (ltype "oper-type") mb_type ++
maybe (loc "oper-def") mb_def
getLocations (ResOverload _ defs) = list (\(x,y) -> ltype "overload-type" x ++

View File

@@ -2,7 +2,8 @@ module GF.Compile.ToAPI
(stringToAPI,exprToAPI)
where
import PGF2
import PGF.Internal
import PGF(showCId)
import Data.Maybe
--import System.IO
--import Control.Monad
@@ -46,12 +47,12 @@ exprToFunc :: Expr -> APIfunc
exprToFunc expr =
case unApp expr of
Just (cid,l) ->
case Map.lookup cid syntaxFuncs of
case Map.lookup (showCId cid) syntaxFuncs of
Just sig -> mkAPI True (fst sig,expr)
_ -> case l of
[] -> BasicFunc cid
[] -> BasicFunc (showCId cid)
_ -> let es = map exprToFunc l
in AppFunc cid es
in AppFunc (showCId cid) es
_ -> BasicFunc (showExpr [] expr)
@@ -68,8 +69,8 @@ mkAPI opt (ty,expr) =
where
rephraseSentence ty expr =
case unApp expr of
Just (cid,es) -> if isPrefixOf "Use" cid then
let newCat = drop 3 cid
Just (cid,es) -> if isPrefixOf "Use" (showCId cid) then
let newCat = drop 3 (showCId cid)
afClause = mkAPI True (newCat, es !! 2)
afPol = mkAPI True ("Pol",es !! 1)
lTense = mkAPI True ("Temp", head es)
@@ -97,9 +98,9 @@ mkAPI opt (ty,expr) =
computeAPI :: (String,Expr) -> APIfunc
computeAPI (ty,expr) =
case (unApp expr) of
Just (cid,[]) -> getSimpCat cid ty
Just (cid,[]) -> getSimpCat (showCId cid) ty
Just (cid,es) ->
let p = specFunction cid es
let p = specFunction (showCId cid) es
in if isJust p then fromJust p
else case Map.lookup (show cid) syntaxFuncs of
Nothing -> exprToFunc expr
@@ -146,23 +147,23 @@ optimize expr = optimizeNP expr
optimizeNP expr =
case unApp expr of
Just (cid,es) ->
if cid == "MassNP" then let afs = nounAsCN (head es)
in AppFunc "mkNP" [afs]
else if cid == "DetCN" then let quants = quantAsDet (head es)
ns = nounAsCN (head $ tail es)
in AppFunc "mkNP" (quants ++ [ns])
if showCId cid == "MassNP" then let afs = nounAsCN (head es)
in AppFunc "mkNP" [afs]
else if showCId cid == "DetCN" then let quants = quantAsDet (head es)
ns = nounAsCN (head $ tail es)
in AppFunc "mkNP" (quants ++ [ns])
else mkAPI False ("NP",expr)
_ -> error $ "incorrect expression " ++ (showExpr [] expr)
where
nounAsCN expr =
case unApp expr of
Just (cid,es) -> if cid == "UseN" then (mkAPI False) ("N",head es)
Just (cid,es) -> if showCId cid == "UseN" then (mkAPI False) ("N",head es)
else (mkAPI False) ("CN",expr)
_ -> error $ "incorrect expression "++ (showExpr [] expr)
quantAsDet expr =
case unApp expr of
Just (cid,es) -> if cid == "DetQuant" then map (mkAPI False) [("Quant", head es),("Num",head $ tail es)]
Just (cid,es) -> if showCId cid == "DetQuant" then map (mkAPI False) [("Quant", head es),("Num",head $ tail es)]
else [mkAPI False ("Det",expr)]
_ -> error $ "incorrect expression "++ (showExpr [] expr)

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/09/15 16:22:02 $
-- > CVS $Date: 2005/09/15 16:22:02 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.16 $
--
@@ -13,11 +13,11 @@
-----------------------------------------------------------------------------
module GF.Compile.TypeCheck.Abstract (-- * top-level type checking functions; TC should not be called directly.
checkContext,
checkTyp,
checkDef,
checkConstrs,
) where
checkContext,
checkTyp,
checkDef,
checkConstrs,
) where
import GF.Data.Operations
@@ -33,8 +33,8 @@ import GF.Text.Pretty
--import Control.Monad (foldM, liftM, liftM2)
-- | invariant way of creating TCEnv from context
initTCEnv gamma =
(length gamma,[(x,VGen i x) | ((x,_),i) <- zip gamma [0..]], gamma)
initTCEnv gamma =
(length gamma,[(x,VGen i x) | ((x,_),i) <- zip gamma [0..]], gamma)
-- interface to TC type checker

View File

@@ -1,7 +1,6 @@
{-# LANGUAGE PatternGuards #-}
module GF.Compile.TypeCheck.Concrete( checkLType, inferLType, computeLType, ppType ) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
module GF.Compile.TypeCheck.Concrete( {-checkLType, inferLType, computeLType, ppType-} ) where
{-
import GF.Infra.CheckM
import GF.Data.Operations
@@ -13,7 +12,6 @@ import GF.Grammar.Lockfield (isLockLabel, lockRecType, unlockRecord)
import GF.Compile.TypeCheck.Primitives
import Data.List
import Data.Maybe(fromMaybe)
import Control.Monad
import GF.Text.Pretty
@@ -24,16 +22,10 @@ computeLType gr g0 t = comp (reverse [(b,x, Vr x) | (b,x,_) <- g0] ++ g0) t
_ | Just _ <- isTypeInts ty -> return ty ---- shouldn't be needed
| isPredefConstant ty -> return ty ---- shouldn't be needed
Q (m,ident) -> checkIn ("module" <+> m) $ do
Q (m,ident) -> checkIn (text "module" <+> ppIdent m) $ do
ty' <- lookupResDef gr (m,ident)
if ty' == ty then return ty else comp g ty' --- is this necessary to test?
AdHocOverload ts -> do
over <- getOverload gr g (Just typeType) t
case over of
Just (tr,_) -> return tr
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 t)
Vr ident -> checkLookup ident g -- never needed to compute!
App f a -> do
@@ -70,6 +62,7 @@ computeLType gr g0 t = comp (reverse [(b,x, Vr x) | (b,x,_) <- g0] ++ g0) t
lockRecType c t' ---- locking to be removed AR 20/6/2009
_ | ty == typeTok -> return typeStr
_ | isPredefConstant ty -> return ty
_ -> composOp (comp g) ty
@@ -80,26 +73,26 @@ inferLType gr g trm = case trm of
Q (m,ident) | isPredef m -> termWith trm $ case typPredefined ident of
Just ty -> return ty
Nothing -> checkError ("unknown in Predef:" <+> ident)
Nothing -> checkError (text "unknown in Predef:" <+> ppIdent ident)
Q ident -> checks [
termWith trm $ lookupResType gr ident >>= computeLType gr g
,
lookupResDef gr ident >>= inferLType gr g
,
checkError ("cannot infer type of constant" <+> ppTerm Unqualified 0 trm)
checkError (text "cannot infer type of constant" <+> ppTerm Unqualified 0 trm)
]
QC (m,ident) | isPredef m -> termWith trm $ case typPredefined ident of
Just ty -> return ty
Nothing -> checkError ("unknown in Predef:" <+> ident)
Nothing -> checkError (text "unknown in Predef:" <+> ppIdent ident)
QC ident -> checks [
termWith trm $ lookupResType gr ident >>= computeLType gr g
,
lookupResDef gr ident >>= inferLType gr g
,
checkError ("cannot infer type of canonical constant" <+> ppTerm Unqualified 0 trm)
checkError (text "cannot infer type of canonical constant" <+> ppTerm Unqualified 0 trm)
]
Vr ident -> termWith trm $ checkLookup ident g
@@ -107,12 +100,7 @@ inferLType gr g trm = case trm of
Typed e t -> do
t' <- computeLType gr g t
checkLType gr g e t'
AdHocOverload ts -> do
over <- getOverload gr g Nothing trm
case over of
Just trty -> return trty
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 trm)
return (e,t')
App f a -> do
over <- getOverload gr g Nothing trm
@@ -122,17 +110,13 @@ inferLType gr g trm = case trm of
(f',fty) <- inferLType gr g f
fty' <- computeLType gr g fty
case fty' of
Prod bt z arg val -> do
Prod bt z arg val -> do
a' <- justCheck g a arg
ty <- if isWildIdent z
ty <- if isWildIdent z
then return val
else substituteLType [(bt,z,a')] val
return (App f' a',ty)
_ ->
let term = ppTerm Unqualified 0 f
funName = pp . head . words .render $ term
in checkError ("A function type is expected for" <+> term <+> "instead of type" <+> ppType fty $$
"\n ** Maybe you gave too many arguments to" <+> funName <+> "\n")
return (App f' a',ty)
_ -> checkError (text "A function type is expected for" <+> ppTerm Unqualified 0 f <+> text "instead of type" <+> ppType fty)
S f x -> do
(f', fty) <- inferLType gr g f
@@ -140,7 +124,7 @@ inferLType gr g trm = case trm of
Table arg val -> do
x'<- justCheck g x arg
return (S f' x', val)
_ -> checkError ("table lintype expected for the table in" $$ nest 2 (ppTerm Unqualified 0 trm))
_ -> checkError (text "table lintype expected for the table in" $$ nest 2 (ppTerm Unqualified 0 trm))
P t i -> do
(t',ty) <- inferLType gr g t --- ??
@@ -148,16 +132,16 @@ inferLType gr g trm = case trm of
let tr2 = P t' i
termWith tr2 $ case ty' of
RecType ts -> case lookup i ts of
Nothing -> checkError ("unknown label" <+> i <+> "in" $$ nest 2 (ppTerm Unqualified 0 ty'))
Nothing -> checkError (text "unknown label" <+> ppLabel i <+> text "in" $$ nest 2 (ppTerm Unqualified 0 ty'))
Just x -> return x
_ -> checkError ("record type expected for:" <+> ppTerm Unqualified 0 t $$
" instead of the inferred:" <+> ppTerm Unqualified 0 ty')
_ -> checkError (text "record type expected for:" <+> ppTerm Unqualified 0 t $$
text " instead of the inferred:" <+> ppTerm Unqualified 0 ty')
R r -> do
let (ls,fs) = unzip r
fsts <- mapM inferM fs
let ts = [ty | (Just ty,_) <- fsts]
checkCond ("cannot infer type of record" $$ nest 2 (ppTerm Unqualified 0 trm)) (length ts == length fsts)
checkCond (text "cannot infer type of record" $$ nest 2 (ppTerm Unqualified 0 trm)) (length ts == length fsts)
return $ (R (zip ls fsts), RecType (zip ls ts))
T (TTyped arg) pts -> do
@@ -168,10 +152,10 @@ inferLType gr g trm = case trm of
checkLType gr g trm (Table arg val)
T ti pts -> do -- tries to guess: good in oper type inference
let pts' = [pt | pt@(p,_) <- pts, isConstPatt p]
case pts' of
[] -> checkError ("cannot infer table type of" <+> ppTerm Unqualified 0 trm)
---- PInt k : _ -> return $ Ints $ max [i | PInt i <- pts']
_ -> do
case pts' of
[] -> checkError (text "cannot infer table type of" <+> ppTerm Unqualified 0 trm)
---- PInt k : _ -> return $ Ints $ max [i | PInt i <- pts']
_ -> do
(arg,val) <- checks $ map (inferCase Nothing) pts'
checkLType gr g trm (Table arg val)
V arg pts -> do
@@ -182,9 +166,9 @@ inferLType gr g trm = case trm of
K s -> do
if elem ' ' s
then do
let ss = foldr C Empty (map K (words s))
let ss = foldr C Empty (map K (words s))
----- removed irritating warning AR 24/5/2008
----- checkWarn ("token \"" ++ s ++
----- checkWarn ("token \"" ++ s ++
----- "\" converted to token list" ++ prt ss)
return (ss, typeStr)
else return (trm, typeStr)
@@ -195,56 +179,50 @@ inferLType gr g trm = case trm of
Empty -> return (trm, typeStr)
C s1 s2 ->
C s1 s2 ->
check2 (flip (justCheck g) typeStr) C s1 s2 typeStr
Glue s1 s2 ->
Glue s1 s2 ->
check2 (flip (justCheck g) typeStr) Glue s1 s2 typeStr ---- typeTok
---- hack from Rename.identRenameTerm, to live with files with naming conflicts 18/6/2007
Strs (Cn c : ts) | c == cConflict -> do
checkWarn ("unresolved constant, could be any of" <+> hcat (map (ppTerm Unqualified 0) ts))
checkWarn (text "unresolved constant, could be any of" <+> hcat (map (ppTerm Unqualified 0) ts))
inferLType gr g (head ts)
Strs ts -> do
ts' <- mapM (\t -> justCheck g t typeStr) ts
ts' <- mapM (\t -> justCheck g t typeStr) ts
return (Strs ts', typeStrs)
Alts t aa -> do
t' <- justCheck g t typeStr
aa' <- flip mapM aa (\ (c,v) -> do
c' <- justCheck g c typeStr
c' <- justCheck g c typeStr
v' <- checks $ map (justCheck g v) [typeStrs, EPattType typeStr]
return (c',v'))
return (Alts t' aa', typeStr)
RecType r -> do
let (ls,ts) = unzip r
ts' <- mapM (flip (justCheck g) typeType) ts
ts' <- mapM (flip (justCheck g) typeType) ts
return (RecType (zip ls ts'), typeType)
ExtR r s -> do
--- over <- getOverload gr g Nothing r
--- let r1 = maybe r fst over
let r1 = r ---
(r',rT) <- inferLType gr g r1
(r',rT) <- inferLType gr g r
rT' <- computeLType gr g rT
(s',sT) <- inferLType gr g s
sT' <- computeLType gr g sT
let trm' = ExtR r' s'
---- trm' <- plusRecord r' s'
case (rT', sT') of
(RecType rs, RecType ss) -> do
let rt = RecType ([field | field@(l,_) <- rs, notElem l (map fst ss)] ++ ss) -- select types of later fields
rt <- plusRecType rT' sT'
checkLType gr g trm' rt ---- return (trm', rt)
_ | rT' == typeType && sT' == typeType -> do
return (trm', typeType)
_ -> checkError ("records or record types expected in" <+> ppTerm Unqualified 0 trm)
_ | rT' == typeType && sT' == typeType -> return (trm', typeType)
_ -> checkError (text "records or record types expected in" <+> ppTerm Unqualified 0 trm)
Sort _ ->
Sort _ ->
termWith trm $ return typeType
Prod bt x a b -> do
@@ -253,7 +231,7 @@ inferLType gr g trm = case trm of
return (Prod bt x a' b', typeType)
Table p t -> do
p' <- justCheck g p typeType --- check p partype!
p' <- justCheck g p typeType --- check p partype!
t' <- justCheck g t typeType
return $ (Table p' t', typeType)
@@ -265,17 +243,16 @@ inferLType gr g trm = case trm of
EPattType ty -> do
ty' <- justCheck g ty typeType
return (EPattType ty',typeType)
EPatt _ _ p -> do
EPatt p -> do
ty <- inferPatt p
let (minp,maxp,p') = measurePatt gr p
return (EPatt minp maxp p', EPattType ty)
return (trm, EPattType ty)
ELin c trm -> do
(trm',ty) <- inferLType gr g trm
ty' <- lockRecType c ty ---- lookup c; remove lock AR 20/6/2009
return $ (ELin c trm', ty')
return $ (ELin c trm', ty')
_ -> checkError ("cannot infer lintype of" <+> ppTerm Unqualified 0 trm)
_ -> checkError (text "cannot infer lintype of" <+> ppTerm Unqualified 0 trm)
where
isPredef m = elem m [cPredef,cPredefAbs]
@@ -292,7 +269,7 @@ inferLType gr g trm = case trm of
inferCase mty (patt,term) = do
arg <- maybe (inferPatt patt) return mty
cont <- pattContext gr g arg patt
(term',val) <- inferLType gr (reverse cont ++ g) term
(_,val) <- inferLType gr (reverse cont ++ g) term
return (arg,val)
isConstPatt p = case p of
PC _ ps -> True --- all isConstPatt ps
@@ -304,9 +281,9 @@ inferLType gr g trm = case trm of
PFloat _ -> True
PChar -> True
PChars _ -> True
PSeq _ _ p _ _ q -> isConstPatt p && isConstPatt q
PSeq p q -> isConstPatt p && isConstPatt q
PAlt p q -> isConstPatt p && isConstPatt q
PRep _ _ p -> isConstPatt p
PRep p -> isConstPatt p
PNeg p -> isConstPatt p
PAs _ p -> isConstPatt p
_ -> False
@@ -316,43 +293,12 @@ inferLType gr g trm = case trm of
PAs _ p -> inferPatt p
PNeg p -> inferPatt p
PAlt p q -> checks [inferPatt p, inferPatt q]
PSeq _ _ _ _ _ _ -> return $ typeStr
PRep _ _ _ -> return $ typeStr
PSeq _ _ -> return $ typeStr
PRep _ -> return $ typeStr
PChar -> return $ typeStr
PChars _ -> return $ typeStr
_ -> inferLType gr g (patt2term p) >>= return . snd
measurePatt gr p =
case p of
PM q -> case lookupResDef gr q of
Ok t -> case t of
EPatt minp maxp _ -> (minp,maxp,p)
_ -> error "Expected pattern macro"
Bad msg -> error msg
PR ass -> let p' = PR (map (\(lbl,p) -> let (_,_,p') = measurePatt gr p in (lbl,p')) ass)
in (0,Nothing,p')
PString s -> let len=length s
in (len,Just len,p)
PT t p -> let (min,max,p') = measurePatt gr p
in (min,max,PT t p')
PAs x p -> let (min,max,p') = measurePatt gr p
in (min,max,PAs x p')
PImplArg p -> let (min,max,p') = measurePatt gr p
in (min,max,PImplArg p')
PNeg p -> let (_,_,p') = measurePatt gr p
in (0,Nothing,PNeg p')
PAlt p1 p2 -> let (min1,max1,p1') = measurePatt gr p1
(min2,max2,p2') = measurePatt gr p2
in (min min1 min2,liftM2 max max1 max2,PAlt p1' p2')
PSeq _ _ p1 _ _ p2
-> let (min1,max1,p1') = measurePatt gr p1
(min2,max2,p2') = measurePatt gr p2
in (min1+min2,liftM2 (+) max1 max2,PSeq min1 max1 p1' min2 max2 p2')
PRep _ _ p -> let (minp,maxp,p') = measurePatt gr p
in (0,Nothing,PRep minp maxp p')
PChar -> (1,Just 1,p)
PChars _ -> (1,Just 1,p)
_ -> (0,Nothing,p)
-- type inference: Nothing, type checking: Just t
-- the latter permits matching with value type
@@ -364,28 +310,15 @@ getOverload gr g mt ot = case appForm ot of
v <- matchOverload f typs ttys
return $ Just v
_ -> return Nothing
(AdHocOverload cs@(f:_), ts) -> do --- the function name f is only used in error messages
let typs = concatMap collectOverloads cs
ttys <- mapM (inferLType gr g) ts
v <- matchOverload f typs ttys
return $ Just v
_ -> return Nothing
where
collectOverloads tr@(Q c) = case lookupOverload gr c of
Ok typs -> typs
_ -> case lookupResType gr c of
Ok ty -> let (args,val) = typeFormCnc ty in [(map (\(b,x,t) -> t) args,(val,tr))]
_ -> []
collectOverloads _ = [] --- constructors QC
matchOverload f typs ttys = do
let (tts,tys) = unzip ttys
let vfs = lookupOverloadInstance tys typs
let matches = [vf | vf@((_,v,_),_) <- vfs, matchVal mt v]
let showTypes ty = hsep (map ppType ty)
let (stys,styps) = (showTypes tys, [showTypes ty | (ty,_) <- typs])
-- to avoid strange error msg e.g. in case of unmatch record extension, show whole types if needed AR 28/1/2013
@@ -396,57 +329,50 @@ getOverload gr g mt ot = case appForm ot of
case ([vf | (vf,True) <- matches],[vf | (vf,False) <- matches]) of
([(_,val,fun)],_) -> return (mkApp fun tts, val)
([],[(pre,val,fun)]) -> do
checkWarn $ "ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot $$
"for" $$
checkWarn $ text "ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot $$
text "for" $$
nest 2 (showTypes tys) $$
"using" $$
text "using" $$
nest 2 (showTypes pre)
return (mkApp fun tts, val)
([],[]) -> do
checkError $ "no overload instance of" <+> ppTerm Qualified 0 f $$
maybe empty (\x -> "with value type" <+> ppType x) mt $$
"for argument list" $$
checkError $ text "no overload instance of" <+> ppTerm Unqualified 0 f $$
text "for" $$
nest 2 stysError $$
"among alternatives" $$
nest 2 (vcat stypsError)
text "among" $$
nest 2 (vcat stypsError) $$
maybe empty (\x -> text "with value type" <+> ppType x) mt
(vfs1,vfs2) -> case (noProds vfs1,noProds vfs2) of
([(val,fun)],_) -> do
return (mkApp fun tts, val)
([],[(val,fun)]) -> do
checkWarn ("ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot)
checkWarn (text "ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot)
return (mkApp fun tts, val)
----- unsafely exclude irritating warning AR 24/5/2008
----- checkWarn $ "overloading of" +++ prt f +++
----- checkWarn $ "overloading of" +++ prt f +++
----- "resolved by excluding partial applications:" ++++
----- unlines [prtType env ty | (ty,_) <- vfs', not (noProd ty)]
--- now forgiving ambiguity with a warning AR 1/2/2014
-- This gives ad hoc overloading the same behaviour as the choice of the first match in renaming did before.
-- But it also gives a chance to ambiguous overloadings that were banned before.
(nps1,nps2) -> do
checkWarn $ "ambiguous overloading of" <+> ppTerm Unqualified 0 f <+>
---- "with argument types" <+> hsep (map (ppTerm Qualified 0) tys) $$
"resolved by selecting the first of the alternatives" $$
nest 2 (vcat [ppTerm Qualified 0 fun | (_,ty,fun) <- vfs1 ++ if null vfs1 then vfs2 else []])
case [(mkApp fun tts,val) | (val,fun) <- nps1 ++ nps2] of
[] -> checkError $ "no alternatives left when resolving" <+> ppTerm Unqualified 0 f
h:_ -> return h
_ -> checkError $ text "ambiguous overloading of" <+> ppTerm Unqualified 0 f <+>
text "for" <+> hsep (map ppType tys) $$
text "with alternatives" $$
nest 2 (vcat [ppType ty | (_,ty,_) <- if null vfs1 then vfs2 else vfs2])
matchVal mt v = elem mt [Nothing,Just v,Just (unlocked v)]
unlocked v = case v of
RecType fs -> RecType $ filter (not . isLockLabel . fst) (sortRec fs)
RecType fs -> RecType $ filter (not . isLockLabel . fst) fs
_ -> v
---- TODO: accept subtypes
---- TODO: use a trie
lookupOverloadInstance tys typs =
[((pre,mkFunType rest val, t),isExact) |
lookupOverloadInstance tys typs =
[((pre,mkFunType rest val, t),isExact) |
let lt = length tys,
(ty,(val,t)) <- typs, length ty >= lt,
let (pre,rest) = splitAt lt ty,
let (pre,rest) = splitAt lt ty,
let isExact = pre == tys,
isExact || map unlocked pre == map unlocked tys
]
@@ -459,21 +385,20 @@ getOverload gr g mt ot = case appForm ot of
checkLType :: SourceGrammar -> Context -> Term -> Type -> Check (Term, Type)
checkLType gr g trm typ0 = do
typ <- computeLType gr g typ0
case trm of
Abs bt x c -> do
case typ of
Prod bt' z a b -> do
Prod bt' z a b -> do
(c',b') <- if isWildIdent z
then checkLType gr ((bt,x,a):g) c b
else do b' <- checkIn (pp "abs") $ substituteLType [(bt',z,Vr x)] b
else do b' <- checkIn (text "abs") $ substituteLType [(bt',z,Vr x)] b
checkLType gr ((bt,x,a):g) c b'
return $ (Abs bt x c', Prod bt' z a b')
_ -> checkError $ "function type expected instead of" <+> ppType typ $$
"\n ** Double-check that the type signature of the operation" $$
"matches the number of arguments given to it.\n"
return $ (Abs bt x c', Prod bt' x a b')
_ -> checkError $ text "function type expected instead of" <+> ppType typ
App f a -> do
over <- getOverload gr g (Just typ) trm
@@ -483,12 +408,6 @@ checkLType gr g trm typ0 = do
(trm',ty') <- inferLType gr g trm
termWith trm' $ checkEqLType gr g typ ty' trm'
AdHocOverload ts -> do
over <- getOverload gr g Nothing trm
case over of
Just trty -> return trty
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 trm)
Q _ -> do
over <- getOverload gr g (Just typ) trm
case over of
@@ -498,21 +417,21 @@ checkLType gr g trm typ0 = do
termWith trm' $ checkEqLType gr g typ ty' trm'
T _ [] ->
checkError ("found empty table in type" <+> ppTerm Unqualified 0 typ)
T _ cs -> case typ of
Table arg val -> do
checkError (text "found empty table in type" <+> ppTerm Unqualified 0 typ)
T _ cs -> case typ of
Table arg val -> do
case allParamValues gr arg of
Ok vs -> do
let ps0 = map fst cs
ps <- testOvershadow ps0 vs
if null ps
then return ()
else checkWarn ("patterns never reached:" $$
if null ps
then return ()
else checkWarn (text "patterns never reached:" $$
nest 2 (vcat (map (ppPatt Unqualified 0) ps)))
_ -> return () -- happens with variable types
cs' <- mapM (checkCase arg val) cs
return (T (TTyped arg) cs', typ)
_ -> checkError $ "table type expected for table instead of" $$ nest 2 (ppType typ)
_ -> checkError $ text "table type expected for table instead of" $$ nest 2 (ppType typ)
V arg0 vs ->
case typ of
Table arg1 val ->
@@ -520,54 +439,51 @@ checkLType gr g trm typ0 = do
vs1 <- allParamValues gr arg1
if length vs1 == length vs
then return ()
else checkError $ "wrong number of values in table" <+> ppTerm Unqualified 0 trm
else checkError $ text "wrong number of values in table" <+> ppTerm Unqualified 0 trm
vs' <- map fst `fmap` sequence [checkLType gr g v val|v<-vs]
return (V arg' vs',typ)
R r -> case typ of --- why needed? because inference may be too difficult
RecType rr -> do
--let (ls,_) = unzip rr -- labels of expected type
let (ls,_) = unzip rr -- labels of expected type
fsts <- mapM (checkM r) rr -- check that they are found in the record
return $ (R fsts, typ) -- normalize record
_ -> checkError ("record type expected in type checking instead of" $$ nest 2 (ppTerm Unqualified 0 typ))
_ -> checkError (text "record type expected in type checking instead of" $$ nest 2 (ppTerm Unqualified 0 typ))
ExtR r s -> case typ of
_ | typ == typeType -> do
trm' <- computeLType gr g trm
case trm' of
RecType _ -> termWith trm' $ return typeType
ExtR (Vr _) (RecType _) -> termWith trm' $ return typeType
RecType _ -> termWith trm $ return typeType
ExtR (Vr _) (RecType _) -> termWith trm $ return typeType
-- ext t = t ** ...
_ -> checkError ("invalid record type extension" <+> nest 2 (ppTerm Unqualified 0 trm))
_ -> checkError (text "invalid record type extension" <+> nest 2 (ppTerm Unqualified 0 trm))
RecType rr -> do
(r',ty,s') <- checks [
do (r',ty) <- inferLType gr g r
return (r',ty,s)
,
do (s',ty) <- inferLType gr g s
return (s',ty,r)
]
ll2 <- case s of
R ss -> return $ map fst ss
_ -> do
(s',typ2) <- inferLType gr g s
case typ2 of
RecType ss -> return $ map fst ss
_ -> checkError ("cannot get labels from" $$ nest 2 (ppTerm Unqualified 0 typ2))
let ll1 = [l | (l,_) <- rr, notElem l ll2]
--- over <- getOverload gr g Nothing r --- this would solve #66 but fail ParadigmsAra. AR 6/7/2020
--- let r1 = maybe r fst over
let r1 = r ---
(r',_) <- checkLType gr g r1 (RecType [field | field@(l,_) <- rr, elem l ll1])
(s',_) <- checkLType gr g s (RecType [field | field@(l,_) <- rr, elem l ll2])
let rec = R ([(l,(Nothing,P r' l)) | l <- ll1] ++ [(l,(Nothing,P s' l)) | l <- ll2])
return (rec, typ)
case ty of
RecType rr1 -> do
let (rr0,rr2) = recParts rr rr1
r2 <- justCheck g r' rr0
s2 <- justCheck g s' rr2
return $ (ExtR r2 s2, typ)
_ -> checkError (text "record type expected in extension of" <+> ppTerm Unqualified 0 r $$
text "but found" <+> ppTerm Unqualified 0 ty)
ExtR ty ex -> do
r' <- justCheck g r ty
s' <- justCheck g s ex
return $ (ExtR r' s', typ) --- is this all? it assumes the same division in trm and typ
_ -> checkError ("record extension not meaningful for" <+> ppTerm Unqualified 0 typ)
_ -> checkError (text "record extension not meaningful for" <+> ppTerm Unqualified 0 typ)
FV vs -> do
ttys <- mapM (flip (checkLType gr g) typ) vs
@@ -582,7 +498,7 @@ checkLType gr g trm typ0 = do
(arg',val) <- checkLType gr g arg p
checkEqLType gr g typ t trm
return (S tab' arg', t)
_ -> checkError ("table type expected for applied table instead of" <+> ppType ty')
_ -> checkError (text "table type expected for applied table instead of" <+> ppType ty')
, do
(arg',ty) <- inferLType gr g arg
ty' <- computeLType gr g ty
@@ -591,8 +507,7 @@ checkLType gr g trm typ0 = do
]
Let (x,(mty,def)) body -> case mty of
Just ty -> do
(ty0,_) <- checkLType gr g ty typeType
(def',ty') <- checkLType gr g def ty0
(def',ty') <- checkLType gr g def ty
body' <- justCheck ((Explicit,x,ty'):g) body typ
return (Let (x,(Just ty',def')) body', typ)
_ -> do
@@ -608,10 +523,10 @@ checkLType gr g trm typ0 = do
termWith trm' $ checkEqLType gr g typ ty' trm'
where
justCheck g ty te = checkLType gr g ty te >>= return . fst
{-
recParts rr t = (RecType rr1,RecType rr2) where
(rr1,rr2) = partition (flip elem (map fst t) . fst) rr
-}
recParts rr t = (RecType rr1,RecType rr2) where
(rr1,rr2) = partition (flip elem (map fst t) . fst) rr
checkM rms (l,ty) = case lookup l rms of
Just (Just ty0,t) -> do
checkEqLType gr g ty ty0 t
@@ -620,18 +535,17 @@ checkLType gr g trm typ0 = do
Just (_,t) -> do
(t',ty') <- checkLType gr g t ty
return (l,(Just ty',t'))
_ -> checkError $
if isLockLabel l
_ -> checkError $
if isLockLabel l
then let cat = drop 5 (showIdent (label2ident l))
in ppTerm Unqualified 0 (R rms) <+> "is not in the lincat of" <+> cat <>
"; try wrapping it with lin" <+> cat
else "cannot find value for label" <+> l <+> "in" <+> ppTerm Unqualified 0 (R rms)
in ppTerm Unqualified 0 (R rms) <+> text "is not in the lincat of" <+> text cat <>
text "; try wrapping it with lin" <+> text cat
else text "cannot find value for label" <+> ppLabel l <+> text "in" <+> ppTerm Unqualified 0 (R rms)
checkCase arg val (p,t) = do
cont <- pattContext gr g arg p
t' <- justCheck (reverse cont ++ g) t val
let (_,_,p') = measurePatt gr p
return (p',t')
return (p,t')
pattContext :: SourceGrammar -> Context -> Type -> Patt -> Check Context
pattContext env g typ p = case p of
@@ -639,7 +553,7 @@ pattContext env g typ p = case p of
PP (q,c) ps | q /= cPredef -> do ---- why this /=? AR 6/1/2006
t <- lookupResType env (q,c)
let (cont,v) = typeFormCnc t
checkCond ("wrong number of arguments for constructor in" <+> ppPatt Unqualified 0 p)
checkCond (text "wrong number of arguments for constructor in" <+> ppPatt Unqualified 0 p)
(length cont == length ps)
checkEqLType env g typ v (patt2term p)
mapM (\((_,_,ty),p) -> pattContext env g ty p) (zip cont ps) >>= return . concat
@@ -650,7 +564,7 @@ pattContext env g typ p = case p of
let pts = [(ty,tr) | (l,tr) <- r, Just ty <- [lookup l t]]
----- checkWarn $ prt p ++++ show pts ----- debug
mapM (uncurry (pattContext env g)) pts >>= return . concat
_ -> checkError ("record type expected for pattern instead of" <+> ppTerm Unqualified 0 typ')
_ -> checkError (text "record type expected for pattern instead of" <+> ppTerm Unqualified 0 typ')
PT t p' -> do
checkEqLType env g typ t (patt2term p')
pattContext env g typ p'
@@ -663,24 +577,24 @@ pattContext env g typ p = case p of
g1 <- pattContext env g typ p'
g2 <- pattContext env g typ q
let pts = nub ([x | pt@(_,x,_) <- g1, notElem pt g2] ++ [x | pt@(_,x,_) <- g2, notElem pt g1])
checkCond
("incompatible bindings of" <+>
fsep pts <+>
"in pattern alterantives" <+> ppPatt Unqualified 0 p) (null pts)
checkCond
(text "incompatible bindings of" <+>
fsep (map ppIdent pts) <+>
text "in pattern alterantives" <+> ppPatt Unqualified 0 p) (null pts)
return g1 -- must be g1 == g2
PSeq _ _ p _ _ q -> do
PSeq p q -> do
g1 <- pattContext env g typ p
g2 <- pattContext env g typ q
return $ g1 ++ g2
PRep _ _ p' -> noBind typeStr p'
PRep p' -> noBind typeStr p'
PNeg p' -> noBind typ p'
_ -> return [] ---- check types!
where
where
noBind typ p' = do
co <- pattContext env g typ p'
if not (null co)
then checkWarn ("no variable bound inside pattern" <+> ppPatt Unqualified 0 p)
then checkWarn (text "no variable bound inside pattern" <+> ppPatt Unqualified 0 p)
>> return []
else return []
@@ -689,31 +603,9 @@ checkEqLType gr g t u trm = do
(b,t',u',s) <- checkIfEqLType gr g t u trm
case b of
True -> return t'
False ->
let inferredType = ppTerm Qualified 0 u
expectedType = ppTerm Qualified 0 t
term = ppTerm Unqualified 0 trm
funName = pp . head . words .render $ term
helpfulMsg =
case (arrows inferredType, arrows expectedType) of
(0,0) -> pp "" -- None of the types is a function
_ -> "\n **" <+>
if expectedType `isLessApplied` inferredType
then "Maybe you gave too few arguments to" <+> funName
else pp "Double-check that type signature and number of arguments match."
in checkError $ s <+> "type of" <+> term $$
"expected:" <+> expectedType $$ -- ppqType t u $$
"inferred:" <+> inferredType $$ -- ppqType u t
helpfulMsg
where
-- count the number of arrows in the prettyprinted term
arrows :: Doc -> Int
arrows = length . filter (=="->") . words . render
-- If prettyprinted type t has fewer arrows then prettyprinted type u,
-- then t is "less applied", and we can print out more helpful error msg.
isLessApplied :: Doc -> Doc -> Bool
isLessApplied t u = arrows t < arrows u
False -> checkError $ text s <+> text "type of" <+> ppTerm Unqualified 0 trm $$
text "expected:" <+> ppType t $$
text "inferred:" <+> ppType u
checkIfEqLType :: SourceGrammar -> Context -> Type -> Type -> Term -> Check (Bool,Type,Type,String)
checkIfEqLType gr g t u trm = do
@@ -725,62 +617,60 @@ checkIfEqLType gr g t u trm = do
--- better: use a flag to forgive? (AR 31/1/2006)
_ -> case missingLock [] t' u' of
Ok lo -> do
checkWarn $ "missing lock field" <+> fsep lo
checkWarn $ text "missing lock field" <+> fsep (map ppLabel lo)
return (True,t',u',[])
Bad s -> return (False,t',u',s)
where
-- check that u is a subtype of t
-- t is a subtype of u
--- quick hack version of TC.eqVal
alpha g t u = case (t,u) of
alpha g t u = case (t,u) of
-- error (the empty type!) is subtype of any other type
(_,u) | u == typeError -> True
-- contravariance
(Prod _ x a b, Prod _ y c d) -> alpha g c a && alpha ((x,y):g) b d
(Prod _ x a b, Prod _ y c d) -> alpha g c a && alpha ((x,y):g) b d
-- record subtyping
(RecType rs, RecType ts) -> all (\ (l,a) ->
any (\ (k,b) -> l == k && alpha g a b) ts) rs
(RecType rs, RecType ts) -> all (\ (l,a) ->
any (\ (k,b) -> alpha g a b && l == k) ts) rs
(ExtR r s, ExtR r' s') -> alpha g r r' && alpha g s s'
(ExtR r s, t) -> alpha g r t || alpha g s t
-- the following say that Ints n is a subset of Int and of Ints m >= n
-- But why does it also allow Int as a subtype of Ints m? /TH 2014-04-04
(t,u) | Just m <- isTypeInts t, Just n <- isTypeInts u -> m >= n
(t,u) | Just m <- isTypeInts t, Just n <- isTypeInts t -> m >= n
| Just _ <- isTypeInts t, u == typeInt -> True ---- check size!
| t == typeInt, Just _ <- isTypeInts u -> True ---- why this ???? AR 11/12/2005
---- this should be made in Rename
(Q (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
(Q (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
|| m == n --- for Predef
(QC (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
(QC (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
(QC (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
(QC (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
(Q (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
(Q (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
-- contravariance
(Table a b, Table c d) -> alpha g c a && alpha g b d
(Table a b, Table c d) -> alpha g a c && alpha g b d
(Vr x, Vr y) -> x == y || elem (x,y) g || elem (y,x) g
_ -> t == u
_ -> t == u
--- the following should be one-way coercions only. AR 4/1/2001
|| elem t sTypes && elem u sTypes
|| (t == typeType && u == typePType)
|| (u == typeType && t == typePType)
|| (t == typeType && u == typePType)
|| (u == typeType && t == typePType)
missingLock g t u = case (t,u) of
(RecType rs, RecType ts) ->
let
ls = [l | (l,a) <- rs,
missingLock g t u = case (t,u) of
(RecType rs, RecType ts) ->
let
ls = [l | (l,a) <- rs,
not (any (\ (k,b) -> alpha g a b && l == k) ts)]
(locks,others) = partition isLockLabel ls
in case others of
_:_ -> Bad $ render ("missing record fields:" <+> fsep (punctuate ',' (others)))
_:_ -> Bad $ render (text "missing record fields:" <+> fsep (punctuate comma (map ppLabel others)))
_ -> return locks
-- contravariance
(Prod _ x a b, Prod _ y c d) -> do
@@ -806,7 +696,7 @@ termWith t ct = do
return (t,ty)
-- | compositional check\/infer of binary operations
check2 :: (Term -> Check Term) -> (Term -> Term -> Term) ->
check2 :: (Term -> Check Term) -> (Term -> Term -> Term) ->
Term -> Term -> Type -> Check (Term,Type)
check2 chk con a b t = do
a' <- chk a
@@ -818,18 +708,14 @@ ppType :: Type -> Doc
ppType ty =
case ty of
RecType fs -> case filter isLockLabel $ map fst fs of
[lock] -> pp (drop 5 (showIdent (label2ident lock)))
[lock] -> text (drop 5 (showIdent (label2ident lock)))
_ -> ppTerm Unqualified 0 ty
Prod _ x a b -> ppType a <+> "->" <+> ppType b
Prod _ x a b -> ppType a <+> text "->" <+> ppType b
_ -> ppTerm Unqualified 0 ty
{-
ppqType :: Type -> Type -> Doc
ppqType t u = case (ppType t, ppType u) of
(pt,pu) | render pt == render pu -> ppTerm Qualified 0 t
(pt,_) -> pt
-}
checkLookup :: Ident -> Context -> Check Type
checkLookup x g =
case [ty | (b,y,ty) <- g, x == y] of
[] -> checkError ("unknown variable" <+> x)
[] -> checkError (text "unknown variable" <+> ppIdent x)
(ty:_) -> return ty
-}

View File

@@ -10,7 +10,8 @@ import GF.Grammar hiding (Env, VGen, VApp, VRecType)
import GF.Grammar.Lookup
import GF.Grammar.Predef
import GF.Grammar.Lockfield
import GF.Compile.Compute.Concrete
import GF.Compile.Compute.ConcreteNew
import GF.Compile.Compute.Predef(predef,predefName)
import GF.Infra.CheckM
import GF.Data.Operations
import Control.Applicative(Applicative(..))
@@ -21,20 +22,20 @@ import qualified Data.IntMap as IntMap
import Data.Maybe(fromMaybe,isNothing)
import qualified Control.Monad.Fail as Fail
checkLType :: Grammar -> Term -> Type -> Check (Term, Type)
checkLType ge t ty = error "TODO: checkLType" {- runTcM $ do
checkLType :: GlobalEnv -> Term -> Type -> Check (Term, Type)
checkLType ge t ty = runTcM $ do
vty <- liftErr (eval ge [] ty)
(t,_) <- tcRho ge [] t (Just vty)
t <- zonkTerm t
return (t,ty) -}
return (t,ty)
inferLType :: Grammar -> Term -> Check (Term, Type)
inferLType ge t = error "TODO: inferLType" {- runTcM $ do
inferLType :: GlobalEnv -> Term -> Check (Term, Type)
inferLType ge t = runTcM $ do
(t,ty) <- inferSigma ge [] t
t <- zonkTerm t
ty <- zonkTerm =<< tc_value2term (geLoc ge) [] ty
return (t,ty) -}
{-
return (t,ty)
inferSigma :: GlobalEnv -> Scope -> Term -> TcM (Term,Sigma)
inferSigma ge scope t = do -- GEN1
(t,ty) <- tcRho ge scope t Nothing
@@ -132,7 +133,7 @@ tcRho ge scope t@(RecType rs) (Just ty) = do
[] -> unifyVar ge scope i env vs vtypePType
_ -> return ()
ty -> do ty <- zonkTerm =<< tc_value2term (geLoc ge) (scopeVars scope) ty
tcError ("The record type" <+> ppTerm Unqualified 0 t $$
tcError ("The record type" <+> ppTerm Unqualified 0 t $$
"cannot be of type" <+> ppTerm Unqualified 0 ty)
(rs,mb_ty) <- tcRecTypeFields ge scope rs (Just ty')
return (f (RecType rs),ty)
@@ -186,7 +187,7 @@ tcRho ge scope (R rs) (Just ty) = do
case ty' of
(VRecType ltys) -> do lttys <- checkRecFields ge scope rs ltys
rs <- mapM (\(l,t,ty) -> tc_value2term (geLoc ge) (scopeVars scope) ty >>= \ty -> return (l, (Just ty, t))) lttys
return ((f . R) rs,
return ((f . R) rs,
VRecType [(l, ty) | (l,t,ty) <- lttys]
)
ty -> do lttys <- inferRecFields ge scope rs
@@ -276,11 +277,11 @@ tcApp ge scope (App fun arg) = -- APP2
varg <- liftErr (eval ge (scopeEnv scope) arg)
return (App fun arg, res_ty varg)
tcApp ge scope (Q id) = -- VAR (global)
mkTcA (lookupOverloadTypes (geGrammar ge) id) `bindTcA` \(t,ty) ->
mkTcA (lookupOverloadTypes (geGrammar ge) id) `bindTcA` \(t,ty) ->
do ty <- liftErr (eval ge [] ty)
return (t,ty)
tcApp ge scope (QC id) = -- VAR (global)
mkTcA (lookupOverloadTypes (geGrammar ge) id) `bindTcA` \(t,ty) ->
mkTcA (lookupOverloadTypes (geGrammar ge) id) `bindTcA` \(t,ty) ->
do ty <- liftErr (eval ge [] ty)
return (t,ty)
tcApp ge scope t =
@@ -317,7 +318,7 @@ tcPatt ge scope (PString s) ty0 = do
tcPatt ge scope PChar ty0 = do
unify ge scope ty0 vtypeStr
return scope
tcPatt ge scope (PSeq _ _ p1 _ _ p2) ty0 = do
tcPatt ge scope (PSeq p1 p2) ty0 = do
unify ge scope ty0 vtypeStr
scope <- tcPatt ge scope p1 vtypeStr
scope <- tcPatt ge scope p2 vtypeStr
@@ -349,7 +350,7 @@ tcPatt ge scope (PM q) ty0 = do
Bad err -> tcError (pp err)
tcPatt ge scope p ty = unimplemented ("tcPatt "++show p)
inferRecFields ge scope rs =
inferRecFields ge scope rs =
mapM (\(l,r) -> tcRecField ge scope l r Nothing) rs
checkRecFields ge scope [] ltys
@@ -367,7 +368,7 @@ checkRecFields ge scope ((l,t):lts) ltys =
where
takeIt l1 [] = (Nothing, [])
takeIt l1 (lty@(l2,ty):ltys)
| l1 == l2 = (Just ty,ltys)
| l1 == l2 = (Just ty,ltys)
| otherwise = let (mb_ty,ltys') = takeIt l1 ltys
in (mb_ty,lty:ltys')
@@ -389,13 +390,13 @@ tcRecTypeFields ge scope ((l,ty):rs) mb_ty = do
| s == cPType -> return mb_ty
VMeta _ _ _ -> return mb_ty
_ -> do sort <- zonkTerm =<< tc_value2term (geLoc ge) (scopeVars scope) sort
tcError ("The record type field" <+> l <+> ':' <+> ppTerm Unqualified 0 ty $$
tcError ("The record type field" <+> l <+> ':' <+> ppTerm Unqualified 0 ty $$
"cannot be of type" <+> ppTerm Unqualified 0 sort)
(rs,mb_ty) <- tcRecTypeFields ge scope rs mb_ty
return ((l,ty):rs,mb_ty)
-- | Invariant: if the third argument is (Just rho),
-- then rho is in weak-prenex form
-- then rho is in weak-prenex form
instSigma :: GlobalEnv -> Scope -> Term -> Sigma -> Maybe Rho -> TcM (Term, Rho)
instSigma ge scope t ty1 Nothing = return (t,ty1) -- INST1
instSigma ge scope t ty1 (Just ty2) = do -- INST2
@@ -443,11 +444,11 @@ subsCheckRho ge scope t (VApp p1 _) (VApp p2 _) -- Rule
| predefName p1 == cInts && predefName p2 == cInt = return t
subsCheckRho ge scope t (VApp p1 [VInt i]) (VApp p2 [VInt j]) -- Rule INT2
| predefName p1 == cInts && predefName p2 == cInts =
if i <= j
if i <= j
then return t
else tcError ("Ints" <+> i <+> "is not a subtype of" <+> "Ints" <+> j)
subsCheckRho ge scope t ty1@(VRecType rs1) ty2@(VRecType rs2) = do -- Rule REC
let mkAccess scope t =
let mkAccess scope t =
case t of
ExtR t1 t2 -> do (scope,mkProj1,mkWrap1) <- mkAccess scope t1
(scope,mkProj2,mkWrap2) <- mkAccess scope t2
@@ -556,7 +557,7 @@ unify ge scope v (VMeta i env vs) = unifyVar ge scope i env vs v
unify ge scope v1 v2 = do
t1 <- zonkTerm =<< tc_value2term (geLoc ge) (scopeVars scope) v1
t2 <- zonkTerm =<< tc_value2term (geLoc ge) (scopeVars scope) v2
tcError ("Cannot unify terms:" <+> (ppTerm Unqualified 0 t1 $$
tcError ("Cannot unify terms:" <+> (ppTerm Unqualified 0 t1 $$
ppTerm Unqualified 0 t2))
-- | Invariant: tv1 is a flexible type variable
@@ -567,9 +568,9 @@ unifyVar ge scope i env vs ty2 = do -- Check whether i is bound
Bound ty1 -> do v <- liftErr (eval ge env ty1)
unify ge scope (vapply (geLoc ge) v vs) ty2
Unbound scope' _ -> case value2term (geLoc ge) (scopeVars scope') ty2 of
-- Left i -> let (v,_) = reverse scope !! i
-- in tcError ("Variable" <+> pp v <+> "has escaped")
ty2' -> do ms2 <- getMetaVars (geLoc ge) [(scope,ty2)]
Left i -> let (v,_) = reverse scope !! i
in tcError ("Variable" <+> pp v <+> "has escaped")
Right ty2' -> do ms2 <- getMetaVars (geLoc ge) [(scope,ty2)]
if i `elem` ms2
then tcError ("Occurs check for" <+> ppMeta i <+> "in:" $$
nest 2 (ppTerm Unqualified 0 ty2'))
@@ -608,7 +609,7 @@ quantify ge scope t tvs ty0 = do
ty <- tc_value2term (geLoc ge) (scopeVars scope) ty0
let used_bndrs = nub (bndrs ty) -- Avoid quantified type variables in use
new_bndrs = take (length tvs) (allBinders \\ used_bndrs)
mapM_ bind (tvs `zip` new_bndrs) -- 'bind' is just a cunning way
mapM_ bind (tvs `zip` new_bndrs) -- 'bind' is just a cunning way
ty <- zonkTerm ty -- of doing the substitution
vty <- liftErr (eval ge [] (foldr (\v ty -> Prod Implicit v typeType ty) ty new_bndrs))
return (foldr (Abs Implicit) t new_bndrs,vty)
@@ -618,7 +619,7 @@ quantify ge scope t tvs ty0 = do
bndrs (Prod _ x t1 t2) = [x] ++ bndrs t1 ++ bndrs t2
bndrs _ = []
allBinders :: [Ident] -- a,b,..z, a1, b1,... z1, a2, b2,...
allBinders :: [Ident] -- a,b,..z, a1, b1,... z1, a2, b2,...
allBinders = [ identS [x] | x <- ['a'..'z'] ] ++
[ identS (x : show i) | i <- [1 :: Integer ..], x <- ['a'..'z']]
@@ -630,8 +631,8 @@ allBinders = [ identS [x] | x <- ['a'..'z'] ] ++
type Scope = [(Ident,Value)]
type Sigma = Value
type Rho = Value -- No top-level ForAll
type Tau = Value -- No ForAlls anywhere
type Rho = Value -- No top-level ForAll
type Tau = Value -- No ForAlls anywhere
data MetaValue
= Unbound Scope Sigma
@@ -687,12 +688,12 @@ runTcM f = case unTcM f IntMap.empty [] of
TcFail (msg:msgs) -> do checkWarnings msgs; checkError msg
newMeta :: Scope -> Sigma -> TcM MetaId
newMeta scope ty = TcM (\ms msgs ->
newMeta scope ty = TcM (\ms msgs ->
let i = IntMap.size ms
in TcOk i (IntMap.insert i (Unbound scope ty) ms) msgs)
getMeta :: MetaId -> TcM MetaValue
getMeta i = TcM (\ms msgs ->
getMeta i = TcM (\ms msgs ->
case IntMap.lookup i ms of
Just mv -> TcOk mv ms msgs
Nothing -> TcFail (("Unknown metavariable" <+> ppMeta i) : msgs))
@@ -701,7 +702,7 @@ setMeta :: MetaId -> MetaValue -> TcM ()
setMeta i mv = TcM (\ms msgs -> TcOk () (IntMap.insert i mv ms) msgs)
newVar :: Scope -> Ident
newVar scope = head [x | i <- [1..],
newVar scope = head [x | i <- [1..],
let x = identS ('v':show i),
isFree scope x]
where
@@ -720,11 +721,11 @@ getMetaVars loc sc_tys = do
return (foldr go [] tys)
where
-- Get the MetaIds from a term; no duplicates in result
go (Vr tv) acc = acc
go (Vr tv) acc = acc
go (App x y) acc = go x (go y acc)
go (Meta i) acc
| i `elem` acc = acc
| otherwise = i : acc
| i `elem` acc = acc
| otherwise = i : acc
go (Q _) acc = acc
go (QC _) acc = acc
go (Sort _) acc = acc
@@ -740,10 +741,10 @@ getFreeVars loc sc_tys = do
tys <- mapM (\(scope,ty) -> zonkTerm =<< tc_value2term loc (scopeVars scope) ty) sc_tys
return (foldr (go []) [] tys)
where
go bound (Vr tv) acc
| tv `elem` bound = acc
| tv `elem` acc = acc
| otherwise = tv : acc
go bound (Vr tv) acc
| tv `elem` bound = acc
| tv `elem` acc = acc
| otherwise = tv : acc
go bound (App x y) acc = go bound x (go bound y acc)
go bound (Meta _) acc = acc
go bound (Q _) acc = acc
@@ -764,13 +765,13 @@ zonkTerm (Meta i) = do
zonkTerm t = composOp zonkTerm t
tc_value2term loc xs v =
return $ value2term loc xs v
-- Old value2term error message:
-- Left i -> tcError ("Variable #" <+> pp i <+> "has escaped")
case value2term loc xs v of
Left i -> tcError ("Variable #" <+> pp i <+> "has escaped")
Right t -> return t
data TcA x a
data TcA x a
= TcSingle (MetaStore -> [Message] -> TcResult a)
| TcMany [x] (MetaStore -> [Message] -> [(a,MetaStore,[Message])])
@@ -799,4 +800,3 @@ runTcA g f = TcM (\ms msgs -> case f of
[(x,ms,msgs)] -> TcOk x ms msgs
rs -> unTcM (g xs) ms msgs
TcSingle f -> f ms msgs)
-}

View File

@@ -8,7 +8,7 @@ typPredefined :: Ident -> Maybe Type
typPredefined f = case Map.lookup f primitives of
Just (ResOper (Just (L _ ty)) _) -> Just ty
Just (ResParam _ _) -> Just typePType
Just (ResValue (L _ ty) _) -> Just ty
Just (ResValue (L _ ty)) -> Just ty
_ -> Nothing
primitives = Map.fromList
@@ -16,9 +16,9 @@ primitives = Map.fromList
, (cInt , ResOper (Just (noLoc typePType)) Nothing)
, (cFloat , ResOper (Just (noLoc typePType)) Nothing)
, (cInts , fun [typeInt] typePType)
, (cPBool , ResParam (Just (noLoc [(cPTrue,[]),(cPFalse,[])])) (Just ([QC (cPredef,cPTrue), QC (cPredef,cPFalse)],2)))
, (cPTrue , ResValue (noLoc typePBool) 0)
, (cPFalse , ResValue (noLoc typePBool) 1)
, (cPBool , ResParam (Just (noLoc [(cPTrue,[]),(cPFalse,[])])) (Just [QC (cPredef,cPTrue), QC (cPredef,cPFalse)]))
, (cPTrue , ResValue (noLoc typePBool))
, (cPFalse , ResValue (noLoc typePBool))
, (cError , fun [typeStr] typeError) -- non-can. of empty set
, (cLength , fun [typeTok] typeInt)
, (cDrop , fun [typeInt,typeTok] typeTok)

View File

@@ -0,0 +1,801 @@
{-# LANGUAGE PatternGuards #-}
module GF.Compile.TypeCheck.RConcrete( checkLType, inferLType, computeLType, ppType ) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Infra.CheckM
import GF.Data.Operations
import GF.Grammar
import GF.Grammar.Lookup
import GF.Grammar.Predef
import GF.Grammar.PatternMatch
import GF.Grammar.Lockfield (isLockLabel, lockRecType, unlockRecord)
import GF.Compile.TypeCheck.Primitives
import Data.List
import Control.Monad
import GF.Text.Pretty
computeLType :: SourceGrammar -> Context -> Type -> Check Type
computeLType gr g0 t = comp (reverse [(b,x, Vr x) | (b,x,_) <- g0] ++ g0) t
where
comp g ty = case ty of
_ | Just _ <- isTypeInts ty -> return ty ---- shouldn't be needed
| isPredefConstant ty -> return ty ---- shouldn't be needed
Q (m,ident) -> checkIn ("module" <+> m) $ do
ty' <- lookupResDef gr (m,ident)
if ty' == ty then return ty else comp g ty' --- is this necessary to test?
AdHocOverload ts -> do
over <- getOverload gr g (Just typeType) t
case over of
Just (tr,_) -> return tr
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 t)
Vr ident -> checkLookup ident g -- never needed to compute!
App f a -> do
f' <- comp g f
a' <- comp g a
case f' of
Abs b x t -> comp ((b,x,a'):g) t
_ -> return $ App f' a'
Prod bt x a b -> do
a' <- comp g a
b' <- comp ((bt,x,Vr x) : g) b
return $ Prod bt x a' b'
Abs bt x b -> do
b' <- comp ((bt,x,Vr x):g) b
return $ Abs bt x b'
Let (x,(_,a)) b -> comp ((Explicit,x,a):g) b
ExtR r s -> do
r' <- comp g r
s' <- comp g s
case (r',s') of
(RecType rs, RecType ss) -> plusRecType r' s' >>= comp g
_ -> return $ ExtR r' s'
RecType fs -> do
let fs' = sortRec fs
liftM RecType $ mapPairsM (comp g) fs'
ELincat c t -> do
t' <- comp g t
lockRecType c t' ---- locking to be removed AR 20/6/2009
_ | ty == typeTok -> return typeStr
_ | isPredefConstant ty -> return ty
_ -> composOp (comp g) ty
-- the underlying algorithms
inferLType :: SourceGrammar -> Context -> Term -> Check (Term, Type)
inferLType gr g trm = case trm of
Q (m,ident) | isPredef m -> termWith trm $ case typPredefined ident of
Just ty -> return ty
Nothing -> checkError ("unknown in Predef:" <+> ident)
Q ident -> checks [
termWith trm $ lookupResType gr ident >>= computeLType gr g
,
lookupResDef gr ident >>= inferLType gr g
,
checkError ("cannot infer type of constant" <+> ppTerm Unqualified 0 trm)
]
QC (m,ident) | isPredef m -> termWith trm $ case typPredefined ident of
Just ty -> return ty
Nothing -> checkError ("unknown in Predef:" <+> ident)
QC ident -> checks [
termWith trm $ lookupResType gr ident >>= computeLType gr g
,
lookupResDef gr ident >>= inferLType gr g
,
checkError ("cannot infer type of canonical constant" <+> ppTerm Unqualified 0 trm)
]
Vr ident -> termWith trm $ checkLookup ident g
Typed e t -> do
t' <- computeLType gr g t
checkLType gr g e t'
AdHocOverload ts -> do
over <- getOverload gr g Nothing trm
case over of
Just trty -> return trty
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 trm)
App f a -> do
over <- getOverload gr g Nothing trm
case over of
Just trty -> return trty
_ -> do
(f',fty) <- inferLType gr g f
fty' <- computeLType gr g fty
case fty' of
Prod bt z arg val -> do
a' <- justCheck g a arg
ty <- if isWildIdent z
then return val
else substituteLType [(bt,z,a')] val
return (App f' a',ty)
_ ->
let term = ppTerm Unqualified 0 f
funName = pp . head . words .render $ term
in checkError ("A function type is expected for" <+> term <+> "instead of type" <+> ppType fty $$
"\n ** Maybe you gave too many arguments to" <+> funName <+> "\n")
S f x -> do
(f', fty) <- inferLType gr g f
case fty of
Table arg val -> do
x'<- justCheck g x arg
return (S f' x', val)
_ -> checkError ("table lintype expected for the table in" $$ nest 2 (ppTerm Unqualified 0 trm))
P t i -> do
(t',ty) <- inferLType gr g t --- ??
ty' <- computeLType gr g ty
let tr2 = P t' i
termWith tr2 $ case ty' of
RecType ts -> case lookup i ts of
Nothing -> checkError ("unknown label" <+> i <+> "in" $$ nest 2 (ppTerm Unqualified 0 ty'))
Just x -> return x
_ -> checkError ("record type expected for:" <+> ppTerm Unqualified 0 t $$
" instead of the inferred:" <+> ppTerm Unqualified 0 ty')
R r -> do
let (ls,fs) = unzip r
fsts <- mapM inferM fs
let ts = [ty | (Just ty,_) <- fsts]
checkCond ("cannot infer type of record" $$ nest 2 (ppTerm Unqualified 0 trm)) (length ts == length fsts)
return $ (R (zip ls fsts), RecType (zip ls ts))
T (TTyped arg) pts -> do
(_,val) <- checks $ map (inferCase (Just arg)) pts
checkLType gr g trm (Table arg val)
T (TComp arg) pts -> do
(_,val) <- checks $ map (inferCase (Just arg)) pts
checkLType gr g trm (Table arg val)
T ti pts -> do -- tries to guess: good in oper type inference
let pts' = [pt | pt@(p,_) <- pts, isConstPatt p]
case pts' of
[] -> checkError ("cannot infer table type of" <+> ppTerm Unqualified 0 trm)
---- PInt k : _ -> return $ Ints $ max [i | PInt i <- pts']
_ -> do
(arg,val) <- checks $ map (inferCase Nothing) pts'
checkLType gr g trm (Table arg val)
V arg pts -> do
(_,val) <- checks $ map (inferLType gr g) pts
-- return (trm, Table arg val) -- old, caused issue 68
checkLType gr g trm (Table arg val)
K s -> do
if elem ' ' s
then do
let ss = foldr C Empty (map K (words s))
----- removed irritating warning AR 24/5/2008
----- checkWarn ("token \"" ++ s ++
----- "\" converted to token list" ++ prt ss)
return (ss, typeStr)
else return (trm, typeStr)
EInt i -> return (trm, typeInt)
EFloat i -> return (trm, typeFloat)
Empty -> return (trm, typeStr)
C s1 s2 ->
check2 (flip (justCheck g) typeStr) C s1 s2 typeStr
Glue s1 s2 ->
check2 (flip (justCheck g) typeStr) Glue s1 s2 typeStr ---- typeTok
---- hack from Rename.identRenameTerm, to live with files with naming conflicts 18/6/2007
Strs (Cn c : ts) | c == cConflict -> do
checkWarn ("unresolved constant, could be any of" <+> hcat (map (ppTerm Unqualified 0) ts))
inferLType gr g (head ts)
Strs ts -> do
ts' <- mapM (\t -> justCheck g t typeStr) ts
return (Strs ts', typeStrs)
Alts t aa -> do
t' <- justCheck g t typeStr
aa' <- flip mapM aa (\ (c,v) -> do
c' <- justCheck g c typeStr
v' <- checks $ map (justCheck g v) [typeStrs, EPattType typeStr]
return (c',v'))
return (Alts t' aa', typeStr)
RecType r -> do
let (ls,ts) = unzip r
ts' <- mapM (flip (justCheck g) typeType) ts
return (RecType (zip ls ts'), typeType)
ExtR r s -> do
--- over <- getOverload gr g Nothing r
--- let r1 = maybe r fst over
let r1 = r ---
(r',rT) <- inferLType gr g r1
rT' <- computeLType gr g rT
(s',sT) <- inferLType gr g s
sT' <- computeLType gr g sT
let trm' = ExtR r' s'
case (rT', sT') of
(RecType rs, RecType ss) -> do
let rt = RecType ([field | field@(l,_) <- rs, notElem l (map fst ss)] ++ ss) -- select types of later fields
checkLType gr g trm' rt ---- return (trm', rt)
_ | rT' == typeType && sT' == typeType -> do
return (trm', typeType)
_ -> checkError ("records or record types expected in" <+> ppTerm Unqualified 0 trm)
Sort _ ->
termWith trm $ return typeType
Prod bt x a b -> do
a' <- justCheck g a typeType
b' <- justCheck ((bt,x,a'):g) b typeType
return (Prod bt x a' b', typeType)
Table p t -> do
p' <- justCheck g p typeType --- check p partype!
t' <- justCheck g t typeType
return $ (Table p' t', typeType)
FV vs -> do
(_,ty) <- checks $ map (inferLType gr g) vs
--- checkIfComplexVariantType trm ty
checkLType gr g trm ty
EPattType ty -> do
ty' <- justCheck g ty typeType
return (EPattType ty',typeType)
EPatt p -> do
ty <- inferPatt p
return (trm, EPattType ty)
ELin c trm -> do
(trm',ty) <- inferLType gr g trm
ty' <- lockRecType c ty ---- lookup c; remove lock AR 20/6/2009
return $ (ELin c trm', ty')
_ -> checkError ("cannot infer lintype of" <+> ppTerm Unqualified 0 trm)
where
isPredef m = elem m [cPredef,cPredefAbs]
justCheck g ty te = checkLType gr g ty te >>= return . fst
-- for record fields, which may be typed
inferM (mty, t) = do
(t', ty') <- case mty of
Just ty -> checkLType gr g t ty
_ -> inferLType gr g t
return (Just ty',t')
inferCase mty (patt,term) = do
arg <- maybe (inferPatt patt) return mty
cont <- pattContext gr g arg patt
(_,val) <- inferLType gr (reverse cont ++ g) term
return (arg,val)
isConstPatt p = case p of
PC _ ps -> True --- all isConstPatt ps
PP _ ps -> True --- all isConstPatt ps
PR ps -> all (isConstPatt . snd) ps
PT _ p -> isConstPatt p
PString _ -> True
PInt _ -> True
PFloat _ -> True
PChar -> True
PChars _ -> True
PSeq p q -> isConstPatt p && isConstPatt q
PAlt p q -> isConstPatt p && isConstPatt q
PRep p -> isConstPatt p
PNeg p -> isConstPatt p
PAs _ p -> isConstPatt p
_ -> False
inferPatt p = case p of
PP (q,c) ps | q /= cPredef -> liftM valTypeCnc (lookupResType gr (q,c))
PAs _ p -> inferPatt p
PNeg p -> inferPatt p
PAlt p q -> checks [inferPatt p, inferPatt q]
PSeq _ _ -> return $ typeStr
PRep _ -> return $ typeStr
PChar -> return $ typeStr
PChars _ -> return $ typeStr
_ -> inferLType gr g (patt2term p) >>= return . snd
-- type inference: Nothing, type checking: Just t
-- the latter permits matching with value type
getOverload :: SourceGrammar -> Context -> Maybe Type -> Term -> Check (Maybe (Term,Type))
getOverload gr g mt ot = case appForm ot of
(f@(Q c), ts) -> case lookupOverload gr c of
Ok typs -> do
ttys <- mapM (inferLType gr g) ts
v <- matchOverload f typs ttys
return $ Just v
_ -> return Nothing
(AdHocOverload cs@(f:_), ts) -> do --- the function name f is only used in error messages
let typs = concatMap collectOverloads cs
ttys <- mapM (inferLType gr g) ts
v <- matchOverload f typs ttys
return $ Just v
_ -> return Nothing
where
collectOverloads tr@(Q c) = case lookupOverload gr c of
Ok typs -> typs
_ -> case lookupResType gr c of
Ok ty -> let (args,val) = typeFormCnc ty in [(map (\(b,x,t) -> t) args,(val,tr))]
_ -> []
collectOverloads _ = [] --- constructors QC
matchOverload f typs ttys = do
let (tts,tys) = unzip ttys
let vfs = lookupOverloadInstance tys typs
let matches = [vf | vf@((_,v,_),_) <- vfs, matchVal mt v]
let showTypes ty = hsep (map ppType ty)
let (stys,styps) = (showTypes tys, [showTypes ty | (ty,_) <- typs])
-- to avoid strange error msg e.g. in case of unmatch record extension, show whole types if needed AR 28/1/2013
let (stysError,stypsError) = if elem (render stys) (map render styps)
then (hsep (map (ppTerm Unqualified 0) tys), [hsep (map (ppTerm Unqualified 0) ty) | (ty,_) <- typs])
else (stys,styps)
case ([vf | (vf,True) <- matches],[vf | (vf,False) <- matches]) of
([(_,val,fun)],_) -> return (mkApp fun tts, val)
([],[(pre,val,fun)]) -> do
checkWarn $ "ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot $$
"for" $$
nest 2 (showTypes tys) $$
"using" $$
nest 2 (showTypes pre)
return (mkApp fun tts, val)
([],[]) -> do
checkError $ "no overload instance of" <+> ppTerm Qualified 0 f $$
maybe empty (\x -> "with value type" <+> ppType x) mt $$
"for argument list" $$
nest 2 stysError $$
"among alternatives" $$
nest 2 (vcat stypsError)
(vfs1,vfs2) -> case (noProds vfs1,noProds vfs2) of
([(val,fun)],_) -> do
return (mkApp fun tts, val)
([],[(val,fun)]) -> do
checkWarn ("ignoring lock fields in resolving" <+> ppTerm Unqualified 0 ot)
return (mkApp fun tts, val)
----- unsafely exclude irritating warning AR 24/5/2008
----- checkWarn $ "overloading of" +++ prt f +++
----- "resolved by excluding partial applications:" ++++
----- unlines [prtType env ty | (ty,_) <- vfs', not (noProd ty)]
--- now forgiving ambiguity with a warning AR 1/2/2014
-- This gives ad hoc overloading the same behaviour as the choice of the first match in renaming did before.
-- But it also gives a chance to ambiguous overloadings that were banned before.
(nps1,nps2) -> do
checkWarn $ "ambiguous overloading of" <+> ppTerm Unqualified 0 f <+>
---- "with argument types" <+> hsep (map (ppTerm Qualified 0) tys) $$
"resolved by selecting the first of the alternatives" $$
nest 2 (vcat [ppTerm Qualified 0 fun | (_,ty,fun) <- vfs1 ++ if null vfs1 then vfs2 else []])
case [(mkApp fun tts,val) | (val,fun) <- nps1 ++ nps2] of
[] -> checkError $ "no alternatives left when resolving" <+> ppTerm Unqualified 0 f
h:_ -> return h
matchVal mt v = elem mt [Nothing,Just v,Just (unlocked v)]
unlocked v = case v of
RecType fs -> RecType $ filter (not . isLockLabel . fst) (sortRec fs)
_ -> v
---- TODO: accept subtypes
---- TODO: use a trie
lookupOverloadInstance tys typs =
[((pre,mkFunType rest val, t),isExact) |
let lt = length tys,
(ty,(val,t)) <- typs, length ty >= lt,
let (pre,rest) = splitAt lt ty,
let isExact = pre == tys,
isExact || map unlocked pre == map unlocked tys
]
noProds vfs = [(v,f) | (_,v,f) <- vfs, noProd v]
noProd ty = case ty of
Prod _ _ _ _ -> False
_ -> True
checkLType :: SourceGrammar -> Context -> Term -> Type -> Check (Term, Type)
checkLType gr g trm typ0 = do
typ <- computeLType gr g typ0
case trm of
Abs bt x c -> do
case typ of
Prod bt' z a b -> do
(c',b') <- if isWildIdent z
then checkLType gr ((bt,x,a):g) c b
else do b' <- checkIn (pp "abs") $ substituteLType [(bt',z,Vr x)] b
checkLType gr ((bt,x,a):g) c b'
return $ (Abs bt x c', Prod bt' z a b')
_ -> checkError $ "function type expected instead of" <+> ppType typ $$
"\n ** Double-check that the type signature of the operation" $$
"matches the number of arguments given to it.\n"
App f a -> do
over <- getOverload gr g (Just typ) trm
case over of
Just trty -> return trty
_ -> do
(trm',ty') <- inferLType gr g trm
termWith trm' $ checkEqLType gr g typ ty' trm'
AdHocOverload ts -> do
over <- getOverload gr g Nothing trm
case over of
Just trty -> return trty
_ -> checkError ("unresolved overloading of constants" <+> ppTerm Qualified 0 trm)
Q _ -> do
over <- getOverload gr g (Just typ) trm
case over of
Just trty -> return trty
_ -> do
(trm',ty') <- inferLType gr g trm
termWith trm' $ checkEqLType gr g typ ty' trm'
T _ [] ->
checkError ("found empty table in type" <+> ppTerm Unqualified 0 typ)
T _ cs -> case typ of
Table arg val -> do
case allParamValues gr arg of
Ok vs -> do
let ps0 = map fst cs
ps <- testOvershadow ps0 vs
if null ps
then return ()
else checkWarn ("patterns never reached:" $$
nest 2 (vcat (map (ppPatt Unqualified 0) ps)))
_ -> return () -- happens with variable types
cs' <- mapM (checkCase arg val) cs
return (T (TTyped arg) cs', typ)
_ -> checkError $ "table type expected for table instead of" $$ nest 2 (ppType typ)
V arg0 vs ->
case typ of
Table arg1 val ->
do arg' <- checkEqLType gr g arg0 arg1 trm
vs1 <- allParamValues gr arg1
if length vs1 == length vs
then return ()
else checkError $ "wrong number of values in table" <+> ppTerm Unqualified 0 trm
vs' <- map fst `fmap` sequence [checkLType gr g v val|v<-vs]
return (V arg' vs',typ)
R r -> case typ of --- why needed? because inference may be too difficult
RecType rr -> do
--let (ls,_) = unzip rr -- labels of expected type
fsts <- mapM (checkM r) rr -- check that they are found in the record
return $ (R fsts, typ) -- normalize record
_ -> checkError ("record type expected in type checking instead of" $$ nest 2 (ppTerm Unqualified 0 typ))
ExtR r s -> case typ of
_ | typ == typeType -> do
trm' <- computeLType gr g trm
case trm' of
RecType _ -> termWith trm' $ return typeType
ExtR (Vr _) (RecType _) -> termWith trm' $ return typeType
-- ext t = t ** ...
_ -> checkError ("invalid record type extension" <+> nest 2 (ppTerm Unqualified 0 trm))
RecType rr -> do
ll2 <- case s of
R ss -> return $ map fst ss
_ -> do
(s',typ2) <- inferLType gr g s
case typ2 of
RecType ss -> return $ map fst ss
_ -> checkError ("cannot get labels from" $$ nest 2 (ppTerm Unqualified 0 typ2))
let ll1 = [l | (l,_) <- rr, notElem l ll2]
--- over <- getOverload gr g Nothing r --- this would solve #66 but fail ParadigmsAra. AR 6/7/2020
--- let r1 = maybe r fst over
let r1 = r ---
(r',_) <- checkLType gr g r1 (RecType [field | field@(l,_) <- rr, elem l ll1])
(s',_) <- checkLType gr g s (RecType [field | field@(l,_) <- rr, elem l ll2])
let rec = R ([(l,(Nothing,P r' l)) | l <- ll1] ++ [(l,(Nothing,P s' l)) | l <- ll2])
return (rec, typ)
ExtR ty ex -> do
r' <- justCheck g r ty
s' <- justCheck g s ex
return $ (ExtR r' s', typ) --- is this all? it assumes the same division in trm and typ
_ -> checkError ("record extension not meaningful for" <+> ppTerm Unqualified 0 typ)
FV vs -> do
ttys <- mapM (flip (checkLType gr g) typ) vs
--- checkIfComplexVariantType trm typ
return (FV (map fst ttys), typ) --- typ' ?
S tab arg -> checks [ do
(tab',ty) <- inferLType gr g tab
ty' <- computeLType gr g ty
case ty' of
Table p t -> do
(arg',val) <- checkLType gr g arg p
checkEqLType gr g typ t trm
return (S tab' arg', t)
_ -> checkError ("table type expected for applied table instead of" <+> ppType ty')
, do
(arg',ty) <- inferLType gr g arg
ty' <- computeLType gr g ty
(tab',_) <- checkLType gr g tab (Table ty' typ)
return (S tab' arg', typ)
]
Let (x,(mty,def)) body -> case mty of
Just ty -> do
(ty0,_) <- checkLType gr g ty typeType
(def',ty') <- checkLType gr g def ty0
body' <- justCheck ((Explicit,x,ty'):g) body typ
return (Let (x,(Just ty',def')) body', typ)
_ -> do
(def',ty) <- inferLType gr g def -- tries to infer type of local constant
checkLType gr g (Let (x,(Just ty,def')) body) typ
ELin c tr -> do
tr1 <- unlockRecord c tr
checkLType gr g tr1 typ
_ -> do
(trm',ty') <- inferLType gr g trm
termWith trm' $ checkEqLType gr g typ ty' trm'
where
justCheck g ty te = checkLType gr g ty te >>= return . fst
{-
recParts rr t = (RecType rr1,RecType rr2) where
(rr1,rr2) = partition (flip elem (map fst t) . fst) rr
-}
checkM rms (l,ty) = case lookup l rms of
Just (Just ty0,t) -> do
checkEqLType gr g ty ty0 t
(t',ty') <- checkLType gr g t ty
return (l,(Just ty',t'))
Just (_,t) -> do
(t',ty') <- checkLType gr g t ty
return (l,(Just ty',t'))
_ -> checkError $
if isLockLabel l
then let cat = drop 5 (showIdent (label2ident l))
in ppTerm Unqualified 0 (R rms) <+> "is not in the lincat of" <+> cat <>
"; try wrapping it with lin" <+> cat
else "cannot find value for label" <+> l <+> "in" <+> ppTerm Unqualified 0 (R rms)
checkCase arg val (p,t) = do
cont <- pattContext gr g arg p
t' <- justCheck (reverse cont ++ g) t val
return (p,t')
pattContext :: SourceGrammar -> Context -> Type -> Patt -> Check Context
pattContext env g typ p = case p of
PV x -> return [(Explicit,x,typ)]
PP (q,c) ps | q /= cPredef -> do ---- why this /=? AR 6/1/2006
t <- lookupResType env (q,c)
let (cont,v) = typeFormCnc t
checkCond ("wrong number of arguments for constructor in" <+> ppPatt Unqualified 0 p)
(length cont == length ps)
checkEqLType env g typ v (patt2term p)
mapM (\((_,_,ty),p) -> pattContext env g ty p) (zip cont ps) >>= return . concat
PR r -> do
typ' <- computeLType env g typ
case typ' of
RecType t -> do
let pts = [(ty,tr) | (l,tr) <- r, Just ty <- [lookup l t]]
----- checkWarn $ prt p ++++ show pts ----- debug
mapM (uncurry (pattContext env g)) pts >>= return . concat
_ -> checkError ("record type expected for pattern instead of" <+> ppTerm Unqualified 0 typ')
PT t p' -> do
checkEqLType env g typ t (patt2term p')
pattContext env g typ p'
PAs x p -> do
g' <- pattContext env g typ p
return ((Explicit,x,typ):g')
PAlt p' q -> do
g1 <- pattContext env g typ p'
g2 <- pattContext env g typ q
let pts = nub ([x | pt@(_,x,_) <- g1, notElem pt g2] ++ [x | pt@(_,x,_) <- g2, notElem pt g1])
checkCond
("incompatible bindings of" <+>
fsep pts <+>
"in pattern alterantives" <+> ppPatt Unqualified 0 p) (null pts)
return g1 -- must be g1 == g2
PSeq p q -> do
g1 <- pattContext env g typ p
g2 <- pattContext env g typ q
return $ g1 ++ g2
PRep p' -> noBind typeStr p'
PNeg p' -> noBind typ p'
_ -> return [] ---- check types!
where
noBind typ p' = do
co <- pattContext env g typ p'
if not (null co)
then checkWarn ("no variable bound inside pattern" <+> ppPatt Unqualified 0 p)
>> return []
else return []
checkEqLType :: SourceGrammar -> Context -> Type -> Type -> Term -> Check Type
checkEqLType gr g t u trm = do
(b,t',u',s) <- checkIfEqLType gr g t u trm
case b of
True -> return t'
False ->
let inferredType = ppTerm Qualified 0 u
expectedType = ppTerm Qualified 0 t
term = ppTerm Unqualified 0 trm
funName = pp . head . words .render $ term
helpfulMsg =
case (arrows inferredType, arrows expectedType) of
(0,0) -> pp "" -- None of the types is a function
_ -> "\n **" <+>
if expectedType `isLessApplied` inferredType
then "Maybe you gave too few arguments to" <+> funName
else pp "Double-check that type signature and number of arguments match."
in checkError $ s <+> "type of" <+> term $$
"expected:" <+> expectedType $$ -- ppqType t u $$
"inferred:" <+> inferredType $$ -- ppqType u t
helpfulMsg
where
-- count the number of arrows in the prettyprinted term
arrows :: Doc -> Int
arrows = length . filter (=="->") . words . render
-- If prettyprinted type t has fewer arrows then prettyprinted type u,
-- then t is "less applied", and we can print out more helpful error msg.
isLessApplied :: Doc -> Doc -> Bool
isLessApplied t u = arrows t < arrows u
checkIfEqLType :: SourceGrammar -> Context -> Type -> Type -> Term -> Check (Bool,Type,Type,String)
checkIfEqLType gr g t u trm = do
t' <- computeLType gr g t
u' <- computeLType gr g u
case t' == u' || alpha [] t' u' of
True -> return (True,t',u',[])
-- forgive missing lock fields by only generating a warning.
--- better: use a flag to forgive? (AR 31/1/2006)
_ -> case missingLock [] t' u' of
Ok lo -> do
checkWarn $ "missing lock field" <+> fsep lo
return (True,t',u',[])
Bad s -> return (False,t',u',s)
where
-- check that u is a subtype of t
--- quick hack version of TC.eqVal
alpha g t u = case (t,u) of
-- error (the empty type!) is subtype of any other type
(_,u) | u == typeError -> True
-- contravariance
(Prod _ x a b, Prod _ y c d) -> alpha g c a && alpha ((x,y):g) b d
-- record subtyping
(RecType rs, RecType ts) -> all (\ (l,a) ->
any (\ (k,b) -> l == k && alpha g a b) ts) rs
(ExtR r s, ExtR r' s') -> alpha g r r' && alpha g s s'
(ExtR r s, t) -> alpha g r t || alpha g s t
-- the following say that Ints n is a subset of Int and of Ints m >= n
-- But why does it also allow Int as a subtype of Ints m? /TH 2014-04-04
(t,u) | Just m <- isTypeInts t, Just n <- isTypeInts u -> m >= n
| Just _ <- isTypeInts t, u == typeInt -> True ---- check size!
| t == typeInt, Just _ <- isTypeInts u -> True ---- why this ???? AR 11/12/2005
---- this should be made in Rename
(Q (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
|| m == n --- for Predef
(QC (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
(QC (m,a), Q (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
(Q (m,a), QC (n,b)) | a == b -> elem m (allExtendsPlus gr n)
|| elem n (allExtendsPlus gr m)
-- contravariance
(Table a b, Table c d) -> alpha g c a && alpha g b d
(Vr x, Vr y) -> x == y || elem (x,y) g || elem (y,x) g
_ -> t == u
--- the following should be one-way coercions only. AR 4/1/2001
|| elem t sTypes && elem u sTypes
|| (t == typeType && u == typePType)
|| (u == typeType && t == typePType)
missingLock g t u = case (t,u) of
(RecType rs, RecType ts) ->
let
ls = [l | (l,a) <- rs,
not (any (\ (k,b) -> alpha g a b && l == k) ts)]
(locks,others) = partition isLockLabel ls
in case others of
_:_ -> Bad $ render ("missing record fields:" <+> fsep (punctuate ',' (others)))
_ -> return locks
-- contravariance
(Prod _ x a b, Prod _ y c d) -> do
ls1 <- missingLock g c a
ls2 <- missingLock g b d
return $ ls1 ++ ls2
_ -> Bad ""
sTypes = [typeStr, typeTok, typeString]
-- auxiliaries
-- | light-weight substitution for dep. types
substituteLType :: Context -> Type -> Check Type
substituteLType g t = case t of
Vr x -> return $ maybe t id $ lookup x [(x,t) | (_,x,t) <- g]
_ -> composOp (substituteLType g) t
termWith :: Term -> Check Type -> Check (Term, Type)
termWith t ct = do
ty <- ct
return (t,ty)
-- | compositional check\/infer of binary operations
check2 :: (Term -> Check Term) -> (Term -> Term -> Term) ->
Term -> Term -> Type -> Check (Term,Type)
check2 chk con a b t = do
a' <- chk a
b' <- chk b
return (con a' b', t)
-- printing a type with a lock field lock_C as C
ppType :: Type -> Doc
ppType ty =
case ty of
RecType fs -> case filter isLockLabel $ map fst fs of
[lock] -> pp (drop 5 (showIdent (label2ident lock)))
_ -> ppTerm Unqualified 0 ty
Prod _ x a b -> ppType a <+> "->" <+> ppType b
_ -> ppTerm Unqualified 0 ty
{-
ppqType :: Type -> Type -> Doc
ppqType t u = case (ppType t, ppType u) of
(pt,pu) | render pt == render pu -> ppTerm Qualified 0 t
(pt,_) -> pt
-}
checkLookup :: Ident -> Context -> Check Type
checkLookup x g =
case [ty | (b,y,ty) <- g, x == y] of
[] -> checkError ("unknown variable" <+> x)
(ty:_) -> return ty

View File

@@ -5,22 +5,21 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/10/02 20:50:19 $
-- > CVS $Date: 2005/10/02 20:50:19 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.11 $
--
-- Thierry Coquand's type checking algorithm that creates a trace
-----------------------------------------------------------------------------
module GF.Compile.TypeCheck.TC (
AExp(..),
Theory,
checkExp,
inferExp,
checkBranch,
eqVal,
whnf
) where
module GF.Compile.TypeCheck.TC (AExp(..),
Theory,
checkExp,
inferExp,
checkBranch,
eqVal,
whnf
) where
import GF.Data.Operations
import GF.Grammar
@@ -32,17 +31,17 @@ import Data.Maybe
import GF.Text.Pretty
data AExp =
AVr Ident Val
AVr Ident Val
| ACn QIdent Val
| AType
| AInt Integer
| AType
| AInt Int
| AFloat Double
| AStr String
| AMeta MetaId Val
| ALet (Ident,(Val,AExp)) AExp
| AApp AExp AExp Val
| AAbs Ident Val AExp
| AProd Ident AExp AExp
| AApp AExp AExp Val
| AAbs Ident Val AExp
| AProd Ident AExp AExp
-- -- | AEqs [([Exp],AExp)] --- not used
| ARecType [ALabelling]
| AR [AAssign]
@@ -51,7 +50,7 @@ data AExp =
| AData Val
deriving (Eq,Show)
type ALabelling = (Label, AExp)
type ALabelling = (Label, AExp)
type AAssign = (Label, (Val, AExp))
type Theory = QIdent -> Err Val
@@ -72,7 +71,7 @@ whnf :: Val -> Err Val
whnf v = ---- errIn ("whnf" +++ prt v) $ ---- debug
case v of
VApp u w -> do
u' <- whnf u
u' <- whnf u
w' <- whnf w
app u' w'
VClos env e -> eval env e
@@ -82,9 +81,9 @@ app :: Val -> Val -> Err Val
app u v = case u of
VClos env (Abs _ x e) -> eval ((x,v):env) e
_ -> return $ VApp u v
eval :: Env -> Term -> Err Val
eval env e = ---- errIn ("eval" +++ prt e +++ "in" +++ prEnv env) $
eval env e = ---- errIn ("eval" +++ prt e +++ "in" +++ prEnv env) $
case e of
Vr x -> lookupVar env x
Q c -> return $ VCn c
@@ -96,23 +95,23 @@ eval env e = ---- errIn ("eval" +++ prt e +++ "in" +++ prEnv env) $
_ -> return $ VClos env e
eqVal :: Int -> Val -> Val -> Err [(Val,Val)]
eqVal k u1 u2 = ---- errIn (prt u1 +++ "<>" +++ prBracket (show k) +++ prt u2) $
eqVal k u1 u2 = ---- errIn (prt u1 +++ "<>" +++ prBracket (show k) +++ prt u2) $
do
w1 <- whnf u1
w2 <- whnf u2
w2 <- whnf u2
let v = VGen k
case (w1,w2) of
(VApp f1 a1, VApp f2 a2) -> liftM2 (++) (eqVal k f1 f2) (eqVal k a1 a2)
(VClos env1 (Abs _ x1 e1), VClos env2 (Abs _ x2 e2)) ->
eqVal (k+1) (VClos ((x1,v x1):env1) e1) (VClos ((x2,v x1):env2) e2)
(VClos env1 (Prod _ x1 a1 e1), VClos env2 (Prod _ x2 a2 e2)) ->
liftM2 (++)
liftM2 (++)
(eqVal k (VClos env1 a1) (VClos env2 a2))
(eqVal (k+1) (VClos ((x1,v x1):env1) e1) (VClos ((x2,v x1):env2) e2))
(VGen i _, VGen j _) -> return [(w1,w2) | i /= j]
(VCn (_, i), VCn (_,j)) -> return [(w1,w2) | i /= j]
(VCn (_, i), VCn (_,j)) -> return [(w1,w2) | i /= j]
--- thus ignore qualifications; valid because inheritance cannot
--- be qualified. Simplifies annotation. AR 17/3/2005
--- be qualified. Simplifies annotation. AR 17/3/2005
_ -> return [(w1,w2) | w1 /= w2]
-- invariant: constraints are in whnf
@@ -128,10 +127,10 @@ checkExp th tenv@(k,rho,gamma) e ty = do
Abs _ x t -> case typ of
VClos env (Prod _ y a b) -> do
a' <- whnf $ VClos env a ---
(t',cs) <- checkExp th
(k+1,(x,v x):rho, (x,a'):gamma) t (VClos ((y,v x):env) b)
return (AAbs x a' t', cs)
a' <- whnf $ VClos env a ---
(t',cs) <- checkExp th
(k+1,(x,v x):rho, (x,a'):gamma) t (VClos ((y,v x):env) b)
return (AAbs x a' t', cs)
_ -> Bad (render ("function type expected for" <+> ppTerm Unqualified 0 e <+> "instead of" <+> ppValue Unqualified 0 typ))
Let (x, (mb_typ, e1)) e2 -> do
@@ -151,7 +150,7 @@ checkExp th tenv@(k,rho,gamma) e ty = do
(b',csb) <- checkType th (k+1, (x,v x):rho, (x,VClos rho a):gamma) b
return (AProd x a' b', csa ++ csb)
R xs ->
R xs ->
case typ of
VRecType ys -> do case [l | (l,_) <- ys, isNothing (lookup l xs)] of
[] -> return ()
@@ -175,7 +174,7 @@ checkInferExp th tenv@(k,_,_) e typ = do
(e',w,cs1) <- inferExp th tenv e
cs2 <- eqVal k w typ
return (e',cs1 ++ cs2)
inferExp :: Theory -> TCEnv -> Term -> Err (AExp, Val, [(Val,Val)])
inferExp th tenv@(k,rho,gamma) e = case e of
Vr x -> mkAnnot (AVr x) $ noConstr $ lookupVar gamma x
@@ -201,13 +200,13 @@ inferExp th tenv@(k,rho,gamma) e = case e of
(e2,val2,cs2) <- inferExp th (k,rho,(x,val1):gamma) e2
return (ALet (x,(val1,e1)) e2, val2, cs1++cs2)
App f t -> do
(f',w,csf) <- inferExp th tenv f
(f',w,csf) <- inferExp th tenv f
typ <- whnf w
case typ of
VClos env (Prod _ x a b) -> do
(a',csa) <- checkExp th tenv t (VClos env a)
b' <- whnf $ VClos ((x,VClos rho t):env) b
return $ (AApp f' a' b', b', csf ++ csa)
b' <- whnf $ VClos ((x,VClos rho t):env) b
return $ (AApp f' a' b', b', csf ++ csa)
_ -> Bad (render ("Prod expected for function" <+> ppTerm Unqualified 0 f <+> "instead of" <+> ppValue Unqualified 0 typ))
_ -> Bad (render ("cannot infer type of expression" <+> ppTerm Unqualified 0 e))
@@ -233,9 +232,9 @@ checkAssign th tenv@(k,rho,gamma) typs (lbl,(Nothing,exp)) = do
return ((lbl,(val,aexp)),cs)
checkBranch :: Theory -> TCEnv -> Equation -> Val -> Err (([Term],AExp),[(Val,Val)])
checkBranch th tenv b@(ps,t) ty = errIn ("branch" +++ show b) $
chB tenv' ps' ty
where
checkBranch th tenv b@(ps,t) ty = errIn ("branch" +++ show b) $
chB tenv' ps' ty
where
(ps',_,rho2,k') = ps2ts k ps
tenv' = (k, rho2++rho, gamma) ---- k' ?
@@ -246,11 +245,11 @@ checkBranch th tenv b@(ps,t) ty = errIn ("branch" +++ show b) $
typ <- whnf ty
case typ of
VClos env (Prod _ y a b) -> do
a' <- whnf $ VClos env a
a' <- whnf $ VClos env a
(p', sigma, binds, cs1) <- checkP tenv p y a'
let tenv' = (length binds, sigma ++ rho, binds ++ gamma)
((ps',exp),cs2) <- chB tenv' ps2 (VClos ((y,p'):env) b)
return ((p:ps',exp), cs1 ++ cs2) -- don't change the patt
return ((p:ps',exp), cs1 ++ cs2) -- don't change the patt
_ -> Bad (render ("Product expected for definiens" <+> ppTerm Unqualified 0 t <+> "instead of" <+> ppValue Unqualified 0 typ))
[] -> do
(e,cs) <- checkExp th tenv t ty
@@ -260,15 +259,15 @@ checkBranch th tenv b@(ps,t) ty = errIn ("branch" +++ show b) $
let sigma = [(x, VGen i x) | ((x,_),i) <- zip delta [k..]]
return (VClos sigma t, sigma, delta, cs)
ps2ts k = foldr p2t ([],0,[],k)
ps2ts k = foldr p2t ([],0,[],k)
p2t p (ps,i,g,k) = case p of
PW -> (Meta i : ps, i+1,g,k)
PW -> (Meta i : ps, i+1,g,k)
PV x -> (Vr x : ps, i, upd x k g,k+1)
PAs x p -> p2t p (ps,i,g,k)
PString s -> (K s : ps, i, g, k)
PInt n -> (EInt n : ps, i, g, k)
PFloat n -> (EFloat n : ps, i, g, k)
PP c xs -> (mkApp (Q c) xss : ps, j, g',k')
PP c xs -> (mkApp (Q c) xss : ps, j, g',k')
where (xss,j,g',k') = foldr p2t ([],i,g,k) xs
PImplArg p -> p2t p (ps,i,g,k)
PTilde t -> (t : ps, i, g, k)
@@ -308,8 +307,8 @@ checkPatt th tenv exp val = do
case typ of
VClos env (Prod _ x a b) -> do
(a',_,csa) <- checkExpP tenv t (VClos env a)
b' <- whnf $ VClos ((x,VClos rho t):env) b
return $ (AApp f' a' b', b', csf ++ csa)
b' <- whnf $ VClos ((x,VClos rho t):env) b
return $ (AApp f' a' b', b', csf ++ csa)
_ -> Bad (render ("Prod expected for function" <+> ppTerm Unqualified 0 f <+> "instead of" <+> ppValue Unqualified 0 typ))
_ -> Bad (render ("cannot typecheck pattern" <+> ppTerm Unqualified 0 exp))
@@ -322,3 +321,4 @@ mkAnnot :: (Val -> AExp) -> Err (Val,[(Val,Val)]) -> Err (AExp,Val,[(Val,Val)])
mkAnnot a ti = do
(v,cs) <- ti
return (a v, v, cs)

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/05/30 18:39:44 $
-- > CVS $Date: 2005/05/30 18:39:44 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.8 $
--
@@ -34,14 +34,14 @@ buildAnyTree :: Fail.MonadFail m => ModuleName -> [(Ident,Info)] -> m (Map.Map I
buildAnyTree m = go Map.empty
where
go map [] = return map
go map ((c,j):is) =
go map ((c,j):is) = do
case Map.lookup c map of
Just i -> case unifyAnyInfo m i j of
Ok k -> go (Map.insert c k map) is
Bad _ -> fail $ render ("conflicting information in module"<+>m $$
nest 4 (ppJudgement Qualified (c,i)) $$
"and" $+$
nest 4 (ppJudgement Qualified (c,j)))
Ok k -> go (Map.insert c k map) is
Bad _ -> fail $ render ("conflicting information in module"<+>m $$
nest 4 (ppJudgement Qualified (c,i)) $$
"and" $+$
nest 4 (ppJudgement Qualified (c,j)))
Nothing -> go (Map.insert c j map) is
extendModule :: FilePath -> SourceGrammar -> SourceModule -> Check SourceModule
@@ -51,14 +51,14 @@ extendModule cwd gr (name,m)
---- Should be replaced by real control. AR 4/2/2005
| mstatus m == MSIncomplete && isModCnc m = return (name,m)
| otherwise = checkInModule cwd m NoLoc empty $ do
m' <- foldM extOne m (mextend m)
m' <- foldM extOne m (mextend m)
return (name,m')
where
extOne mo (n,cond) = do
m0 <- lookupModule gr n
-- test that the module types match, and find out if the old is complete
unless (sameMType (mtype m) (mtype mo))
unless (sameMType (mtype m) (mtype mo))
(checkError ("illegal extension type to module" <+> name))
let isCompl = isCompleteModule m0
@@ -67,7 +67,7 @@ extendModule cwd gr (name,m)
js1 <- extendMod gr isCompl ((n,m0), isInherited cond) name (jments mo)
-- if incomplete, throw away extension information
return $
return $
if isCompl
then mo {jments = js1}
else mo {mextend= filter ((/=n) . fst) (mextend mo)
@@ -75,10 +75,10 @@ extendModule cwd gr (name,m)
,jments = js1
}
-- | rebuilding instance + interface, and "with" modules, prior to renaming.
-- | rebuilding instance + interface, and "with" modules, prior to renaming.
-- AR 24/10/2003
rebuildModule :: FilePath -> SourceGrammar -> SourceModule -> Check SourceModule
rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ env_ js_)) =
checkInModule cwd mi NoLoc empty $ do
---- deps <- moduleDeps ms
@@ -88,8 +88,8 @@ rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
-- add the information given in interface into an instance module
Nothing -> do
unless (null is || mstatus mi == MSIncomplete)
(checkError ("module" <+> i <+>
unless (null is || mstatus mi == MSIncomplete)
(checkError ("module" <+> i <+>
"has open interfaces and must therefore be declared incomplete"))
case mt of
MTInstance (i0,mincl) -> do
@@ -113,9 +113,9 @@ rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
let stat' = if all (flip elem infs) is
then MSComplete
else MSIncomplete
unless (stat' == MSComplete || stat == MSIncomplete)
unless (stat' == MSComplete || stat == MSIncomplete)
(checkError ("module" <+> i <+> "remains incomplete"))
ModInfo mt0 _ fs me' _ ops0 _ fpath js <- lookupModule gr ext
ModInfo mt0 _ fs me' _ ops0 _ fpath _ js <- lookupModule gr ext
let ops1 = nub $
ops_ ++ -- N.B. js has been name-resolved already
[OQualif i j | (i,j) <- ops] ++
@@ -131,7 +131,7 @@ rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
js
let js1 = Map.union js0 js_
let med1= nub (ext : infs ++ insts ++ med_)
return $ ModInfo mt0 stat' fs1 me Nothing ops1 med1 msrc_ js1
return $ ModInfo mt0 stat' fs1 me Nothing ops1 med1 msrc_ env_ js1
return (i,mi')
@@ -141,24 +141,24 @@ rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
extendMod :: Grammar ->
Bool -> (Module,Ident -> Bool) -> ModuleName ->
Map.Map Ident Info -> Check (Map.Map Ident Info)
extendMod gr isCompl ((name,mi),cond) base new = foldM try new $ Map.toList (jments mi)
extendMod gr isCompl ((name,mi),cond) base new = foldM try new $ Map.toList (jments mi)
where
try new (c,i0)
| not (cond c) = return new
| otherwise = case Map.lookup c new of
Just j -> case unifyAnyInfo name i j of
Ok k -> return $ Map.insert c k new
Bad _ -> do (base,j) <- case j of
AnyInd _ m -> lookupOrigInfo gr (m,c)
_ -> return (base,j)
(name,i) <- case i of
Ok k -> return $ Map.insert c k new
Bad _ -> do (base,j) <- case j of
AnyInd _ m -> lookupOrigInfo gr (m,c)
_ -> return (base,j)
(name,i) <- case i of
AnyInd _ m -> lookupOrigInfo gr (m,c)
_ -> return (name,i)
checkError ("cannot unify the information" $$
nest 4 (ppJudgement Qualified (c,i)) $$
"in module" <+> name <+> "with" $$
nest 4 (ppJudgement Qualified (c,j)) $$
"in module" <+> base)
checkError ("cannot unify the information" $$
nest 4 (ppJudgement Qualified (c,i)) $$
"in module" <+> name <+> "with" $$
nest 4 (ppJudgement Qualified (c,j)) $$
"in module" <+> base)
Nothing-> if isCompl
then return $ Map.insert c (indirInfo name i) new
else return $ Map.insert c i new
@@ -166,11 +166,11 @@ extendMod gr isCompl ((name,mi),cond) base new = foldM try new $ Map.toList (jme
i = globalizeLoc (msrc mi) i0
indirInfo :: ModuleName -> Info -> Info
indirInfo n info = AnyInd b n' where
indirInfo n info = AnyInd b n' where
(b,n') = case info of
ResValue _ _ -> (True,n)
ResValue _ -> (True,n)
ResParam _ _ -> (True,n)
AbsFun _ _ Nothing _ -> (True,n)
AbsFun _ _ Nothing _ -> (True,n)
AnyInd b k -> (b,k)
_ -> (False,n) ---- canonical in Abs
@@ -179,7 +179,7 @@ globalizeLoc fpath i =
AbsCat mc -> AbsCat (fmap gl mc)
AbsFun mt ma md moper -> AbsFun (fmap gl mt) ma (fmap (fmap gl) md) moper
ResParam mt mv -> ResParam (fmap gl mt) mv
ResValue t i -> ResValue (gl t) i
ResValue t -> ResValue (gl t)
ResOper mt m -> ResOper (fmap gl mt) (fmap gl m)
ResOverload ms os -> ResOverload ms (map (\(x,y) -> (gl x,gl y)) os)
CncCat mc md mr mp mpmcfg-> CncCat (fmap gl mc) (fmap gl md) (fmap gl mr) (fmap gl mp) mpmcfg
@@ -194,24 +194,24 @@ globalizeLoc fpath i =
unifyAnyInfo :: ModuleName -> Info -> Info -> Err Info
unifyAnyInfo m i j = case (i,j) of
(AbsCat mc1, AbsCat mc2) ->
(AbsCat mc1, AbsCat mc2) ->
liftM AbsCat (unifyMaybeL mc1 mc2)
(AbsFun mt1 ma1 md1 moper1, AbsFun mt2 ma2 md2 moper2) ->
(AbsFun mt1 ma1 md1 moper1, AbsFun mt2 ma2 md2 moper2) ->
liftM4 AbsFun (unifyMaybeL mt1 mt2) (unifAbsArrity ma1 ma2) (unifAbsDefs md1 md2) (unifyMaybe moper1 moper2) -- adding defs
(ResParam mt1 mv1, ResParam mt2 mv2) ->
liftM2 ResParam (unifyMaybeL mt1 mt2) (unifyMaybe mv1 mv2)
(ResValue (L l1 t1) i1, ResValue (L l2 t2) i2)
| t1==t2 && i1 == i2 -> return (ResValue (L l1 t1) i1)
| otherwise -> fail ""
(ResValue (L l1 t1), ResValue (L l2 t2))
| t1==t2 -> return (ResValue (L l1 t1))
| otherwise -> fail ""
(_, ResOverload ms t) | elem m ms ->
return $ ResOverload ms t
(ResOper mt1 m1, ResOper mt2 m2) ->
(ResOper mt1 m1, ResOper mt2 m2) ->
liftM2 ResOper (unifyMaybeL mt1 mt2) (unifyMaybeL m1 m2)
(CncCat mc1 md1 mr1 mp1 mpmcfg1, CncCat mc2 md2 mr2 mp2 mpmcfg2) ->
(CncCat mc1 md1 mr1 mp1 mpmcfg1, CncCat mc2 md2 mr2 mp2 mpmcfg2) ->
liftM5 CncCat (unifyMaybeL mc1 mc2) (unifyMaybeL md1 md2) (unifyMaybeL mr1 mr2) (unifyMaybeL mp1 mp2) (unifyMaybe mpmcfg1 mpmcfg2)
(CncFun m mt1 md1 mpmcfg1, CncFun _ mt2 md2 mpmcfg2) ->
(CncFun m mt1 md1 mpmcfg1, CncFun _ mt2 md2 mpmcfg2) ->
liftM3 (CncFun m) (unifyMaybeL mt1 mt2) (unifyMaybeL md1 md2) (unifyMaybe mpmcfg1 mpmcfg2)
(AnyInd b1 m1, AnyInd b2 m2) -> do

View File

@@ -1,6 +1,6 @@
-- | Parallel grammar compilation
module GF.CompileInParallel(parallelBatchCompile) where
import Prelude hiding (catch,(<>))
import Prelude hiding (catch,(<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Control.Monad(join,ap,when,unless)
import Control.Applicative
import GF.Infra.Concurrency
@@ -36,8 +36,11 @@ import qualified Control.Monad.Fail as Fail
parallelBatchCompile jobs opts rootfiles0 =
do setJobs jobs
rootfiles <- mapM canonical rootfiles0
lib_dir <- canonical =<< getLibraryDirectory opts
filepaths <- mapM (getPathFromFile lib_dir opts) rootfiles
lib_dirs1 <- getLibraryDirectory opts
lib_dirs2 <- mapM canonical lib_dirs1
let lib_dir = head lib_dirs2
when (length lib_dirs2 >1) $ ePutStrLn ("GF_LIB_PATH defines more than one directory; using the first, " ++ show lib_dir)
filepaths <- mapM (getPathFromFile [lib_dir] opts) rootfiles
let groups = groupFiles lib_dir filepaths
n = length groups
when (n>1) $ ePutStrLn "Grammar mixes present and alltenses, dividing modules into two groups"

View File

@@ -8,6 +8,7 @@ module GF.CompileOne(-- ** Compiling a single module
import GF.Compile.GetGrammar(getSourceModule)
import GF.Compile.Rename(renameModule)
import GF.Compile.CheckGrammar(checkModule)
import GF.Compile.Optimize(optimizeModule)
import GF.Compile.SubExOpt(subexpModule,unsubexpModule)
import GF.Compile.GeneratePMCFG(generatePMCFG)
import GF.Compile.Update(extendModule,rebuildModule)
@@ -106,9 +107,10 @@ compileSourceModule opts cwd mb_gfFile gr =
-- Apply to complete modules when not generating tags
backend mo3 =
do if isModCnc (snd mo3) && flag optPMCFG opts
then runPassI "generating PMCFG" $ fmap fst $ runCheck' opts (generatePMCFG opts cwd gr mo3)
else runPassI "" $ return mo3
do mo4 <- runPassE Optimize "optimizing" $ optimizeModule opts gr mo3
if isModCnc (snd mo4) && flag optPMCFG opts
then runPassI "generating PMCFG" $ generatePMCFG opts gr mb_gfFile mo4
else runPassI "" $ return mo4
ifComplete yes mo@(_,mi) =
if isCompleteModule mi then yes mo else return mo
@@ -126,6 +128,7 @@ compileSourceModule opts cwd mb_gfFile gr =
-- * Running a compiler pass, with impedance matching
runPass = runPass' fst fst snd (liftErr . runCheck' opts)
runPassE = runPass2e liftErr id
runPassI = runPass2e id id Canon
runPass2e lift dump = runPass' id dump (const "") lift

View File

@@ -1,8 +1,11 @@
module GF.Compiler (mainGFC, linkGrammars, writeGrammar, writeOutputs) where
module GF.Compiler (mainGFC, linkGrammars, writePGF, writeLPGF, writeOutputs) where
import PGF2
import PGF2.Internal(unionPGF,writeConcr)
import GF.Compile as S(batchCompile,link,srcAbsName)
import PGF
import PGF.Internal(concretes,optimizePGF,unionPGF)
import PGF.Internal(putSplitAbs,encodeFile,runPut)
import LPGF(LPGF)
import qualified LPGF
import GF.Compile as S(batchCompile,link,linkl,srcAbsName)
import GF.CompileInParallel as P(parallelBatchCompile)
import GF.Compile.Export
import GF.Compile.ConcreteToHaskell(concretes2haskell)
@@ -10,12 +13,12 @@ import GF.Compile.GrammarToCanonical--(concretes2canonical)
import GF.Compile.CFGtoPGF
import GF.Compile.GetGrammar
import GF.Grammar.BNFC
import GF.Grammar.CFG
import GF.Grammar.CFG hiding (Grammar)
import GF.Grammar.Grammar (Grammar, ModuleName)
--import GF.Infra.Ident(showIdent)
import GF.Infra.UseIO
import GF.Infra.Option
import GF.Infra.CheckM
import GF.Data.ErrM
import GF.System.Directory
import GF.Text.Pretty(render,render80)
@@ -23,10 +26,11 @@ import GF.Text.Pretty(render,render80)
import Data.Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set
import Data.Time(UTCTime)
import qualified Data.ByteString.Lazy as BSL
import GF.Grammar.CanonicalJSON (encodeJSON)
import System.FilePath
import Control.Monad(when,unless,forM_)
import Control.Monad(when,unless,forM,void)
-- | Compile the given GF grammar files. The result is a number of @.gfo@ files
-- and, depending on the options, a @.pgf@ file. (@gf -batch@, @gf -make@)
@@ -47,7 +51,7 @@ mainGFC opts fs = do
extensionIs ext = (== ext) . takeExtension
compileSourceFiles :: Options -> [FilePath] -> IOE ()
compileSourceFiles opts fs =
compileSourceFiles opts fs =
do output <- batchCompile opts fs
exportCanonical output
unless (flag optStopAfterPhase opts == Compile) $
@@ -68,25 +72,22 @@ compileSourceFiles opts fs =
where
ofmts = flag optOutputFormats opts
cnc2haskell (cnc,gr) = do
(res,_) <- runCheck (concretes2haskell opts (srcAbsName gr cnc) gr)
mapM_ writeExport res
cnc2haskell (cnc,gr) =
do mapM_ writeExport $ concretes2haskell opts (srcAbsName gr cnc) gr
abs2canonical (cnc,gr) = do
(canAbs,_) <- runCheck (abstract2canonical absname gr)
writeExport ("canonical/"++render absname++".gf",render80 canAbs)
abs2canonical (cnc,gr) =
writeExport ("canonical/"++render absname++".gf",render80 canAbs)
where
absname = srcAbsName gr cnc
canAbs = abstract2canonical absname gr
cnc2canonical (cnc,gr) = do
(res,_) <- runCheck (concretes2canonical opts (srcAbsName gr cnc) gr)
mapM_ (writeExport.fmap render80) res
cnc2canonical (cnc,gr) =
mapM_ (writeExport.fmap render80) $
concretes2canonical opts (srcAbsName gr cnc) gr
grammar2json (cnc,gr) = do
(gr_canon,_) <- runCheck (grammar2canonical opts absname gr)
return (encodeJSON (render absname ++ ".json") gr_canon)
where
absname = srcAbsName gr cnc
grammar2json (cnc,gr) = encodeJSON (render absname ++ ".json") gr_canon
where absname = srcAbsName gr cnc
gr_canon = grammar2canonical opts absname gr
writeExport (path,s) = writing opts path $ writeUTF8File path s
@@ -95,7 +96,11 @@ compileSourceFiles opts fs =
-- in the 'Options') from the output of 'parallelBatchCompile'.
-- If a @.pgf@ file by the same name already exists and it is newer than the
-- source grammar files (as indicated by the 'UTCTime' argument), it is not
-- recreated. Calls 'writeGrammar' and 'writeOutputs'.
-- recreated. Calls 'writePGF' and 'writeOutputs'.
linkGrammars :: Options -> (UTCTime,[(ModuleName, Grammar)]) -> IOE ()
linkGrammars opts (_,cnc_grs) | FmtLPGF `elem` flag optOutputFormats opts = do
lpgf <- linkl opts (head cnc_grs)
void $ writeLPGF opts lpgf
linkGrammars opts (t_src,~cnc_grs@(~(cnc,gr):_)) =
do let abs = render (srcAbsName gr cnc)
pgfFile = outputPath opts (grammarName' opts abs<.>"pgf")
@@ -105,8 +110,10 @@ linkGrammars opts (t_src,~cnc_grs@(~(cnc,gr):_)) =
if t_pgf >= Just t_src
then putIfVerb opts $ pgfFile ++ " is up-to-date."
else do pgfs <- mapM (link opts) cnc_grs
let pgf = foldl1 (\one two -> fromMaybe two (unionPGF one two)) pgfs
writeGrammar opts pgf
let pgf0 = foldl1 unionPGF pgfs
probs <- maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf0
let pgf = setProbabilities probs pgf0
writePGF opts pgf
writeOutputs opts pgf
compileCFFiles :: Options -> [FilePath] -> IOE ()
@@ -116,11 +123,12 @@ compileCFFiles opts fs = do
startCat <- case rules of
(Rule cat _ _ : _) -> return cat
_ -> fail "empty CFG"
probs <- liftIO (maybe (return Map.empty) readProbabilitiesFromFile (flag optProbsFile opts))
let pgf = cf2pgf opts (last fs) (mkCFG startCat Set.empty rules) probs
let pgf = cf2pgf (last fs) (mkCFG startCat Set.empty rules)
unless (flag optStopAfterPhase opts == Compile) $
do writeGrammar opts pgf
writeOutputs opts pgf
do probs <- liftIO (maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf)
let pgf' = setProbabilities probs $ if flag optOptimizePGF opts then optimizePGF pgf else pgf
writePGF opts pgf'
writeOutputs opts pgf'
unionPGFFiles :: Options -> [FilePath] -> IOE ()
unionPGFFiles opts fs =
@@ -138,11 +146,14 @@ unionPGFFiles opts fs =
doIt =
do pgfs <- mapM readPGFVerbose fs
let pgf = foldl1 (\one two -> fromMaybe two (unionPGF one two)) pgfs
let pgfFile = outputPath opts (grammarName opts pgf <.> "pgf")
let pgf0 = foldl1 unionPGF pgfs
pgf1 = if flag optOptimizePGF opts then optimizePGF pgf0 else pgf0
probs <- liftIO (maybe (return . defaultProbabilities) readProbabilitiesFromFile (flag optProbsFile opts) pgf1)
let pgf = setProbabilities probs pgf1
pgfFile = outputPath opts (grammarName opts pgf <.> "pgf")
if pgfFile `elem` fs
then putStrLnE $ "Refusing to overwrite " ++ pgfFile
else writeGrammar opts pgf
else void $ writePGF opts pgf
writeOutputs opts pgf
readPGFVerbose f =
@@ -152,41 +163,51 @@ unionPGFFiles opts fs =
-- Calls 'exportPGF'.
writeOutputs :: Options -> PGF -> IOE ()
writeOutputs opts pgf = do
sequence_ [writeOutput opts name str
sequence_ [writeOutput opts name str
| fmt <- flag optOutputFormats opts,
(name,str) <- exportPGF opts fmt pgf]
-- | Write the result of compiling a grammar (e.g. with 'compileToPGF' or
-- 'link') to a @.pgf@ file.
-- A split PGF file is output if the @-split-pgf@ option is used.
writeGrammar :: Options -> PGF -> IOE ()
writeGrammar opts pgf =
if fst (flag optLinkTargets opts)
then if flag optSplitPGF opts
then writeSplitPGF
else writeNormalPGF
else return ()
writePGF :: Options -> PGF -> IOE [FilePath]
writePGF opts pgf =
if flag optSplitPGF opts then writeSplitPGF else writeNormalPGF
where
writeNormalPGF =
do let outfile = outputPath opts (grammarName opts pgf <.> "pgf")
writing opts outfile (writePGF outfile pgf)
writing opts outfile $ encodeFile outfile pgf
return [outfile]
writeSplitPGF =
do let outfile = outputPath opts (grammarName opts pgf <.> "pgf")
writing opts outfile $ writePGF outfile pgf
forM_ (Map.toList (languages pgf)) $ \(concrname,concr) -> do
let outfile = outputPath opts (concrname <.> "pgf_c")
writing opts outfile (writeConcr outfile concr)
writing opts outfile $ BSL.writeFile outfile (runPut (putSplitAbs pgf))
--encodeFile_ outfile (putSplitAbs pgf)
outfiles <- forM (Map.toList (concretes pgf)) $ \cnc -> do
let outfile = outputPath opts (showCId (fst cnc) <.> "pgf_c")
writing opts outfile $ encodeFile outfile cnc
return outfile
return (outfile:outfiles)
writeOutput :: Options -> FilePath-> String -> IOE ()
writeOutput opts file str = writing opts path $ writeUTF8File path str
where path = outputPath opts file
writeLPGF :: Options -> LPGF -> IOE FilePath
writeLPGF opts lpgf = do
let
grammarName = fromMaybe (showCId (LPGF.abstractName lpgf)) (flag optName opts)
outfile = outputPath opts (grammarName <.> "lpgf")
writing opts outfile $ liftIO $ LPGF.encodeFile outfile lpgf
return outfile
writeOutput :: Options -> FilePath-> String -> IOE FilePath
writeOutput opts file str = do
let outfile = outputPath opts file
writing opts outfile $ writeUTF8File outfile str
return outfile
-- * Useful helper functions
grammarName :: Options -> PGF -> String
grammarName opts pgf = grammarName' opts (abstractName pgf)
grammarName opts pgf = grammarName' opts (showCId (abstractName pgf))
grammarName' opts abs = fromMaybe abs (flag optName opts)
outputJustPGF opts = null (flag optOutputFormats opts) && not (flag optSplitPGF opts)

View File

@@ -16,18 +16,18 @@
{-# LANGUAGE CPP #-}
module GF.Data.BacktrackM (
-- * the backtracking state monad
BacktrackM,
-- * monad specific utilities
member,
cut,
-- * running the monad
foldBM, runBM,
foldSolutions, solutions,
foldFinalStates, finalStates,
-- * reexport the 'MonadState' class
module Control.Monad.State.Class,
) where
BacktrackM,
-- * monad specific utilities
member,
cut,
-- * running the monad
foldBM, runBM,
foldSolutions, solutions,
foldFinalStates, finalStates,
-- * reexport the 'MonadState' class
module Control.Monad.State.Class,
) where
import Data.List
import Control.Applicative
@@ -70,7 +70,7 @@ instance Applicative (BacktrackM s) where
instance Monad (BacktrackM s) where
return a = BM (\c s b -> c a s b)
BM m >>= k = BM (\c s b -> m (\a s b -> unBM (k a) c s b) s b)
where unBM (BM m) = m
where unBM (BM m) = m
#if !(MIN_VERSION_base(4,13,0))
fail = Fail.fail

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/11/10 16:43:44 $
-- > CVS $Date: 2005/11/10 16:43:44 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.2 $
--
@@ -34,7 +34,7 @@ import Data.Set (Set)
import qualified Data.Set as Set
data Graph n a b = Graph [n] ![Node n a] ![Edge n b]
deriving (Eq,Show)
deriving (Eq,Show)
type Node n a = (n,a)
type Edge n b = (n,n,b)
@@ -63,7 +63,7 @@ emap f (Graph c ns es) = Graph c ns [(x,y,f l) | (x,y,l) <- es]
-- | Add a node to the graph.
newNode :: a -- ^ Node label
-> Graph n a b
-> Graph n a b
-> (Graph n a b,n) -- ^ Node graph and name of new node
newNode l (Graph (c:cs) ns es) = (Graph cs ((c,l):ns) es, c)
@@ -83,7 +83,7 @@ newEdges es g = foldl' (flip newEdge) g es
-- lazy version:
-- newEdges es' (Graph c ns es) = Graph c ns (es'++es)
insertEdgeWith :: Eq n =>
insertEdgeWith :: Eq n =>
(b -> b -> b) -> Edge n b -> Graph n a b -> Graph n a b
insertEdgeWith f e@(x,y,l) (Graph c ns es) = Graph c ns (h es)
where h [] = [e]
@@ -97,7 +97,7 @@ removeNode n = removeNodes (Set.singleton n)
-- | Remove a set of nodes and all edges to and from those nodes.
removeNodes :: Ord n => Set n -> Graph n a b -> Graph n a b
removeNodes xs (Graph c ns es) = Graph c ns' es'
where
where
keepNode n = not (Set.member n xs)
ns' = [ x | x@(n,_) <- ns, keepNode n ]
es' = [ e | e@(f,t,_) <- es, keepNode f && keepNode t ]
@@ -105,7 +105,7 @@ removeNodes xs (Graph c ns es) = Graph c ns' es'
-- | Get a map of node names to info about each node.
nodeInfo :: Ord n => Graph n a b -> NodeInfo n a b
nodeInfo g = Map.fromList [ (n, (x, fn inc n, fn out n)) | (n,x) <- nodes g ]
where
where
inc = groupEdgesBy edgeTo g
out = groupEdgesBy edgeFrom g
fn m n = fromMaybe [] (Map.lookup n m)
@@ -148,16 +148,16 @@ reverseGraph :: Graph n a b -> Graph n a b
reverseGraph (Graph c ns es) = Graph c ns [ (t,f,l) | (f,t,l) <- es ]
-- | Add the nodes from the second graph to the first graph.
-- The nodes in the second graph will be renamed using the name
-- The nodes in the second graph will be renamed using the name
-- supply in the first graph.
-- This function is more efficient when the second graph
-- is smaller than the first.
mergeGraphs :: Ord m => Graph n a b -> Graph m a b
mergeGraphs :: Ord m => Graph n a b -> Graph m a b
-> (Graph n a b, m -> n) -- ^ The new graph and a function translating
-- the old names of nodes in the second graph
-- to names in the new graph.
mergeGraphs (Graph c ns1 es1) g2 = (Graph c' (ns2++ns1) (es2++es1), newName)
where
where
(xs,c') = splitAt (length (nodes g2)) c
newNames = Map.fromList (zip (map fst (nodes g2)) xs)
newName n = fromJust $ Map.lookup n newNames
@@ -170,7 +170,7 @@ renameNodes :: (n -> m) -- ^ renaming function
-> Graph n a b -> Graph m a b
renameNodes newName c (Graph _ ns es) = Graph c ns' es'
where ns' = map' (\ (n,x) -> (newName n,x)) ns
es' = map' (\ (f,t,l) -> (newName f, newName t, l)) es
es' = map' (\ (f,t,l) -> (newName f, newName t, l)) es
-- | A strict 'map'
map' :: (a -> b) -> [a] -> [b]

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/09/15 18:10:44 $
-- > CVS $Date: 2005/09/15 18:10:44 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.2 $
--
@@ -13,14 +13,14 @@
-----------------------------------------------------------------------------
module GF.Data.Graphviz (
Graph(..), GraphType(..),
Node(..), Edge(..),
Attr,
addSubGraphs,
setName,
setAttr,
prGraphviz
) where
Graph(..), GraphType(..),
Node(..), Edge(..),
Attr,
addSubGraphs,
setName,
setAttr,
prGraphviz
) where
import Data.Char
@@ -70,14 +70,14 @@ prGraphviz g@(Graph t i _ _ _ _) =
graphtype t ++ " " ++ maybe "" esc i ++ " {\n" ++ prGraph g ++ "}\n"
prSubGraph :: Graph -> String
prSubGraph g@(Graph _ i _ _ _ _) =
prSubGraph g@(Graph _ i _ _ _ _) =
"subgraph" ++ " " ++ maybe "" esc i ++ " {\n" ++ prGraph g ++ "}"
prGraph :: Graph -> String
prGraph (Graph t id at ns es ss) =
prGraph (Graph t id at ns es ss) =
unlines $ map (++";") (map prAttr at
++ map prNode ns
++ map (prEdge t) es
++ map prNode ns
++ map (prEdge t) es
++ map prSubGraph ss)
graphtype :: GraphType -> String
@@ -96,7 +96,7 @@ edgeop Undirected = "--"
prAttrList :: [Attr] -> String
prAttrList [] = ""
prAttrList at = "[" ++ join "," (map prAttr at) ++ "]"
prAttrList at = "[" ++ join "," (map prAttr at) ++ "]"
prAttr :: Attr -> String
prAttr (n,v) = esc n ++ " = " ++ esc v

View File

@@ -0,0 +1,57 @@
-- | In order to build an IntMap in one pass, we need a map data structure with
-- fast lookup in both keys and values.
-- This is achieved by keeping a separate reversed map of values to keys during building.
module GF.Data.IntMapBuilder where
import Data.IntMap (IntMap)
import qualified Data.IntMap as IntMap
import Data.Hashable (Hashable)
import Data.HashMap.Strict (HashMap)
import qualified Data.HashMap.Strict as HashMap
import Data.Tuple (swap)
import Prelude hiding (lookup)
data IMB a = IMB {
intMap :: IntMap a,
valMap :: HashMap a Int
}
-- | An empty IMB
empty :: (Eq a, Hashable a) => IMB a
empty = IMB {
intMap = IntMap.empty,
valMap = HashMap.empty
}
-- | Lookup a value
lookup :: (Eq a, Hashable a) => a -> IMB a -> Maybe Int
lookup a IMB { valMap = vm } = HashMap.lookup a vm
-- | Insert without any lookup
insert :: (Eq a, Hashable a) => a -> IMB a -> (Int, IMB a)
insert a IMB { intMap = im, valMap = vm } =
let
ix = IntMap.size im
im' = IntMap.insert ix a im
vm' = HashMap.insert a ix vm
imb' = IMB { intMap = im', valMap = vm' }
in
(ix, imb')
-- | Insert only when lookup fails
insert' :: (Eq a, Hashable a) => a -> IMB a -> (Int, IMB a)
insert' a imb =
case lookup a imb of
Just ix -> (ix, imb)
Nothing -> insert a imb
-- | Build IMB from existing IntMap
fromIntMap :: (Eq a, Hashable a) => IntMap a -> IMB a
fromIntMap im = IMB {
intMap = im,
valMap = HashMap.fromList (map swap (IntMap.toList im))
}
-- | Get IntMap from IMB
toIntMap :: (Eq a, Hashable a) => IMB a -> IntMap a
toIntMap = intMap

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/11/11 16:12:41 $
-- > CVS $Date: 2005/11/11 16:12:41 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.22 $
--
@@ -15,34 +15,34 @@
-----------------------------------------------------------------------------
module GF.Data.Operations (
-- ** The Error monad
Err(..), err, maybeErr, testErr, fromErr, errIn,
lookupErr,
-- ** The Error monad
Err(..), err, maybeErr, testErr, fromErr, errIn,
lookupErr,
-- ** Error monad class
ErrorMonad(..), checks, --doUntil, allChecks, checkAgain,
liftErr,
-- ** Error monad class
ErrorMonad(..), checks, --doUntil, allChecks, checkAgain,
liftErr,
-- ** Checking
checkUnique, unifyMaybeBy, unifyMaybe,
-- ** Checking
checkUnique, unifyMaybeBy, unifyMaybe,
-- ** Monadic operations on lists and pairs
mapPairsM, pairM,
-- ** Printing
indent, (+++), (++-), (++++), (+++-), (+++++),
prUpper, prReplicate, prTList, prQuotedString, prParenth, prCurly,
prBracket, prArgList, prSemicList, prCurlyList, restoreEscapes,
numberedParagraphs, prConjList, prIfEmpty, wrapLines,
-- ** Monadic operations on lists and pairs
mapPairsM, pairM,
-- ** Topological sorting
topoTest, topoTest2,
-- ** Printing
indent, (+++), (++-), (++++), (+++-), (+++++),
prUpper, prReplicate, prTList, prQuotedString, prParenth, prCurly,
prBracket, prArgList, prSemicList, prCurlyList, restoreEscapes,
numberedParagraphs, prConjList, prIfEmpty, wrapLines,
-- ** Topological sorting
topoTest, topoTest2,
-- ** Misc
readIntArg,
iterFix, chunks,
) where
-- ** Misc
readIntArg,
iterFix, chunks,
) where
import Data.Char (isSpace, toUpper, isSpace, isDigit)
import Data.List (nub, partition, (\\))
@@ -107,7 +107,7 @@ indent i s = replicate i ' ' ++ s
(+++), (++-), (++++), (+++-), (+++++) :: String -> String -> String
a +++ b = a ++ " " ++ b
a ++- "" = a
a ++- "" = a
a ++- b = a +++ b
a ++++ b = a ++ "\n" ++ b
@@ -145,20 +145,20 @@ prCurly s = "{" ++ s ++ "}"
prBracket s = "[" ++ s ++ "]"
prArgList, prSemicList, prCurlyList :: [String] -> String
prArgList = prParenth . prTList ","
prArgList = prParenth . prTList ","
prSemicList = prTList " ; "
prCurlyList = prCurly . prSemicList
restoreEscapes :: String -> String
restoreEscapes s =
case s of
restoreEscapes s =
case s of
[] -> []
'"' : t -> '\\' : '"' : restoreEscapes t
'\\': t -> '\\' : '\\' : restoreEscapes t
c : t -> c : restoreEscapes t
numberedParagraphs :: [[String]] -> [String]
numberedParagraphs t = case t of
numberedParagraphs t = case t of
[] -> []
p:[] -> p
_ -> concat [(show n ++ ".") : s | (n,s) <- zip [1..] t]
@@ -204,12 +204,12 @@ topoTest2 g0 = maybe (Right cycles) Left (tsort g)
([],[]) -> Just []
([],_) -> Nothing
(ns,rest) -> (leaves:) `fmap` tsort [(n,es \\ leaves) | (n,es)<-rest]
where leaves = map fst ns
where leaves = map fst ns
-- | Fix point iterator (for computing e.g. transitive closures or reachability)
iterFix :: Eq a => ([a] -> [a]) -> [a] -> [a]
iterFix more start = iter start start
iterFix more start = iter start start
where
iter old new = if (null new')
then old
@@ -241,7 +241,7 @@ liftErr e = err raise return e
{-
instance ErrorMonad (STM s) where
raise msg = STM (\s -> raise msg)
handle (STM f) g = STM (\s -> (f s)
handle (STM f) g = STM (\s -> (f s)
`handle` (\e -> let STM g' = (g e) in
g' s))

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/10/26 17:13:13 $
-- > CVS $Date: 2005/10/26 17:13:13 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.1 $
--
@@ -83,7 +83,7 @@ transitiveClosure r = fix (Map.map growSet) r
where growSet ys = foldl Set.union ys (map (allRelated r) $ Set.toList ys)
reflexiveClosure_ :: Ord a => [a] -- ^ The set over which the relation is defined.
-> Rel a -> Rel a
-> Rel a -> Rel a
reflexiveClosure_ u r = relates [(x,x) | x <- u] r
-- | Uses 'domain'
@@ -104,7 +104,7 @@ reflexiveElements :: Ord a => Rel a -> Set a
reflexiveElements r = Set.fromList [ x | (x,ys) <- Map.toList r, x `Set.member` ys ]
-- | Keep the related pairs for which the predicate is true.
filterRel :: Ord a => (a -> a -> Bool) -> Rel a -> Rel a
filterRel :: Ord a => (a -> a -> Bool) -> Rel a -> Rel a
filterRel p = fst . purgeEmpty . Map.mapWithKey (Set.filter . p)
-- | Remove keys that map to no elements.
@@ -112,16 +112,16 @@ purgeEmpty :: Ord a => Rel a -> (Rel a, Set a)
purgeEmpty r = let (r',r'') = Map.partition (not . Set.null) r
in (r', Map.keysSet r'')
-- | Get the equivalence classes from an equivalence relation.
-- | Get the equivalence classes from an equivalence relation.
equivalenceClasses :: Ord a => Rel a -> [Set a]
equivalenceClasses r = equivalenceClasses_ (Map.keys r) r
where equivalenceClasses_ [] _ = []
equivalenceClasses_ (x:xs) r = ys:equivalenceClasses_ zs r
where ys = allRelated r x
zs = [x' | x' <- xs, not (x' `Set.member` ys)]
where ys = allRelated r x
zs = [x' | x' <- xs, not (x' `Set.member` ys)]
isTransitive :: Ord a => Rel a -> Bool
isTransitive r = and [z `Set.member` ys | (x,ys) <- Map.toList r,
isTransitive r = and [z `Set.member` ys | (x,ys) <- Map.toList r,
y <- Set.toList ys, z <- Set.toList (allRelated r y)]
isReflexive :: Ord a => Rel a -> Bool
@@ -181,7 +181,7 @@ remove x r = let (mss,r') = Map.updateLookupWithKey (\_ _ -> Nothing) x r
Nothing -> (r', Set.empty, Set.empty)
-- remove element from all incoming and outgoing sets
-- of other elements
Just (is,os) ->
Just (is,os) ->
let r'' = foldr (\i -> Map.adjust (\ (is',os') -> (is', Set.delete x os')) i) r' $ Set.toList is
r''' = foldr (\o -> Map.adjust (\ (is',os') -> (Set.delete x is', os')) o) r'' $ Set.toList os
in (r''', is, os)
@@ -190,4 +190,4 @@ incoming :: Ord a => a -> Rel' a -> Set a
incoming x r = maybe Set.empty fst $ Map.lookup x r
--outgoing :: Ord a => a -> Rel' a -> Set a
--outgoing x r = maybe Set.empty snd $ Map.lookup x r
--outgoing x r = maybe Set.empty snd $ Map.lookup x r

View File

@@ -4,7 +4,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/10/26 18:47:16 $
-- > CVS $Date: 2005/10/26 18:47:16 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.6 $
--
@@ -33,7 +33,7 @@ longerThan n = not . notLongerThan n
lookupList :: Eq a => a -> [(a, b)] -> [b]
lookupList a [] = []
lookupList a (p:ps) | a == fst p = snd p : lookupList a ps
| otherwise = lookupList a ps
| otherwise = lookupList a ps
split :: [a] -> ([a], [a])
split (x : y : as) = (x:xs, y:ys)
@@ -48,8 +48,8 @@ splitBy p (a : as) = if p a then (a:xs, ys) else (xs, a:ys)
foldMerge :: (a -> a -> a) -> a -> [a] -> a
foldMerge merge zero = fm
where fm [] = zero
fm [a] = a
fm abs = let (as, bs) = split abs in fm as `merge` fm bs
fm [a] = a
fm abs = let (as, bs) = split abs in fm as `merge` fm bs
select :: [a] -> [(a, [a])]
select [] = []
@@ -68,7 +68,7 @@ safeInit :: [a] -> [a]
safeInit [] = []
safeInit xs = init xs
-- | Sorts and then groups elements given an ordering of the
-- | Sorts and then groups elements given an ordering of the
-- elements.
sortGroupBy :: (a -> a -> Ordering) -> [a] -> [[a]]
sortGroupBy f = groupBy (compareEq f) . sortBy f

View File

@@ -29,7 +29,7 @@ stripInfo i = case i of
AbsCat _ -> i
AbsFun mt mi me mb -> AbsFun mt mi Nothing mb
ResParam mp mt -> ResParam mp Nothing
ResValue lt _ -> i ----
ResValue lt -> i ----
ResOper mt md -> ResOper mt Nothing
ResOverload is fs -> ResOverload is [(lty, L loc (EInt 0)) | (lty,L loc _) <- fs]
CncCat mty mte _ mtf mpmcfg -> CncCat mty Nothing Nothing Nothing Nothing
@@ -108,7 +108,7 @@ sizeInfo i = case i of
sum [sum (map (sizeTerm . patt2term) ps) + sizeTerm t | Just es <- [me], L _ (ps,t) <- es]
ResParam mp mt ->
1 + sum [1 + sum [1 + sizeTerm ty | (_,_,ty) <- co] | Just (L _ ps) <- [mp], (_,co) <- ps]
ResValue _ _ -> 0
ResValue lt -> 0
ResOper mt md -> 1 + msize mt + msize md
ResOverload is fs -> 1 + sum [sizeTerm ty + sizeTerm tr | (L _ ty, L _ tr) <- fs]
CncCat mty _ _ _ _ -> 1 + msize mty -- ignoring lindef, linref and printname

View File

@@ -15,6 +15,7 @@
module GF.Grammar.BNFC(BNFCRule(..), BNFCSymbol, Symbol(..), CFTerm(..), bnfc2cf) where
import GF.Grammar.CFG
import PGF (Token, mkCId)
import Data.List (partition)
type IsList = Bool
@@ -25,7 +26,7 @@ data BNFCRule = BNFCRule {
ruleName :: CFTerm }
| BNFCCoercions {
coerCat :: Cat,
coerNum :: Integer }
coerNum :: Int }
| BNFCTerminator {
termNonEmpty :: Bool,
termCat :: Cat,
@@ -63,12 +64,12 @@ transformRules sepMap (BNFCCoercions c num) = rules ++ [lastRule]
lastRule = Rule (c',[0]) ss rn
where c' = c ++ show num
ss = [Terminal "(", NonTerminal (c,[0]), Terminal ")"]
rn = CFObj ("coercion_" ++ c) []
rn = CFObj (mkCId $ "coercion_" ++ c) []
fRules c n = Rule (c',[0]) ss rn
where c' = if n == 0 then c else c ++ show n
ss = [NonTerminal (c ++ show (n+1),[0])]
rn = CFObj ("coercion_" ++ c') []
rn = CFObj (mkCId $ "coercion_" ++ c') []
transformSymb :: SepMap -> BNFCSymbol -> (String, ParamCFSymbol)
transformSymb sepMap s = case s of
@@ -93,7 +94,7 @@ createListRules' ne isSep symb c = ruleBase : ruleCons
then [NonTerminal (c,[0]) | ne]
else [NonTerminal (c,[0]) | ne] ++
[Terminal symb | symb /= "" && ne]
rn = CFObj ("Base" ++ c) []
rn = CFObj (mkCId $ "Base" ++ c) []
ruleCons
| isSep && symb /= "" && not ne = [Rule ("List" ++ c,[1]) smbs0 rn
,Rule ("List" ++ c,[1]) smbs1 rn]
@@ -106,4 +107,4 @@ createListRules' ne isSep symb c = ruleBase : ruleCons
smbs = [NonTerminal (c,[0])] ++
[Terminal symb | symb /= ""] ++
[NonTerminal ("List" ++ c,[0])]
rn = CFObj ("Cons" ++ c) []
rn = CFObj (mkCId $ "Cons" ++ c) []

View File

@@ -10,9 +10,9 @@
module GF.Grammar.Binary(VersionTagged(..),decodeModuleHeader,decodeModule,encodeModule) where
import Prelude hiding (catch)
import Control.Monad
import Control.Exception(catch,ErrorCall(..),throwIO)
import Data.Binary
import PGF.Internal(Binary(..),Word8,putWord8,getWord8,encodeFile,decodeFile)
import qualified Data.Map as Map(empty)
import qualified Data.ByteString.Char8 as BS
@@ -22,11 +22,11 @@ import GF.Infra.Option
import GF.Infra.UseIO(MonadIO(..))
import GF.Grammar.Grammar
import PGF2(Literal(..))
import PGF2.Transactions(Symbol(..))
import PGF() -- Binary instances
import PGF.Internal(Literal(..))
-- Please change this every time when the GFO format is changed
gfoVersion = "GF05"
gfoVersion = "GF04"
instance Binary Ident where
put id = put (ident2utf8 id)
@@ -44,9 +44,9 @@ instance Binary Grammar where
get = fmap mGrammar get
instance Binary ModuleInfo where
put mi = do put (mtype mi,mstatus mi,mflags mi,mextend mi,mwith mi,mopens mi,mexdeps mi,msrc mi,jments mi)
get = do (mtype,mstatus,mflags,mextend,mwith,mopens,med,msrc,jments) <- get
return (ModInfo mtype mstatus mflags mextend mwith mopens med msrc jments)
put mi = do put (mtype mi,mstatus mi,mflags mi,mextend mi,mwith mi,mopens mi,mexdeps mi,msrc mi,mseqs mi,jments mi)
get = do (mtype,mstatus,mflags,mextend,mwith,mopens,med,msrc,mseqs,jments) <- get
return (ModInfo mtype mstatus mflags mextend mwith mopens med msrc mseqs jments)
instance Binary ModuleType where
put MTAbstract = putWord8 0
@@ -103,19 +103,24 @@ instance Binary Options where
toString (LInt n) = show n
toString (LFlt d) = show d
instance Binary PMCFGCat where
put (PMCFGCat r rs) = put (r,rs)
get = get >>= \(r,rs) -> return (PMCFGCat r rs)
instance Binary Production where
put (Production res funid args) = put (res,funid,args)
get = do res <- get
funid <- get
args <- get
return (Production res funid args)
instance Binary PMCFGRule where
put (PMCFGRule res args rules) = put (res,args,rules)
get = get >>= \(res,args,rules) -> return (PMCFGRule res args rules)
instance Binary PMCFG where
put (PMCFG prods funs) = put (prods,funs)
get = do prods <- get
funs <- get
return (PMCFG prods funs)
instance Binary Info where
put (AbsCat x) = putWord8 0 >> put x
put (AbsFun w x y z) = putWord8 1 >> put (w,x,y,z)
put (ResParam x y) = putWord8 2 >> put (x,y)
put (ResValue x y) = putWord8 3 >> put (x,y)
put (ResValue x) = putWord8 3 >> put x
put (ResOper x y) = putWord8 4 >> put (x,y)
put (ResOverload x y)= putWord8 5 >> put (x,y)
put (CncCat v w x y z)=putWord8 6 >> put (v,w,x,y,z)
@@ -126,7 +131,7 @@ instance Binary Info where
0 -> get >>= \x -> return (AbsCat x)
1 -> get >>= \(w,x,y,z) -> return (AbsFun w x y z)
2 -> get >>= \(x,y) -> return (ResParam x y)
3 -> get >>= \(x,y) -> return (ResValue x y)
3 -> get >>= \x -> return (ResValue x)
4 -> get >>= \(x,y) -> return (ResOper x y)
5 -> get >>= \(x,y) -> return (ResOverload x y)
6 -> get >>= \(v,w,x,y,z)->return (CncCat v w x y z)
@@ -177,13 +182,14 @@ instance Binary Term where
put (QC x) = putWord8 25 >> put x
put (C x y) = putWord8 26 >> put (x,y)
put (Glue x y) = putWord8 27 >> put (x,y)
put (EPatt x y z) = putWord8 28 >> put (x,y,z)
put (EPatt x) = putWord8 28 >> put x
put (EPattType x) = putWord8 29 >> put x
put (ELincat x y) = putWord8 30 >> put (x,y)
put (ELin x y) = putWord8 31 >> put (x,y)
put (FV x) = putWord8 32 >> put x
put (Alts x y) = putWord8 33 >> put (x,y)
put (Strs x) = putWord8 34 >> put x
put (Error x) = putWord8 35 >> put x
get = do tag <- getWord8
case tag of
@@ -215,13 +221,14 @@ instance Binary Term where
25 -> get >>= \x -> return (QC x)
26 -> get >>= \(x,y) -> return (C x y)
27 -> get >>= \(x,y) -> return (Glue x y)
28 -> get >>= \(x,y,z) -> return (EPatt x y z)
28 -> get >>= \x -> return (EPatt x)
29 -> get >>= \x -> return (EPattType x)
30 -> get >>= \(x,y) -> return (ELincat x y)
31 -> get >>= \(x,y) -> return (ELin x y)
32 -> get >>= \x -> return (FV x)
33 -> get >>= \(x,y) -> return (Alts x y)
34 -> get >>= \x -> return (Strs x)
35 -> get >>= \x -> return (Error x)
_ -> decodingError
instance Binary Patt where
@@ -237,8 +244,8 @@ instance Binary Patt where
put (PAs x y) = putWord8 10 >> put (x,y)
put (PNeg x) = putWord8 11 >> put x
put (PAlt x y) = putWord8 12 >> put (x,y)
put (PSeq minx maxx x miny maxy y) = putWord8 13 >> put (minx,maxx,x,miny,maxy,y)
put (PRep minx maxx x)= putWord8 14 >> put (minx,maxx,x)
put (PSeq x y) = putWord8 13 >> put (x,y)
put (PRep x) = putWord8 14 >> put x
put (PChar) = putWord8 15
put (PChars x) = putWord8 16 >> put x
put (PMacro x) = putWord8 17 >> put x
@@ -259,8 +266,8 @@ instance Binary Patt where
10 -> get >>= \(x,y) -> return (PAs x y)
11 -> get >>= \x -> return (PNeg x)
12 -> get >>= \(x,y) -> return (PAlt x y)
13 -> get >>= \(minx,maxx,x,miny,maxy,y) -> return (PSeq minx maxx x miny maxy y)
14 -> get >>= \(minx,maxx,x)-> return (PRep minx maxx x)
13 -> get >>= \(x,y) -> return (PSeq x y)
14 -> get >>= \x -> return (PRep x)
15 -> return (PChar)
16 -> get >>= \x -> return (PChars x)
17 -> get >>= \x -> return (PMacro x)
@@ -291,53 +298,6 @@ instance Binary Label where
1 -> fmap LVar get
_ -> decodingError
instance Binary BindType where
put Explicit = putWord8 0
put Implicit = putWord8 1
get = do tag <- getWord8
case tag of
0 -> return Explicit
1 -> return Implicit
_ -> decodingError
instance Binary Literal where
put (LStr s) = putWord8 0 >> put s
put (LInt i) = putWord8 1 >> put i
put (LFlt d) = putWord8 2 >> put d
get = do tag <- getWord8
case tag of
0 -> liftM LStr get
1 -> liftM LInt get
2 -> liftM LFlt get
_ -> decodingError
instance Binary Symbol where
put (SymCat d r rs) = putWord8 0 >> put (d,r,rs)
put (SymLit n l) = putWord8 1 >> put (n,l)
put (SymVar n l) = putWord8 2 >> put (n,l)
put (SymKS ts) = putWord8 3 >> put ts
put (SymKP d vs) = putWord8 4 >> put (d,vs)
put SymBIND = putWord8 5
put SymSOFT_BIND = putWord8 6
put SymNE = putWord8 7
put SymSOFT_SPACE = putWord8 8
put SymCAPIT = putWord8 9
put SymALL_CAPIT = putWord8 10
get = do tag <- getWord8
case tag of
0 -> liftM3 SymCat get get get
1 -> liftM2 SymLit get get
2 -> liftM2 SymVar get get
3 -> liftM SymKS get
4 -> liftM2 (\d vs -> SymKP d vs) get get
5 -> return SymBIND
6 -> return SymSOFT_BIND
7 -> return SymNE
8 -> return SymSOFT_SPACE
9 -> return SymCAPIT
10-> return SymALL_CAPIT
_ -> decodingError
--putGFOVersion = mapM_ (putWord8 . fromIntegral . ord) gfoVersion
--getGFOVersion = replicateM (length gfoVersion) (fmap (chr . fromIntegral) getWord8)
--putGFOVersion = put gfoVersion
@@ -372,7 +332,7 @@ decodeModuleHeader :: MonadIO io => FilePath -> io (VersionTagged Module)
decodeModuleHeader = liftIO . fmap (fmap conv) . decodeFile'
where
conv (m,mtype,mstatus,mflags,mextend,mwith,mopens,med,msrc) =
(m,ModInfo mtype mstatus mflags mextend mwith mopens med msrc Map.empty)
(m,ModInfo mtype mstatus mflags mextend mwith mopens med msrc Nothing Map.empty)
encodeModule :: MonadIO io => FilePath -> SourceModule -> io ()
encodeModule fpath mo = liftIO $ encodeFile fpath (Tagged mo)

View File

@@ -4,11 +4,10 @@
--
-- Context-free grammar representation and manipulation.
----------------------------------------------------------------------
module GF.Grammar.CFG(Cat,Token, module GF.Grammar.CFG) where
module GF.Grammar.CFG where
import GF.Data.Utilities
import PGF2(Fun,Cat)
import PGF2.Transactions(Token)
import PGF
import GF.Data.Relation
import Data.Map (Map)
@@ -21,6 +20,8 @@ import qualified Data.Set as Set
-- * Types
--
type Cat = String
data Symbol c t = NonTerminal c | Terminal t
deriving (Eq, Ord, Show)
@@ -38,12 +39,12 @@ data Grammar c t = Grammar {
deriving (Eq, Ord, Show)
data CFTerm
= CFObj Fun [CFTerm] -- ^ an abstract syntax function with arguments
= CFObj CId [CFTerm] -- ^ an abstract syntax function with arguments
| CFAbs Int CFTerm -- ^ A lambda abstraction. The Int is the variable id.
| CFApp CFTerm CFTerm -- ^ Application
| CFRes Int -- ^ The result of the n:th (0-based) non-terminal
| CFVar Int -- ^ A lambda-bound variable
| CFMeta Fun -- ^ A metavariable
| CFMeta CId -- ^ A metavariable
deriving (Eq, Ord, Show)
type CFSymbol = Symbol Cat Token
@@ -231,7 +232,7 @@ uniqueFuns = snd . mapAccumL uniqueFun Set.empty
uniqueFun funs (Rule cat items (CFObj fun args)) = (Set.insert fun' funs,Rule cat items (CFObj fun' args))
where
fun' = head [fun'|suffix<-"":map show ([2..]::[Int]),
let fun'=fun++suffix,
let fun'=mkCId (showCId fun++suffix),
not (fun' `Set.member` funs)]
-- | Gets all rules in a CFG.
@@ -309,12 +310,12 @@ prProductions prods =
prCFTerm :: CFTerm -> String
prCFTerm = pr 0
where
pr p (CFObj f args) = paren p (f ++ " (" ++ concat (intersperse "," (map (pr 0) args)) ++ ")")
pr p (CFObj f args) = paren p (showCId f ++ " (" ++ concat (intersperse "," (map (pr 0) args)) ++ ")")
pr p (CFAbs i t) = paren p ("\\x" ++ show i ++ ". " ++ pr 0 t)
pr p (CFApp t1 t2) = paren p (pr 1 t1 ++ "(" ++ pr 0 t2 ++ ")")
pr _ (CFRes i) = "$" ++ show i
pr _ (CFVar i) = "x" ++ show i
pr _ (CFMeta c) = "?" ++ c
pr _ (CFMeta c) = "?" ++ showCId c
paren 0 x = x
paren 1 x = "(" ++ x ++ ")"
@@ -322,12 +323,12 @@ prCFTerm = pr 0
-- * CFRule Utilities
--
ruleFun :: Rule c t -> Fun
ruleFun :: Rule c t -> CId
ruleFun (Rule _ _ t) = f t
where f (CFObj n _) = n
f (CFApp _ x) = f x
f (CFAbs _ x) = f x
f _ = ""
f _ = mkCId ""
-- | Check if any of the categories used on the right-hand side
-- are in the given list of categories.
@@ -335,7 +336,7 @@ anyUsedBy :: Eq c => [c] -> Rule c t -> Bool
anyUsedBy cs (Rule _ ss _) = any (`elem` cs) (filterCats ss)
mkCFTerm :: String -> CFTerm
mkCFTerm n = CFObj n []
mkCFTerm n = CFObj (mkCId n) []
ruleIsNonRecursive :: Ord c => Set c -> Rule c t -> Bool
ruleIsNonRecursive cs = noCatsInSet cs . ruleRhs

View File

@@ -9,11 +9,8 @@
{-# LANGUAGE DeriveTraversable #-}
module GF.Grammar.Canonical where
import Prelude hiding ((<>))
import GF.Text.Pretty
import GF.Infra.Ident (RawIdent)
import PGF(Literal(..))
-- | A Complete grammar
data Grammar = Grammar Abstract [Concrete] deriving Show
@@ -33,7 +30,7 @@ data TypeApp = TypeApp CatId [Type] deriving Show
data TypeBinding = TypeBinding VarId Type deriving Show
--------------------------------------------------------------------------------
-- ** Concreate syntax
-- ** Concrete syntax
-- | Concrete Syntax
data Concrete = Concrete ModId ModId Flags [ParamDef] [LincatDef] [LinDef]
@@ -60,7 +57,7 @@ newtype ParamType = ParamTypeId ParamId deriving (Eq,Ord,Show)
-- | Linearization value, RHS of @lin@
data LinValue = ConcatValue LinValue LinValue
| LiteralValue Literal
| LiteralValue LinLiteral
| ErrorValue String
| ParamConstant ParamValue
| PredefValue PredefId
@@ -76,6 +73,11 @@ data LinValue = ConcatValue LinValue LinValue
| CommentedValue String LinValue
deriving (Eq,Ord,Show)
data LinLiteral = FloatConstant Float
| IntConstant Int
| StrConstant String
deriving (Eq,Ord,Show)
data LinPattern = ParamPattern ParamPattern
| RecordPattern [RecordRow LinPattern]
| TuplePattern [LinPattern]
@@ -100,9 +102,9 @@ data TableRow rhs = TableRow LinPattern rhs
-- *** Identifiers in Concrete Syntax
newtype PredefId = PredefId Id deriving (Eq,Ord,Show)
newtype LabelId = LabelId Id deriving (Eq,Ord,Show)
data VarValueId = VarValueId QualId deriving (Eq,Ord,Show)
newtype PredefId = PredefId Id deriving (Eq,Ord,Show)
newtype LabelId = LabelId Id deriving (Eq,Ord,Show)
newtype VarValueId = VarValueId QualId deriving (Eq,Ord,Show)
-- | Name of param type or param value
newtype ParamId = ParamId QualId deriving (Eq,Ord,Show)
@@ -113,17 +115,18 @@ newtype ParamId = ParamId QualId deriving (Eq,Ord,Show)
newtype ModId = ModId Id deriving (Eq,Ord,Show)
newtype CatId = CatId Id deriving (Eq,Ord,Show)
newtype FunId = FunId Id deriving (Eq,Show)
newtype FunId = FunId Id deriving (Eq,Ord,Show)
data VarId = Anonymous | VarId Id deriving Show
data VarId = Anonymous | VarId Id deriving (Eq,Show)
newtype Flags = Flags [(FlagName,Literal)] deriving Show
newtype Flags = Flags [(FlagName,FlagValue)] deriving Show
type FlagName = Id
data FlagValue = Str String | Int Int | Flt Double deriving Show
-- *** Identifiers
type Id = RawIdent
type Id = String
data QualId = Qual ModId Id | Unqual Id deriving (Eq,Ord,Show)
--------------------------------------------------------------------------------
@@ -239,13 +242,13 @@ instance PPA LinValue where
VarValue v -> pp v
_ -> parens lv
instance Pretty Literal where pp = ppA
instance Pretty LinLiteral where pp = ppA
instance PPA Literal where
instance PPA LinLiteral where
ppA l = case l of
LFlt f -> pp f
LInt n -> pp n
LStr s -> doubleQuotes s -- hmm
FloatConstant f -> pp f
IntConstant n -> pp n
StrConstant s -> doubleQuotes s -- hmm
instance RhsSeparator LinValue where rhsSep _ = pp "="
@@ -262,6 +265,7 @@ instance PPA LinPattern where
RecordPattern r -> block r
TuplePattern ps -> "<"<>punctuate "," ps<>">"
WildPattern -> pp "_"
_ -> parens p
instance RhsSeparator LinPattern where rhsSep _ = pp "="
@@ -294,6 +298,11 @@ instance Pretty Flags where
where
ppFlag (name,value) = name <+> "=" <+> value <>";"
instance Pretty FlagValue where
pp (Str s) = pp s
pp (Int i) = pp i
pp (Flt d) = pp d
--------------------------------------------------------------------------------
-- | Pretty print atomically (i.e. wrap it in parentheses if necessary)
class Pretty a => PPA a where ppA :: a -> Doc

View File

@@ -7,8 +7,7 @@ import Control.Applicative ((<|>))
import Data.Ratio (denominator, numerator)
import GF.Grammar.Canonical
import Control.Monad (guard)
import GF.Infra.Ident (RawIdent,showRawIdent,rawIdentS)
import PGF(Literal(..))
encodeJSON :: FilePath -> Grammar -> IO ()
encodeJSON fpath g = writeFile fpath (encode g)
@@ -30,7 +29,7 @@ instance JSON Grammar where
-- ** Abstract Syntax
instance JSON Abstract where
showJSON (Abstract absid flags cats funs)
showJSON (Abstract absid flags cats funs)
= makeObj [("abs", showJSON absid),
("flags", showJSON flags),
("cats", showJSON cats),
@@ -82,7 +81,7 @@ instance JSON TypeBinding where
-- ** Concrete syntax
instance JSON Concrete where
showJSON (Concrete cncid absid flags params lincats lins)
showJSON (Concrete cncid absid flags params lincats lins)
= makeObj [("cnc", showJSON cncid),
("abs", showJSON absid),
("flags", showJSON flags),
@@ -171,13 +170,13 @@ instance JSON LinValue where
<|> do vs <- readJSON o :: Result [LinValue]
return (foldr1 ConcatValue vs)
instance JSON Literal where
instance JSON LinLiteral where
-- basic values (Str, Float, Int) are encoded as JSON strings/numbers:
showJSON (LStr s) = showJSON s
showJSON (LFlt f) = showJSON f
showJSON (LInt n) = showJSON n
showJSON (StrConstant s) = showJSON s
showJSON (FloatConstant f) = showJSON f
showJSON (IntConstant n) = showJSON n
readJSON = readBasicJSON LStr LInt LFlt
readJSON = readBasicJSON StrConstant IntConstant FloatConstant
instance JSON LinPattern where
-- wildcards and patterns without arguments are encoded as strings:
@@ -205,12 +204,12 @@ instance JSON a => JSON (RecordRow a) where
-- record rows and lists of record rows are both encoded as JSON records (i.e., objects)
showJSON row = showJSONs [row]
showJSONs rows = makeObj (map toRow rows)
where toRow (RecordRow (LabelId lbl) val) = (showRawIdent lbl, showJSON val)
where toRow (RecordRow (LabelId lbl) val) = (lbl, showJSON val)
readJSON obj = head <$> readJSONs obj
readJSONs obj = mapM fromRow (assocsJSObject obj)
where fromRow (lbl, jsvalue) = do value <- readJSON jsvalue
return (RecordRow (LabelId (rawIdentS lbl)) value)
return (RecordRow (LabelId lbl) value)
instance JSON rhs => JSON (TableRow rhs) where
showJSON (TableRow l v) = makeObj [(".pattern", showJSON l), (".value", showJSON v)]
@@ -220,19 +219,19 @@ instance JSON rhs => JSON (TableRow rhs) where
-- *** Identifiers in Concrete Syntax
instance JSON PredefId where showJSON (PredefId s) = showJSON s ; readJSON = fmap PredefId . readJSON
instance JSON LabelId where showJSON (LabelId s) = showJSON s ; readJSON = fmap LabelId . readJSON
instance JSON VarValueId where showJSON (VarValueId s) = showJSON s ; readJSON = fmap VarValueId . readJSON
instance JSON ParamId where showJSON (ParamId s) = showJSON s ; readJSON = fmap ParamId . readJSON
instance JSON ParamType where showJSON (ParamTypeId s) = showJSON s ; readJSON = fmap ParamTypeId . readJSON
instance JSON PredefId where showJSON (PredefId s) = showJSON s ; readJSON = fmap PredefId . readJSON
instance JSON LabelId where showJSON (LabelId s) = showJSON s ; readJSON = fmap LabelId . readJSON
instance JSON VarValueId where showJSON (VarValueId s) = showJSON s ; readJSON = fmap VarValueId . readJSON
instance JSON ParamId where showJSON (ParamId s) = showJSON s ; readJSON = fmap ParamId . readJSON
instance JSON ParamType where showJSON (ParamTypeId s) = showJSON s ; readJSON = fmap ParamTypeId . readJSON
--------------------------------------------------------------------------------
-- ** Used in both Abstract and Concrete Syntax
instance JSON ModId where showJSON (ModId s) = showJSON s ; readJSON = fmap ModId . readJSON
instance JSON CatId where showJSON (CatId s) = showJSON s ; readJSON = fmap CatId . readJSON
instance JSON FunId where showJSON (FunId s) = showJSON s ; readJSON = fmap FunId . readJSON
instance JSON ModId where showJSON (ModId s) = showJSON s ; readJSON = fmap ModId . readJSON
instance JSON CatId where showJSON (CatId s) = showJSON s ; readJSON = fmap CatId . readJSON
instance JSON FunId where showJSON (FunId s) = showJSON s ; readJSON = fmap FunId . readJSON
instance JSON VarId where
-- the anonymous variable is the underscore:
@@ -243,24 +242,29 @@ instance JSON VarId where
<|> VarId <$> readJSON o
instance JSON QualId where
showJSON (Qual (ModId m) n) = showJSON (showRawIdent m++"."++showRawIdent n)
showJSON (Qual (ModId m) n) = showJSON (m++"."++n)
showJSON (Unqual n) = showJSON n
readJSON o = do qualid <- readJSON o
let (mod, id) = span (/= '.') qualid
return $ if null mod then Unqual (rawIdentS id) else Qual (ModId (rawIdentS mod)) (rawIdentS id)
instance JSON RawIdent where
showJSON i = showJSON $ showRawIdent i
readJSON o = rawIdentS <$> readJSON o
return $ if null mod then Unqual id else Qual (ModId mod) id
instance JSON Flags where
-- flags are encoded directly as JSON records (i.e., objects):
showJSON (Flags fs) = makeObj [(showRawIdent f, showJSON v) | (f, v) <- fs]
showJSON (Flags fs) = makeObj [(f, showJSON v) | (f, v) <- fs]
readJSON obj = Flags <$> mapM fromRow (assocsJSObject obj)
where fromRow (lbl, jsvalue) = do value <- readJSON jsvalue
return (rawIdentS lbl, value)
return (lbl, value)
instance JSON FlagValue where
-- flag values are encoded as basic JSON types:
showJSON (Str s) = showJSON s
showJSON (Int i) = showJSON i
showJSON (Flt f) = showJSON f
readJSON = readBasicJSON Str Int Flt
--------------------------------------------------------------------------------
-- ** Convenience functions

View File

@@ -16,6 +16,7 @@ module GF.Grammar.EBNF (EBNF, ERule, ERHS(..), ebnf2cf) where
import GF.Data.Operations
import GF.Grammar.CFG
import PGF (mkCId)
type EBNF = [ERule]
type ERule = (ECat, ERHS)
@@ -39,7 +40,7 @@ ebnf2cf :: EBNF -> [ParamCFRule]
ebnf2cf ebnf =
[Rule cat items (mkCFF i cat) | (i,(cat,items)) <- zip [0..] (normEBNF ebnf)]
where
mkCFF i (c,_) = CFObj ("Mk" ++ c ++ "_" ++ show i) []
mkCFF i (c,_) = CFObj (mkCId ("Mk" ++ c ++ "_" ++ show i)) []
normEBNF :: EBNF -> [CFJustRule]
normEBNF erules = let

View File

@@ -64,7 +64,7 @@ module GF.Grammar.Grammar (
Location(..), L(..), unLoc, noLoc, ppLocation, ppL,
-- ** PMCFG
PMCFGCat(..), PMCFGRule(..)
PMCFG(..), Production(..), FId, FunId, SeqId, LIndex, Sequence
) where
import GF.Infra.Ident
@@ -73,8 +73,7 @@ import GF.Infra.Location
import GF.Data.Operations
import PGF2(BindType(..))
import PGF2.Transactions(Symbol,LIndex,LParam)
import PGF.Internal (FId, FunId, SeqId, LIndex, Sequence, BindType(..))
import Data.Array.IArray(Array)
import Data.Array.Unboxed(UArray)
@@ -100,6 +99,7 @@ data ModuleInfo = ModInfo {
mopens :: [OpenSpec],
mexdeps :: [ModuleName],
msrc :: FilePath,
mseqs :: Maybe (Array SeqId Sequence),
jments :: Map.Map Ident Info
}
@@ -304,11 +304,14 @@ allConcreteModules gr =
[i | (i, m) <- modules gr, MTConcrete _ <- [mtype m], isCompleteModule m]
data PMCFGCat = PMCFGCat LIndex [(LIndex,LParam)]
deriving (Eq,Show)
data Production = Production {-# UNPACK #-} !FId
{-# UNPACK #-} !FunId
[[FId]]
deriving (Eq,Ord,Show)
data PMCFGRule = PMCFGRule PMCFGCat [PMCFGCat] [[Symbol]]
deriving (Eq,Show)
data PMCFG = PMCFG [Production]
(Array FunId (UArray LIndex SeqId))
deriving (Eq,Show)
-- | the constructors are judgements in
--
@@ -325,18 +328,15 @@ data Info =
| AbsFun (Maybe (L Type)) (Maybe Int) (Maybe [L Equation]) (Maybe Bool) -- ^ (/ABS/) type, arrity and definition of a function
-- judgements in resource
| ResParam (Maybe (L [Param])) (Maybe ([Term],Int)) -- ^ (/RES/) The second argument is list of all possible values
-- and its precomputed length.
| ResValue (L Type) Int -- ^ (/RES/) to mark parameter constructors for lookup.
-- The second argument is the offset into the list of all values
-- where that constructor appears first.
| ResOper (Maybe (L Type)) (Maybe (L Term)) -- ^ (/RES/)
| ResParam (Maybe (L [Param])) (Maybe [Term]) -- ^ (/RES/) the second parameter is list of all possible values
| ResValue (L Type) -- ^ (/RES/) to mark parameter constructors for lookup
| ResOper (Maybe (L Type)) (Maybe (L Term)) -- ^ (/RES/)
| ResOverload [ModuleName] [(L Type,L Term)] -- ^ (/RES/) idents: modules inherited
| ResOverload [ModuleName] [(L Type,L Term)] -- ^ (/RES/) idents: modules inherited
-- judgements in concrete syntax
| CncCat (Maybe (L Type)) (Maybe (L Term)) (Maybe (L Term)) (Maybe (L Term)) (Maybe [PMCFGRule]) -- ^ (/CNC/) lindef ini'zed,
| CncFun (Maybe ([Ident],Ident,Context,Type)) (Maybe (L Term)) (Maybe (L Term)) (Maybe [PMCFGRule]) -- ^ (/CNC/) type info added at 'TC'
| CncCat (Maybe (L Type)) (Maybe (L Term)) (Maybe (L Term)) (Maybe (L Term)) (Maybe PMCFG) -- ^ (/CNC/) lindef ini'zed,
| CncFun (Maybe (Ident,Context,Type)) (Maybe (L Term)) (Maybe (L Term)) (Maybe PMCFG) -- ^ (/CNC/) type info added at 'TC'
-- indirection to module Ident
| AnyInd Bool ModuleName -- ^ (/INDIR/) the 'Bool' says if canonical
@@ -353,7 +353,7 @@ data Term =
| Cn Ident -- ^ constant
| Con Ident -- ^ constructor
| Sort Ident -- ^ basic type
| EInt Integer -- ^ integer literal
| EInt Int -- ^ integer literal
| EFloat Double -- ^ floating point literal
| K String -- ^ string literal or token: @\"foo\"@
| Empty -- ^ the empty string @[]@
@@ -385,7 +385,7 @@ data Term =
| C Term Term -- ^ concatenation: @s ++ t@
| Glue Term Term -- ^ agglutination: @s + t@
| EPatt Int (Maybe Int) Patt -- ^ pattern (in macro definition): # p
| EPatt Patt -- ^ pattern (in macro definition): # p
| EPattType Term -- ^ pattern type: pattern T
| ELincat Ident Term -- ^ boxed linearization type of Ident
@@ -397,7 +397,7 @@ data Term =
| Alts Term [(Term, Term)] -- ^ alternatives by prefix: @pre {t ; s\/c ; ...}@
| Strs [Term] -- ^ conditioning prefix strings: @strs {s ; ...}@
| TSymCat Int LIndex [(LIndex,Ident)]
| Error String -- ^ error values returned by Predef.error
deriving (Show, Eq, Ord)
-- | Patterns
@@ -408,7 +408,7 @@ data Patt =
| PW -- ^ wild card pattern: @_@
| PR [(Label,Patt)] -- ^ record pattern: @{r = p ; ...}@ -- only concrete
| PString String -- ^ string literal pattern: @\"foo\"@ -- only abstract
| PInt Integer -- ^ integer literal pattern: @12@ -- only abstract
| PInt Int -- ^ integer literal pattern: @12@ -- only abstract
| PFloat Double -- ^ float literal pattern: @1.2@ -- only abstract
| PT Type Patt -- ^ type-annotated pattern
@@ -420,23 +420,18 @@ data Patt =
-- regular expression patterns
| PNeg Patt -- ^ negated pattern: -p
| PAlt Patt Patt -- ^ disjunctive pattern: p1 | p2
| PSeq Int (Maybe Int) Patt Int (Maybe Int) Patt
-- ^ sequence of token parts: p + q
-- In the constructor PSeq minp maxp p minq maxq q,
-- minp/maxp and minq/maxq are the minimal/maximal
-- length of a matching string for p/q.
| PRep Int (Maybe Int) Patt
-- ^ repetition of token part: p*
-- In the constructor PRep minp maxp p,
-- minp/maxp is the minimal/maximal length of
-- a matching string for p.
| PSeq Patt Patt -- ^ sequence of token parts: p + q
| PMSeq MPatt MPatt -- ^ sequence of token parts: p + q
| PRep Patt -- ^ repetition of token part: p*
| PChar -- ^ string of length one: ?
| PChars [Char] -- ^ character list: ["aeiou"]
| PMacro Ident -- #p
| PM QIdent -- #m.p
deriving (Show, Eq, Ord)
-- | Measured pattern (paired with the min & max matching length)
type MPatt = ((Int,Int),Patt)
-- | to guide computation and type checking of tables
data TInfo =
TRaw -- ^ received from parser; can be anything
@@ -453,7 +448,7 @@ data Label =
type MetaId = Int
type Hypo = (BindType,Ident,Type) -- (x:A) (_:A) A ({x}:A)
type Hypo = (BindType,Ident,Term) -- (x:A) (_:A) A ({x}:A)
type Context = [Hypo] -- (x:A)(y:B) (x,y:A) (_,_:A)
type Equation = ([Patt],Term)

View File

@@ -130,7 +130,7 @@ data Token
| T_separator
| T_nonempty
| T_String String -- string literals
| T_Integer Integer -- integer literals
| T_Integer Int -- integer literals
| T_Double Double -- double precision float literals
| T_Ident Ident
| T_EOF
@@ -267,7 +267,7 @@ type AlexInput2 = (AlexInput,AlexInput)
data ParseResult a
= POk AlexInput2 a
| PFailed Posn -- The position of the error
| PFailed Posn -- The position of the error
String -- The error message
newtype P a = P { unP :: AlexInput2 -> ParseResult a }

View File

@@ -6,7 +6,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/10/27 13:21:53 $
-- > CVS $Date: 2005/10/27 13:21:53 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.15 $
--
@@ -20,17 +20,17 @@ module GF.Grammar.Lookup (
lookupOrigInfo,
allOrigInfos,
lookupResDef, lookupResDefLoc,
lookupResType,
lookupResType,
lookupOverload,
lookupOverloadTypes,
lookupParamValues,
lookupParamValues,
allParamValues,
lookupAbsDef,
lookupLincat,
lookupAbsDef,
lookupLincat,
lookupFunType,
lookupCatContext,
allOpers, allOpersTo
) where
) where
import GF.Data.Operations
import GF.Infra.Ident
@@ -69,7 +69,7 @@ lookupResDef gr x = fmap unLoc (lookupResDefLoc gr x)
lookupResDefLoc gr (m,c)
| isPredefCat c = fmap noLoc (lock c defLinType)
| otherwise = look m c
where
where
look m c = do
info <- lookupQIdentInfo gr (m,c)
case info of
@@ -77,13 +77,13 @@ lookupResDefLoc gr (m,c)
ResOper _ Nothing -> return (noLoc (Q (m,c)))
CncCat (Just (L l ty)) _ _ _ _ -> fmap (L l) (lock c ty)
CncCat _ _ _ _ _ -> fmap noLoc (lock c defLinType)
CncFun (Just (_,cat,_,_)) (Just (L l tr)) _ _ -> fmap (L l) (unlock cat tr)
CncFun _ (Just ltr) _ _ -> return ltr
CncFun (Just (cat,_,_)) (Just (L l tr)) _ _ -> fmap (L l) (unlock cat tr)
CncFun _ (Just ltr) _ _ -> return ltr
AnyInd _ n -> look n c
ResParam _ _ -> return (noLoc (QC (m,c)))
ResValue _ _ -> return (noLoc (QC (m,c)))
ResValue _ -> return (noLoc (QC (m,c)))
_ -> raise $ render (c <+> "is not defined in resource" <+> m)
lookupResType :: ErrorMonad m => Grammar -> QIdent -> m Type
@@ -94,12 +94,12 @@ lookupResType gr (m,c) = do
-- used in reused concrete
CncCat _ _ _ _ _ -> return typeType
CncFun (Just (_,cat,cont,val)) _ _ _ -> do
val' <- lock cat val
CncFun (Just (cat,cont,val)) _ _ _ -> do
val' <- lock cat val
return $ mkProd cont val' []
AnyInd _ n -> lookupResType gr (n,c)
ResParam _ _ -> return typePType
ResValue (L _ t) _ -> return t
ResParam _ _ -> return typePType
ResValue (L _ t) -> return t
_ -> raise $ render (c <+> "has no type defined in resource" <+> m)
lookupOverloadTypes :: ErrorMonad m => Grammar -> QIdent -> m [(Term,Type)]
@@ -110,11 +110,11 @@ lookupOverloadTypes gr id@(m,c) = do
-- used in reused concrete
CncCat _ _ _ _ _ -> ret typeType
CncFun (Just (_,cat,cont,val)) _ _ _ -> do
val' <- lock cat val
CncFun (Just (cat,cont,val)) _ _ _ -> do
val' <- lock cat val
ret $ mkProd cont val' []
ResParam _ _ -> ret typePType
ResValue (L _ t) _ -> ret t
ResParam _ _ -> ret typePType
ResValue (L _ t) -> ret t
ResOverload os tysts -> do
tss <- mapM (\x -> lookupOverloadTypes gr (x,c)) os
return $ [(tr,ty) | (L _ ty,L _ tr) <- tysts] ++
@@ -130,8 +130,8 @@ lookupOverload gr (m,c) = do
case info of
ResOverload os tysts -> do
tss <- mapM (\x -> lookupOverload gr (x,c)) os
return $ [let (args,val) = typeFormCnc ty in (map (\(b,x,t) -> t) args,(val,tr)) |
(L _ ty,L _ tr) <- tysts] ++
return $ [let (args,val) = typeFormCnc ty in (map (\(b,x,t) -> t) args,(val,tr)) |
(L _ ty,L _ tr) <- tysts] ++
concat tss
AnyInd _ n -> lookupOverload gr (n,c)
@@ -154,8 +154,8 @@ lookupParamValues :: ErrorMonad m => Grammar -> QIdent -> m [Term]
lookupParamValues gr c = do
(_,info) <- lookupOrigInfo gr c
case info of
ResParam _ (Just (pvs,_)) -> return pvs
_ -> raise $ render (ppQIdent Qualified c <+> "has no parameter values defined")
ResParam _ (Just pvs) -> return pvs
_ -> raise $ render (ppQIdent Qualified c <+> "has no parameter values defined")
allParamValues :: ErrorMonad m => Grammar -> Type -> m [Term]
allParamValues cnc ptyp =
@@ -216,7 +216,7 @@ lookupCatContext gr m c = do
-- notice that it only gives the modules that are reachable and the opers that are included
allOpers :: Grammar -> [(QIdent,Type,Location)]
allOpers gr =
allOpers gr =
[((m,op),typ,loc) |
(m,mi) <- maybe [] (allExtends gr) (greatestResource gr),
(op,info) <- Map.toList (jments mi),
@@ -226,9 +226,9 @@ allOpers gr =
typesIn info = case info of
AbsFun (Just ltyp) _ _ _ -> [ltyp]
ResOper (Just ltyp) _ -> [ltyp]
ResValue ltyp _ -> [ltyp]
ResValue ltyp -> [ltyp]
ResOverload _ tytrs -> [ltyp | (ltyp,_) <- tytrs]
CncFun (Just (_,i,ctx,typ)) _ _ _ ->
CncFun (Just (i,ctx,typ)) _ _ _ ->
[L NoLoc (mkProdSimple ctx (lock' i typ))]
_ -> []

View File

@@ -5,7 +5,7 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/11/11 16:38:00 $
-- > CVS $Date: 2005/11/11 16:38:00 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.24 $
--
@@ -51,14 +51,14 @@ typeForm t =
_ -> error (render ("no normal form of type" <+> ppTerm Unqualified 0 t))
typeFormCnc :: Type -> (Context, Type)
typeFormCnc t =
typeFormCnc t =
case t of
Prod b x a t -> let (x', v) = typeFormCnc t
in ((b,x,a):x',v)
_ -> ([],t)
valCat :: Type -> Cat
valCat typ =
valCat typ =
let (_,cat,_) = typeForm typ
in cat
@@ -99,7 +99,7 @@ isHigherOrderType t = fromErr True $ do -- pessimistic choice
contextOfType :: Monad m => Type -> m Context
contextOfType typ = case typ of
Prod b x a t -> liftM ((b,x,a):) $ contextOfType t
_ -> return []
_ -> return []
termForm :: Monad m => Term -> m ([(BindType,Ident)], Term, [Term])
termForm t = case t of
@@ -108,8 +108,8 @@ termForm t = case t of
return ((b,x):x', fun, args)
App c a ->
do (_,fun, args) <- termForm c
return ([],fun,args ++ [a])
_ ->
return ([],fun,args ++ [a])
_ ->
return ([],t,[])
termFormCnc :: Term -> ([(BindType,Ident)], Term)
@@ -216,7 +216,7 @@ typeTok = Sort cTok
typeStrs = Sort cStrs
typeString, typeFloat, typeInt :: Type
typeInts :: Integer -> Type
typeInts :: Int -> Type
typePBool :: Type
typeError :: Type
@@ -227,7 +227,7 @@ typeInts i = App (cnPredef cInts) (EInt i)
typePBool = cnPredef cPBool
typeError = cnPredef cErrorType
isTypeInts :: Type -> Maybe Integer
isTypeInts :: Type -> Maybe Int
isTypeInts (App c (EInt i)) | c == cnPredef cInts = Just i
isTypeInts _ = Nothing
@@ -238,6 +238,12 @@ isPredefConstant t = case t of
Q (mod,_) | mod == cPredef || mod == cPredefAbs -> True
_ -> False
checkPredefError :: Fail.MonadFail m => Term -> m Term
checkPredefError t =
case t of
Error s -> fail ("Error: "++s)
_ -> return t
cnPredef :: Ident -> Term
cnPredef f = Q (cPredef,f)
@@ -248,7 +254,7 @@ mkTable :: [Term] -> Term -> Term
mkTable tt t = foldr Table t tt
mkCTable :: [(BindType,Ident)] -> Term -> Term
mkCTable ids v = foldr ccase v ids where
mkCTable ids v = foldr ccase v ids where
ccase (_,x) t = T TRaw [(PV x,t)]
mkHypo :: Term -> Hypo
@@ -281,7 +287,7 @@ plusRecType t1 t2 = case (t1, t2) of
filter (`elem` (map fst r1)) (map fst r2) of
[] -> return (RecType (r1 ++ r2))
ls -> raise $ render ("clashing labels" <+> hsep ls)
_ -> raise $ render ("cannot add record types" <+> ppTerm Unqualified 0 t1 <+> "and" <+> ppTerm Unqualified 0 t2)
_ -> raise $ render ("cannot add record types" <+> ppTerm Unqualified 0 t1 <+> "and" <+> ppTerm Unqualified 0 t2)
--plusRecord :: Term -> Term -> Err Term
plusRecord t1 t2 =
@@ -298,7 +304,7 @@ defLinType = RecType [(theLinLabel, typeStr)]
-- | refreshing variables
mkFreshVar :: [Ident] -> Ident
mkFreshVar olds = varX (maxVarIndex olds + 1)
mkFreshVar olds = varX (maxVarIndex olds + 1)
-- | trying to preserve a given symbol
mkFreshVarX :: [Ident] -> Ident -> Ident
@@ -307,7 +313,7 @@ mkFreshVarX olds x = if (elem x olds) then (varX (maxVarIndex olds + 1)) else x
maxVarIndex :: [Ident] -> Int
maxVarIndex = maximum . ((-1):) . map varIndex
mkFreshVars :: Int -> [Ident] -> [Ident]
mkFreshVars :: Int -> [Ident] -> [Ident]
mkFreshVars n olds = [varX (maxVarIndex olds + i) | i <- [1..n]]
-- | quick hack for refining with var in editor
@@ -318,7 +324,7 @@ freshAsTerm s = Vr (varX (readIntArg s))
string2term :: String -> Term
string2term = K
int2term :: Integer -> Term
int2term :: Int -> Term
int2term = EInt
float2term :: Double -> Term
@@ -378,7 +384,7 @@ term2patt trm = case termForm trm of
return (PNeg a')
Ok ([], Cn id, [a]) | id == cRep -> do
a' <- term2patt a
return (PRep 0 Nothing a')
return (PRep a')
Ok ([], Cn id, []) | id == cRep -> do
return PChar
Ok ([], Cn id,[K s]) | id == cChars -> do
@@ -386,7 +392,7 @@ term2patt trm = case termForm trm of
Ok ([], Cn id, [a,b]) | id == cSeq -> do
a' <- term2patt a
b' <- term2patt b
return (PSeq 0 Nothing a' 0 Nothing b')
return (PSeq a' b')
Ok ([], Cn id, [a,b]) | id == cAlt -> do
a' <- term2patt a
b' <- term2patt b
@@ -407,18 +413,18 @@ patt2term pt = case pt of
PC c pp -> mkApp (Con c) (map patt2term pp)
PP c pp -> mkApp (QC c) (map patt2term pp)
PR r -> R [assign l (patt2term p) | (l,p) <- r]
PR r -> R [assign l (patt2term p) | (l,p) <- r]
PT _ p -> patt2term p
PInt i -> EInt i
PFloat i -> EFloat i
PString s -> K s
PString s -> K s
PAs x p -> appCons cAs [Vr x, patt2term p] --- an encoding
PChar -> appCons cChar [] --- an encoding
PChars s -> appCons cChars [K s] --- an encoding
PSeq _ _ a _ _ b -> appCons cSeq [(patt2term a), (patt2term b)] --- an encoding
PSeq a b -> appCons cSeq [(patt2term a), (patt2term b)] --- an encoding
PAlt a b -> appCons cAlt [(patt2term a), (patt2term b)] --- an encoding
PRep _ _ a-> appCons cRep [(patt2term a)] --- an encoding
PRep a -> appCons cRep [(patt2term a)] --- an encoding
PNeg a -> appCons cNeg [(patt2term a)] --- an encoding
@@ -430,7 +436,7 @@ composSafeOp op = runIdentity . composOp (return . op)
-- | to define compositional term functions
composOp :: Monad m => (Term -> m Term) -> Term -> m Term
composOp co trm =
composOp co trm =
case trm of
App c a -> liftM2 App (co c) (co a)
Abs b x t -> liftM (Abs b x) (co t)
@@ -469,8 +475,9 @@ composPattOp op patt =
PImplArg p -> liftM PImplArg (op p)
PNeg p -> liftM PNeg (op p)
PAlt p1 p2 -> liftM2 PAlt (op p1) (op p2)
PSeq _ _ p1 _ _ p2 -> liftM2 (\p1 p2 -> PSeq 0 Nothing p1 0 Nothing p2) (op p1) (op p2)
PRep _ _ p -> liftM (PRep 0 Nothing) (op p)
PSeq p1 p2 -> liftM2 PSeq (op p1) (op p2)
PMSeq (_,p1) (_,p2) -> liftM2 PSeq (op p1) (op p2) -- information loss
PRep p -> liftM PRep (op p)
_ -> return patt -- covers cases without subpatterns
collectOp :: Monoid m => (Term -> m) -> Term -> m
@@ -507,8 +514,9 @@ collectPattOp op patt =
PImplArg p -> op p
PNeg p -> op p
PAlt p1 p2 -> op p1++op p2
PSeq _ _ p1 _ _ p2 -> op p1++op p2
PRep _ _ p -> op p
PSeq p1 p2 -> op p1++op p2
PMSeq (_,p1) (_,p2) -> op p1++op p2
PRep p -> op p
_ -> [] -- covers cases without subpatterns
@@ -544,13 +552,13 @@ strsFromTerm t = case t of
v0 <- mapM (strsFromTerm . fst) vs
c0 <- mapM (strsFromTerm . snd) vs
--let vs' = zip v0 c0
return [strTok (str2strings def) vars |
return [strTok (str2strings def) vars |
def <- d0,
vars <- [[(str2strings v, map sstr c) | (v,c) <- zip vv c0] |
vars <- [[(str2strings v, map sstr c) | (v,c) <- zip vv c0] |
vv <- sequence v0]
]
FV ts -> mapM strsFromTerm ts >>= return . concat
Strs ts -> mapM strsFromTerm ts >>= return . concat
Strs ts -> mapM strsFromTerm ts >>= return . concat
_ -> raise (render ("cannot get Str from term" <+> ppTerm Unqualified 0 t))
getTableType :: TInfo -> Err Type
@@ -582,11 +590,11 @@ noExist = FV []
defaultLinType :: Type
defaultLinType = mkRecType linLabel [typeStr]
-- | normalize records and record types; put s first
-- normalize records and record types; put s first
sortRec :: [(Label,a)] -> [(Label,a)]
sortRec = sortBy ordLabel where
ordLabel (r1,_) (r2,_) =
ordLabel (r1,_) (r2,_) =
case (showIdent (label2ident r1), showIdent (label2ident r2)) of
("s",_) -> LT
(_,"s") -> GT
@@ -597,7 +605,7 @@ sortRec = sortBy ordLabel where
-- | dependency check, detecting circularities and returning topo-sorted list
allDependencies :: (ModuleName -> Bool) -> Map.Map Ident Info -> [(Ident,[Ident])]
allDependencies ism b =
allDependencies ism b =
[(f, nub (concatMap opty (pts i))) | (f,i) <- Map.toList b]
where
opersIn t = case t of

View File

@@ -25,6 +25,7 @@ import GF.Compile.Update (buildAnyTree)
import Data.List(intersperse)
import Data.Char(isAlphaNum)
import qualified Data.Map as Map
import PGF(mkCId)
}
@@ -132,14 +133,14 @@ ModDef
(opens,jments,opts) = case content of { Just c -> c; Nothing -> ([],[],noOptions) }
jments <- mapM (checkInfoType mtype) jments
defs <- buildAnyTree id jments
return (id, ModInfo mtype mstat opts extends with opens [] "" defs) }
return (id, ModInfo mtype mstat opts extends with opens [] "" Nothing defs) }
ModHeader :: { SourceModule }
ModHeader
: ComplMod ModType '=' ModHeaderBody { let { mstat = $1 ;
(mtype,id) = $2 ;
(extends,with,opens) = $4 }
in (id, ModInfo mtype mstat noOptions extends with opens [] "" Map.empty) }
in (id, ModInfo mtype mstat noOptions extends with opens [] "" Nothing Map.empty) }
ComplMod :: { ModuleStatus }
ComplMod
@@ -267,7 +268,7 @@ DataDef
ParamDef :: { [(Ident,Info)] }
ParamDef
: Posn LhsIdent '=' ListParConstr Posn { ($2, ResParam (Just (mkL $1 $5 [param | L loc param <- $4])) Nothing) :
[(f, ResValue (L loc (mkProdSimple co (Cn $2))) 0) | L loc (f,co) <- $4] }
[(f, ResValue (L loc (mkProdSimple co (Cn $2)))) | L loc (f,co) <- $4] }
| Posn LhsIdent Posn { [($2, ResParam Nothing Nothing)] }
OperDef :: { [(Ident,Info)] }
@@ -444,7 +445,7 @@ Exp4
| 'pre' '{' String ';' ListAltern '}' { Alts (K $3) $5 }
| 'pre' '{' Ident ';' ListAltern '}' { Alts (Vr $3) $5 }
| 'strs' '{' ListExp '}' { Strs $3 }
| '#' Patt3 { EPatt 0 Nothing $2 }
| '#' Patt3 { EPatt $2 }
| 'pattern' Exp5 { EPattType $2 }
| 'lincat' Ident Exp5 { ELincat $2 $3 }
| 'lin' Ident Exp5 { ELin $2 $3 }
@@ -485,14 +486,14 @@ Exps
Patt :: { Patt }
Patt
: Patt '|' Patt1 { PAlt $1 $3 }
| Patt '+' Patt1 { PSeq 0 Nothing $1 0 Nothing $3 }
| Patt '+' Patt1 { PSeq $1 $3 }
| Patt1 { $1 }
Patt1 :: { Patt }
Patt1
: Ident ListPatt { PC $1 $2 }
| ModuleName '.' Ident ListPatt { PP ($1,$3) $4 }
| Patt3 '*' { PRep 0 Nothing $1 }
| Patt3 '*' { PRep $1 }
| Patt2 { $1 }
Patt2 :: { Patt }
@@ -624,7 +625,7 @@ ListCFRule
CFRule :: { [BNFCRule] }
CFRule
: Ident '.' Ident '::=' ListCFSymbol ';' { [BNFCRule (showIdent $3) $5 (CFObj (showIdent $1) [])]
: Ident '.' Ident '::=' ListCFSymbol ';' { [BNFCRule (showIdent $3) $5 (CFObj (mkCId (showIdent $1)) [])]
}
| Ident '::=' ListCFRHS ';' { let { cat = showIdent $1;
mkFun cat its =
@@ -637,7 +638,7 @@ CFRule
Terminal c -> filter isAlphaNum c;
NonTerminal (t,_) -> t
}
} in map (\rhs -> BNFCRule cat rhs (CFObj (mkFun cat rhs) [])) $3
} in map (\rhs -> BNFCRule cat rhs (CFObj (mkCId (mkFun cat rhs)) [])) $3
}
| 'coercions' Ident Integer ';' { [BNFCCoercions (showIdent $2) $3]}
| 'terminator' NonEmpty Ident String ';' { [BNFCTerminator $2 (showIdent $3) $4] }
@@ -774,7 +775,7 @@ checkInfoType mt jment@(id,info) =
CncCat pty pd pr ppn _->ifConcrete mt (locPerh pty ++ locPerh pd ++ locPerh pr ++ locPerh ppn)
CncFun _ pd ppn _ -> ifConcrete mt (locPerh pd ++ locPerh ppn)
ResParam pparam _ -> ifResource mt (locPerh pparam)
ResValue ty _ -> ifResource mt (locL ty)
ResValue ty -> ifResource mt (locL ty)
ResOper pty pt -> ifOper mt pty pt
ResOverload _ xs -> ifResource mt (concat [[loc1,loc2] | (L loc1 _,L loc2 _) <- xs])
where

View File

@@ -5,18 +5,18 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/10/12 12:38:29 $
-- > CVS $Date: 2005/10/12 12:38:29 $
-- > CVS $Author: aarne $
-- > CVS $Revision: 1.7 $
--
-- pattern matching for both concrete and abstract syntax. AR -- 16\/6\/2003
-----------------------------------------------------------------------------
module GF.Grammar.PatternMatch (
matchPattern,
testOvershadow,
findMatch
) where
module GF.Grammar.PatternMatch (matchPattern,
testOvershadow,
findMatch,
measurePatt
) where
import GF.Data.Operations
import GF.Grammar.Grammar
@@ -24,13 +24,13 @@ import GF.Infra.Ident
import GF.Grammar.Macros
--import GF.Grammar.Printer
import Data.Maybe(fromMaybe)
--import Data.List
import Control.Monad
import GF.Text.Pretty
--import Debug.Trace
matchPattern :: ErrorMonad m => [(Patt,rhs)] -> Term -> m (rhs, Substitution)
matchPattern pts term =
matchPattern pts term =
if not (isInConstantForm term)
then raise (render ("variables occur in" <+> pp term))
else do
@@ -61,15 +61,15 @@ testOvershadow pts vs = do
findMatch :: ErrorMonad m => [([Patt],rhs)] -> [Term] -> m (rhs, Substitution)
findMatch cases terms = case cases of
[] -> raise (render ("no applicable case for" <+> hsep (punctuate ',' terms)))
(patts,_):_ | length patts /= length terms ->
raise (render ("wrong number of args for patterns :" <+> hsep patts <+>
(patts,_):_ | length patts /= length terms ->
raise (render ("wrong number of args for patterns :" <+> hsep patts <+>
"cannot take" <+> hsep terms))
(patts,val):cc -> case mapM tryMatch (zip patts terms) of
Ok substs -> return (val, concat substs)
_ -> findMatch cc terms
tryMatch :: (Patt, Term) -> Err [(Ident, Term)]
tryMatch (p,t) = do
tryMatch (p,t) = do
t' <- termForm t
trym p t'
where
@@ -83,26 +83,26 @@ tryMatch (p,t) = do
(PString s, ([],K i,[])) | s==i -> return []
(PInt s, ([],EInt i,[])) | s==i -> return []
(PFloat s,([],EFloat i,[])) | s==i -> return [] --- rounding?
(PC p pp, ([], Con f, tt)) |
(PC p pp, ([], Con f, tt)) |
p `eqStrIdent` f && length pp == length tt ->
do matches <- mapM tryMatch (zip pp tt)
return (concat matches)
(PP (q,p) pp, ([], QC (r,f), tt)) |
(PP (q,p) pp, ([], QC (r,f), tt)) |
-- q `eqStrIdent` r && --- not for inherited AR 10/10/2005
p `eqStrIdent` f && length pp == length tt ->
do matches <- mapM tryMatch (zip pp tt)
return (concat matches)
---- hack for AppPredef bug
(PP (q,p) pp, ([], Q (r,f), tt)) |
-- q `eqStrIdent` r && ---
(PP (q,p) pp, ([], Q (r,f), tt)) |
-- q `eqStrIdent` r && ---
p `eqStrIdent` f && length pp == length tt ->
do matches <- mapM tryMatch (zip pp tt)
return (concat matches)
(PR r, ([],R r',[])) |
all (`elem` map fst r') (map fst r) ->
do matches <- mapM tryMatch
do matches <- mapM tryMatch
[(p,snd a) | (l,p) <- r, let Just a = lookup l r']
return (concat matches)
(PT _ p',_) -> trym p' t'
@@ -121,10 +121,11 @@ tryMatch (p,t) = do
Bad _ -> return []
_ -> raise (render ("no match with negative pattern" <+> p))
(PSeq min1 max1 p1 min2 max2 p2, ([],K s, [])) -> matchPSeq min1 max1 p1 min2 max2 p2 s
(PSeq p1 p2, ([],K s, [])) -> matchPSeq p1 p2 s
(PMSeq mp1 mp2, ([],K s, [])) -> matchPMSeq mp1 mp2 s
(PRep _ _ p1, ([],K s, [])) -> checks [
trym (foldr (const (PSeq 0 Nothing p1 0 Nothing)) (PString "")
(PRep p1, ([],K s, [])) -> checks [
trym (foldr (const (PSeq p1)) (PString "")
[1..n]) t' | n <- [0 .. length s]
] >>
return []
@@ -138,14 +139,50 @@ tryMatch (p,t) = do
words2term [w] = K w
words2term (w:ws) = C (K w) (words2term ws)
matchPSeq min1 max1 p1 min2 max2 p2 s =
matchPMSeq (m1,p1) (m2,p2) s = matchPSeq' m1 p1 m2 p2 s
--matchPSeq p1 p2 s = matchPSeq' (0,maxBound::Int) p1 (0,maxBound::Int) p2 s
matchPSeq p1 p2 s = matchPSeq' (lengthBounds p1) p1 (lengthBounds p2) p2 s
matchPSeq' b1@(min1,max1) p1 b2@(min2,max2) p2 s =
do let n = length s
lo = min1 `max` (n-fromMaybe n max2)
hi = (n-min2) `min` (fromMaybe n max1)
lo = min1 `max` (n-max2)
hi = (n-min2) `min` max1
cuts = [splitAt i s | i <- [lo..hi]]
matches <- checks [mapM tryMatch [(p1,K s1),(p2,K s2)] | (s1,s2) <- cuts]
return (concat matches)
-- | Estimate the minimal length of the string that a pattern will match
minLength = matchLength 0 id (+) min -- safe underestimate
-- | Estimate the maximal length of the string that a pattern will match
maxLength =
maybe maxBound id . matchLength Nothing Just (liftM2 (+)) (liftM2 max)
-- safe overestimate
matchLength unknown known seq alt = len
where
len p =
case p of
PString s -> known (length s)
PSeq p1 p2 -> seq (len p1) (len p2)
PAlt p1 p2 -> alt (len p1) (len p2)
PChar -> known 1
PChars _ -> known 1
PAs x p' -> len p'
PT t p' -> len p'
_ -> unknown
lengthBounds p = (minLength p,maxLength p)
mPatt p = (lengthBounds p,measurePatt p)
measurePatt p =
case p of
PSeq p1 p2 -> PMSeq (mPatt p1) (mPatt p2)
_ -> composSafePattOp measurePatt p
isInConstantForm :: Term -> Bool
isInConstantForm trm = case trm of
Cn _ -> True

View File

@@ -61,6 +61,7 @@ cRead = identS "read"
cToStr = identS "toStr"
cMapStr = identS "mapStr"
cError = identS "error"
cTrace = identS "trace"
-- * Hacks: dummy identifiers used in various places.
-- Not very nice!

View File

@@ -1,391 +1,365 @@
----------------------------------------------------------------------
-- |
-- Module : GF.Grammar.Printer
-- Maintainer : Krasimir Angelov
-- Stability : (stable)
-- Portability : (portable)
--
-----------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
module GF.Grammar.Printer
( -- ** Pretty printing
TermPrintQual(..)
, ppModule
, ppJudgement
, ppParams
, ppTerm
, ppPatt
, ppValue
, ppConstrs
, ppQIdent
, ppMeta
, getAbs
) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import PGF2(Literal(..))
import PGF2.Transactions(LIndex,LParam,Symbol(..))
import GF.Infra.Ident
import GF.Infra.Option
import GF.Grammar.Values
import GF.Grammar.Grammar
import GF.Text.Pretty
import Data.Maybe (isNothing)
import Data.List (intersperse)
import qualified Data.Map as Map
import qualified Data.Array.IArray as Array
import qualified GHC.Show
data TermPrintQual
= Terse | Unqualified | Qualified | Internal
deriving Eq
instance Pretty Grammar where
pp = vcat . map (ppModule Qualified) . modules
ppModule :: TermPrintQual -> SourceModule -> Doc
ppModule q (mn, ModInfo mtype mstat opts exts with opens _ _ jments) =
hdr $$
nest 2 (ppOptions opts $$
vcat (map (ppJudgement q) (Map.toList jments))) $$
ftr
where
hdr = complModDoc <+> modTypeDoc <+> '=' <+>
hsep (intersperse (pp "**") $
filter (not . isEmpty) $ [ commaPunct ppExtends exts
, maybe empty ppWith with
, if null opens
then pp '{'
else "open" <+> commaPunct ppOpenSpec opens <+> "in" <+> '{'
])
ftr = '}'
complModDoc =
case mstat of
MSComplete -> empty
MSIncomplete -> pp "incomplete"
modTypeDoc =
case mtype of
MTAbstract -> "abstract" <+> mn
MTResource -> "resource" <+> mn
MTConcrete abs -> "concrete" <+> mn <+> "of" <+> abs
MTInterface -> "interface" <+> mn
MTInstance ie -> "instance" <+> mn <+> "of" <+> ppExtends ie
ppExtends (id,MIAll ) = pp id
ppExtends (id,MIOnly incs) = id <+> brackets (commaPunct pp incs)
ppExtends (id,MIExcept incs) = id <+> '-' <+> brackets (commaPunct pp incs)
ppWith (id,ext,opens) = ppExtends (id,ext) <+> "with" <+> commaPunct ppInstSpec opens
ppOptions opts =
"flags" $$
nest 2 (vcat [option <+> '=' <+> ppLit value <+> ';' | (option,value) <- optionsGFO opts])
ppJudgement q (id, AbsCat pcont ) =
"cat" <+> id <+>
(case pcont of
Just (L _ cont) -> hsep (map (ppDecl q) cont)
Nothing -> empty) <+> ';'
ppJudgement q (id, AbsFun ptype _ pexp poper) =
let kind | isNothing pexp = "data"
| poper == Just False = "oper"
| otherwise = "fun"
in
(case ptype of
Just (L _ typ) -> kind <+> id <+> ':' <+> ppTerm q 0 typ <+> ';'
Nothing -> empty) $$
(case pexp of
Just [] -> empty
Just eqs -> "def" <+> vcat [id <+> hsep (map (ppPatt q 2) ps) <+> '=' <+> ppTerm q 0 e <+> ';' | L _ (ps,e) <- eqs]
Nothing -> empty)
ppJudgement q (id, ResParam pparams _) =
"param" <+> id <+>
(case pparams of
Just (L _ ps) -> '=' <+> ppParams q ps
_ -> empty) <+> ';'
ppJudgement q (id, ResValue pvalue idx) =
"-- param constructor" <+> id <+> ':' <+>
(case pvalue of
(L _ ty) -> ppTerm q 0 ty) <+> ';' <+> parens (pp "index = " <> pp idx)
ppJudgement q (id, ResOper ptype pexp) =
"oper" <+> id <+>
(case ptype of {Just (L _ t) -> ':' <+> ppTerm q 0 t; Nothing -> empty} $$
case pexp of {Just (L _ e) -> '=' <+> ppTerm q 0 e; Nothing -> empty}) <+> ';'
ppJudgement q (id, ResOverload ids defs) =
"oper" <+> id <+> '=' <+>
("overload" <+> '{' $$
nest 2 (vcat [id <+> (':' <+> ppTerm q 0 ty $$ '=' <+> ppTerm q 0 e <+> ';') | (L _ ty,L _ e) <- defs]) $$
'}') <+> ';'
ppJudgement q (id, CncCat mtyp pdef pref pprn mpmcfg) =
(case mtyp of
Just (L _ typ) -> "lincat" <+> id <+> '=' <+> ppTerm q 0 typ <+> ';'
Nothing -> empty) $$
(case pdef of
Just (L _ exp) -> "lindef" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
Nothing -> empty) $$
(case pref of
Just (L _ exp) -> "linref" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
Nothing -> empty) $$
(case pprn of
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
Nothing -> empty) $$
(case (mtyp,mpmcfg,q) of
(Just (L _ typ),Just rules,Internal)
-> "pmcfg" <+> '{' $$
nest 2 (vcat (map (ppPmcfgRule id [] id) rules)) $$
'}'
_ -> empty)
ppJudgement q (id, CncFun mtyp pdef pprn mpmcfg) =
(case pdef of
Just (L _ e) -> let (xs,e') = getAbs e
in "lin" <+> id <+> hsep (map ppBind xs) <+> '=' <+> ppTerm q 0 e' <+> ';'
Nothing -> empty) $$
(case pprn of
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
Nothing -> empty) $$
(case (mtyp,mpmcfg,q) of
(Just (args,res,_,_),Just rules,Internal)
-> "pmcfg" <+> '{' $$
nest 2 (vcat (map (ppPmcfgRule id args res) rules)) $$
'}'
_ -> empty)
ppJudgement q (id, AnyInd cann mid) =
case q of
Internal -> "ind" <+> id <+> '=' <+> (if cann then pp "canonical" else empty) <+> mid <+> ';'
_ -> empty
ppPmcfgRule id arg_cats res_cat (PMCFGRule res args lins) =
pp id <+> (':' <+> hsep (intersperse (pp '*') (zipWith ppPmcfgCat arg_cats args)) <+> "->" <+> ppPmcfgCat res_cat res $$
'=' <+> brackets (vcat (map (hsep . map ppSymbol) lins)))
ppPmcfgCat :: Ident -> PMCFGCat -> Doc
ppPmcfgCat cat (PMCFGCat r rs) = pp cat <> parens (ppLinFun ppLParam r rs)
instance Pretty Term where pp = ppTerm Unqualified 0
ppTerm q d (Abs b v e) = let (xs,e') = getAbs (Abs b v e)
in prec d 0 ('\\' <> commaPunct ppBind xs <+> "->" <+> ppTerm q 0 e')
ppTerm q d (T TRaw xs) = case getCTable (T TRaw xs) of
([],_) -> "table" <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
(vs,e) -> prec d 0 ("\\\\" <> commaPunct pp vs <+> "=>" <+> ppTerm q 0 e)
ppTerm q d (T (TTyped t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (T (TComp t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (T (TWild t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (Prod bt x a b)= if x == identW && bt == Explicit
then prec d 0 (ppTerm q 4 a <+> "->" <+> ppTerm q 0 b)
else prec d 0 (parens (ppBind (bt,x) <+> ':' <+> ppTerm q 0 a) <+> "->" <+> ppTerm q 0 b)
ppTerm q d (Table kt vt)=prec d 0 (ppTerm q 3 kt <+> "=>" <+> ppTerm q 0 vt)
ppTerm q d (Let l e) = let (ls,e') = getLet e
in prec d 0 ("let" <+> vcat (map (ppLocDef q) (l:ls)) $$ "in" <+> ppTerm q 0 e')
ppTerm q d (Example e s)=prec d 0 ("in" <+> ppTerm q 5 e <+> str s)
ppTerm q d (C e1 e2) =prec d 1 (hang (ppTerm q 2 e1) 2 ("++" <+> ppTerm q 1 e2))
ppTerm q d (Glue e1 e2) =prec d 2 (ppTerm q 3 e1 <+> '+' <+> ppTerm q 2 e2)
ppTerm q d (S x y) = case x of
T annot xs -> let e = case annot of
TRaw -> y
TTyped t -> Typed y t
TComp t -> Typed y t
TWild t -> Typed y t
in "case" <+> ppTerm q 0 e <+>"of" <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
_ -> prec d 3 (hang (ppTerm q 3 x) 2 ("!" <+> ppTerm q 4 y))
ppTerm q d (ExtR x y) = prec d 3 (ppTerm q 3 x <+> "**" <+> ppTerm q 4 y)
ppTerm q d (App x y) = prec d 4 (ppTerm q 4 x <+> ppTerm q 5 y)
ppTerm q d (V e es) = hang "table" 2 (sep [ppTerm q 6 e,brackets (fsep (punctuate ';' (map (ppTerm q 0) es)))])
ppTerm q d (FV es) = prec d 4 ("variants" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es))))
ppTerm q d (AdHocOverload es) = "overload" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
ppTerm q d (Alts e xs) = prec d 4 ("pre" <+> braces (ppTerm q 0 e <> ';' <+> fsep (punctuate ';' (map (ppAltern q) xs))))
ppTerm q d (Strs es) = "strs" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
ppTerm q d (EPatt _ _ p)=prec d 4 ('#' <+> ppPatt q 2 p)
ppTerm q d (EPattType t)=prec d 4 ("pattern" <+> ppTerm q 0 t)
ppTerm q d (P t l) = prec d 5 (ppTerm q 5 t <> '.' <> l)
ppTerm q d (Cn id) = pp id
ppTerm q d (Vr id) = pp id
ppTerm q d (Q id) = ppQIdent q id
ppTerm q d (QC id) = ppQIdent q id
ppTerm q d (Sort id) = pp id
ppTerm q d (K s) = str s
ppTerm q d (EInt n) = pp n
ppTerm q d (EFloat f) = pp f
ppTerm q d (Meta i) = ppMeta i
ppTerm q d (Empty) = pp "[]"
ppTerm q d (R []) = pp "<>" -- to distinguish from {} empty RecType
ppTerm q d (R xs) = braces (fsep (punctuate ';' [l <+>
fsep [case mb_t of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty},
'=' <+> ppTerm q 0 e] | (l,(mb_t,e)) <- xs]))
ppTerm q d (RecType xs)
| q == Terse = case [cat | (l,_) <- xs, let (p,cat) = splitAt 5 (showIdent (label2ident l)), p == "lock_"] of
[cat] -> pp cat
_ -> doc
| otherwise = doc
where
doc = braces (fsep (punctuate ';' [l <+> ':' <+> ppTerm q 0 t | (l,t) <- xs]))
ppTerm q d (Typed e t) = '<' <> ppTerm q 0 e <+> ':' <+> ppTerm q 0 t <> '>'
ppTerm q d (ImplArg e) = braces (ppTerm q 0 e)
ppTerm q d (ELincat cat t) = prec d 4 ("lincat" <+> cat <+> ppTerm q 5 t)
ppTerm q d (ELin cat t) = prec d 4 ("lin" <+> cat <+> ppTerm q 5 t)
ppTerm q d (TSymCat i r rs) = pp '<' <> pp i <> pp ',' <> ppLinFun pp r rs <> pp '>'
ppEquation q (ps,e) = hcat (map (ppPatt q 2) ps) <+> "->" <+> ppTerm q 0 e
ppCase q (p,e) = ppPatt q 0 p <+> "=>" <+> ppTerm q 0 e
instance Pretty Patt where pp = ppPatt Unqualified 0
ppPatt q d (PAlt p1 p2) = prec d 0 (ppPatt q 0 p1 <+> '|' <+> ppPatt q 1 p2)
ppPatt q d (PSeq _ _ p1 _ _ p2) = prec d 0 (ppPatt q 0 p1 <+> '+' <+> ppPatt q 1 p2)
ppPatt q d (PC f ps) = if null ps
then pp f
else prec d 1 (f <+> hsep (map (ppPatt q 3) ps))
ppPatt q d (PP f ps) = if null ps
then ppQIdent q f
else prec d 1 (ppQIdent q f <+> hsep (map (ppPatt q 3) ps))
ppPatt q d (PRep _ _ p) = prec d 1 (ppPatt q 3 p <> '*')
ppPatt q d (PAs f p) = prec d 2 (f <> '@' <> ppPatt q 3 p)
ppPatt q d (PNeg p) = prec d 2 ('-' <> ppPatt q 3 p)
ppPatt q d (PChar) = pp '?'
ppPatt q d (PChars s) = brackets (str s)
ppPatt q d (PMacro id) = '#' <> id
ppPatt q d (PM id) = '#' <> ppQIdent q id
ppPatt q d PW = pp '_'
ppPatt q d (PV id) = pp id
ppPatt q d (PInt n) = pp n
ppPatt q d (PFloat f) = pp f
ppPatt q d (PString s) = str s
ppPatt q d (PR xs) = braces (hsep (punctuate ';' [l <+> '=' <+> ppPatt q 0 e | (l,e) <- xs]))
ppPatt q d (PImplArg p) = braces (ppPatt q 0 p)
ppPatt q d (PTilde t) = prec d 2 ('~' <> ppTerm q 6 t)
ppValue :: TermPrintQual -> Int -> Val -> Doc
ppValue q d (VGen i x) = x <> "{-" <> i <> "-}" ---- latter part for debugging
ppValue q d (VApp u v) = prec d 4 (ppValue q 4 u <+> ppValue q 5 v)
ppValue q d (VCn (_,c)) = pp c
ppValue q d (VClos env e) = case e of
Meta _ -> ppTerm q d e <> ppEnv env
_ -> ppTerm q d e ---- ++ prEnv env ---- for debugging
ppValue q d (VRecType xs) = braces (hsep (punctuate ',' [l <> '=' <> ppValue q 0 v | (l,v) <- xs]))
ppValue q d VType = pp "Type"
ppConstrs :: Constraints -> [Doc]
ppConstrs = map (\(v,w) -> braces (ppValue Unqualified 0 v <+> "<>" <+> ppValue Unqualified 0 w))
ppEnv :: Env -> Doc
ppEnv e = hcat (map (\(x,t) -> braces (x <> ":=" <> ppValue Unqualified 0 t)) e)
str s = doubleQuotes (pp (foldr showLitChar "" s))
where
showLitChar c
| c == '"' = showString "\\\""
| c > '\DEL' = showChar c
| otherwise = GHC.Show.showLitChar c
ppDecl q (_,id,typ)
| id == identW = ppTerm q 3 typ
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
ppDDecl q (_,id,typ)
| id == identW = ppTerm q 6 typ
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
ppQIdent :: TermPrintQual -> QIdent -> Doc
ppQIdent q (m,id) =
case q of
Terse -> pp id
Unqualified -> pp id
Qualified -> m <> '.' <> id
Internal -> m <> '.' <> id
instance Pretty Label where pp = pp . label2ident
ppOpenSpec (OSimple id) = pp id
ppOpenSpec (OQualif id n) = parens (id <+> '=' <+> n)
ppInstSpec (id,n) = parens (id <+> '=' <+> n)
ppLocDef q (id, (mbt, e)) =
id <+>
(case mbt of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty} <+> '=' <+> ppTerm q 0 e) <+> ';'
ppBind (Explicit,v) = pp v
ppBind (Implicit,v) = braces v
ppAltern q (x,y) = ppTerm q 0 x <+> '/' <+> ppTerm q 0 y
ppParams q ps = fsep (intersperse (pp '|') (map (ppParam q) ps))
ppParam q (id,cxt) = id <+> hsep (map (ppDDecl q) cxt)
commaPunct f ds = (hcat (punctuate "," (map f ds)))
prec d1 d2 doc
| d1 > d2 = parens doc
| otherwise = doc
getAbs :: Term -> ([(BindType,Ident)], Term)
getAbs (Abs bt v e) = let (xs,e') = getAbs e
in ((bt,v):xs,e')
getAbs e = ([],e)
getCTable :: Term -> ([Ident], Term)
getCTable (T TRaw [(PV v,e)]) = let (vs,e') = getCTable e
in (v:vs,e')
getCTable (T TRaw [(PW, e)]) = let (vs,e') = getCTable e
in (identW:vs,e')
getCTable e = ([],e)
getLet :: Term -> ([LocalDef], Term)
getLet (Let l e) = let (ls,e') = getLet e
in (l:ls,e')
getLet e = ([],e)
ppMeta :: Int -> Doc
ppMeta n
| n == 0 = pp '?'
| otherwise = pp '?' <> pp n
ppLit (LStr s) = pp (show s)
ppLit (LInt n) = pp n
ppLit (LFlt d) = pp d
ppSymbol (SymCat d r rs)= pp '<' <> pp d <> pp ',' <> ppLinFun ppLParam r rs <> pp '>'
ppSymbol (SymLit d r) = pp '{' <> pp d <> pp ',' <> pp r <> pp '}'
ppSymbol (SymVar d r) = pp '<' <> pp d <> pp ',' <> pp '$' <> pp r <> pp '>'
ppSymbol (SymKS t) = doubleQuotes (pp t)
ppSymbol SymNE = pp "nonExist"
ppSymbol SymBIND = pp "BIND"
ppSymbol SymSOFT_BIND = pp "SOFT_BIND"
ppSymbol SymSOFT_SPACE = pp "SOFT_SPACE"
ppSymbol SymCAPIT = pp "CAPIT"
ppSymbol SymALL_CAPIT = pp "ALL_CAPIT"
ppSymbol (SymKP syms alts) = pp "pre" <+> braces (hsep (punctuate (pp ';') (hsep (map ppSymbol syms) : map ppAlt alts)))
ppLinFun ppParam r rs
| r == 0 && not (null rs) = hcat (intersperse (pp '+') ( map ppTerm rs))
| otherwise = hcat (intersperse (pp '+') (pp r : map ppTerm rs))
where
ppTerm (i,p)
| i == 1 = ppParam p
| otherwise = pp i <> pp '*' <> ppParam p
ppLParam p
| i == 0 = pp (chars !! j)
| otherwise = pp (chars !! j : show i)
where
chars = "ijklmnopqr"
(i,j) = p `divMod` (length chars)
ppAlt (syms,ps) = hsep (map ppSymbol syms) <+> pp '/' <+> hsep (map (doubleQuotes . pp) ps)
----------------------------------------------------------------------
-- |
-- Module : GF.Grammar.Printer
-- Maintainer : Krasimir Angelov
-- Stability : (stable)
-- Portability : (portable)
--
-----------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts #-}
module GF.Grammar.Printer
( -- ** Pretty printing
TermPrintQual(..)
, ppModule
, ppJudgement
, ppParams
, ppTerm
, ppPatt
, ppValue
, ppConstrs
, ppQIdent
, ppMeta
, getAbs
) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Infra.Ident
import GF.Infra.Option
import GF.Grammar.Values
import GF.Grammar.Grammar
import PGF.Internal (ppMeta, ppLit, ppFId, ppFunId, ppSeqId, ppSeq)
import GF.Text.Pretty
import Data.Maybe (isNothing)
import Data.List (intersperse)
import qualified Data.Map as Map
--import qualified Data.IntMap as IntMap
--import qualified Data.Set as Set
import qualified Data.Array.IArray as Array
data TermPrintQual
= Terse | Unqualified | Qualified | Internal
deriving Eq
instance Pretty Grammar where
pp = vcat . map (ppModule Qualified) . modules
ppModule :: TermPrintQual -> SourceModule -> Doc
ppModule q (mn, ModInfo mtype mstat opts exts with opens _ _ mseqs jments) =
hdr $$
nest 2 (ppOptions opts $$
vcat (map (ppJudgement q) (Map.toList jments)) $$
maybe empty (ppSequences q) mseqs) $$
ftr
where
hdr = complModDoc <+> modTypeDoc <+> '=' <+>
hsep (intersperse (pp "**") $
filter (not . isEmpty) $ [ commaPunct ppExtends exts
, maybe empty ppWith with
, if null opens
then pp '{'
else "open" <+> commaPunct ppOpenSpec opens <+> "in" <+> '{'
])
ftr = '}'
complModDoc =
case mstat of
MSComplete -> empty
MSIncomplete -> pp "incomplete"
modTypeDoc =
case mtype of
MTAbstract -> "abstract" <+> mn
MTResource -> "resource" <+> mn
MTConcrete abs -> "concrete" <+> mn <+> "of" <+> abs
MTInterface -> "interface" <+> mn
MTInstance ie -> "instance" <+> mn <+> "of" <+> ppExtends ie
ppExtends (id,MIAll ) = pp id
ppExtends (id,MIOnly incs) = id <+> brackets (commaPunct pp incs)
ppExtends (id,MIExcept incs) = id <+> '-' <+> brackets (commaPunct pp incs)
ppWith (id,ext,opens) = ppExtends (id,ext) <+> "with" <+> commaPunct ppInstSpec opens
ppOptions opts =
"flags" $$
nest 2 (vcat [option <+> '=' <+> ppLit value <+> ';' | (option,value) <- optionsGFO opts])
ppJudgement q (id, AbsCat pcont ) =
"cat" <+> id <+>
(case pcont of
Just (L _ cont) -> hsep (map (ppDecl q) cont)
Nothing -> empty) <+> ';'
ppJudgement q (id, AbsFun ptype _ pexp poper) =
let kind | isNothing pexp = "data"
| poper == Just False = "oper"
| otherwise = "fun"
in
(case ptype of
Just (L _ typ) -> kind <+> id <+> ':' <+> ppTerm q 0 typ <+> ';'
Nothing -> empty) $$
(case pexp of
Just [] -> empty
Just eqs -> "def" <+> vcat [id <+> hsep (map (ppPatt q 2) ps) <+> '=' <+> ppTerm q 0 e <+> ';' | L _ (ps,e) <- eqs]
Nothing -> empty)
ppJudgement q (id, ResParam pparams _) =
"param" <+> id <+>
(case pparams of
Just (L _ ps) -> '=' <+> ppParams q ps
_ -> empty) <+> ';'
ppJudgement q (id, ResValue pvalue) =
"-- param constructor" <+> id <+> ':' <+>
(case pvalue of
(L _ ty) -> ppTerm q 0 ty) <+> ';'
ppJudgement q (id, ResOper ptype pexp) =
"oper" <+> id <+>
(case ptype of {Just (L _ t) -> ':' <+> ppTerm q 0 t; Nothing -> empty} $$
case pexp of {Just (L _ e) -> '=' <+> ppTerm q 0 e; Nothing -> empty}) <+> ';'
ppJudgement q (id, ResOverload ids defs) =
"oper" <+> id <+> '=' <+>
("overload" <+> '{' $$
nest 2 (vcat [id <+> (':' <+> ppTerm q 0 ty $$ '=' <+> ppTerm q 0 e <+> ';') | (L _ ty,L _ e) <- defs]) $$
'}') <+> ';'
ppJudgement q (id, CncCat pcat pdef pref pprn mpmcfg) =
(case pcat of
Just (L _ typ) -> "lincat" <+> id <+> '=' <+> ppTerm q 0 typ <+> ';'
Nothing -> empty) $$
(case pdef of
Just (L _ exp) -> "lindef" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
Nothing -> empty) $$
(case pref of
Just (L _ exp) -> "linref" <+> id <+> '=' <+> ppTerm q 0 exp <+> ';'
Nothing -> empty) $$
(case pprn of
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
Nothing -> empty) $$
(case (mpmcfg,q) of
(Just (PMCFG prods funs),Internal)
-> "pmcfg" <+> id <+> '=' <+> '{' $$
nest 2 (vcat (map ppProduction prods) $$
' ' $$
vcat (map (\(funid,arr) -> ppFunId funid <+> ":=" <+>
parens (hcat (punctuate ',' (map ppSeqId (Array.elems arr)))))
(Array.assocs funs))) $$
'}'
_ -> empty)
ppJudgement q (id, CncFun ptype pdef pprn mpmcfg) =
(case pdef of
Just (L _ e) -> let (xs,e') = getAbs e
in "lin" <+> id <+> hsep (map ppBind xs) <+> '=' <+> ppTerm q 0 e' <+> ';'
Nothing -> empty) $$
(case pprn of
Just (L _ prn) -> "printname" <+> id <+> '=' <+> ppTerm q 0 prn <+> ';'
Nothing -> empty) $$
(case (mpmcfg,q) of
(Just (PMCFG prods funs),Internal)
-> "pmcfg" <+> id <+> '=' <+> '{' $$
nest 2 (vcat (map ppProduction prods) $$
' ' $$
vcat (map (\(funid,arr) -> ppFunId funid <+> ":=" <+>
parens (hcat (punctuate ',' (map ppSeqId (Array.elems arr)))))
(Array.assocs funs))) $$
'}'
_ -> empty)
ppJudgement q (id, AnyInd cann mid) =
case q of
Internal -> "ind" <+> id <+> '=' <+> (if cann then pp "canonical" else empty) <+> mid <+> ';'
_ -> empty
instance Pretty Term where pp = ppTerm Unqualified 0
ppTerm q d (Abs b v e) = let (xs,e') = getAbs (Abs b v e)
in prec d 0 ('\\' <> commaPunct ppBind xs <+> "->" <+> ppTerm q 0 e')
ppTerm q d (T TRaw xs) = case getCTable (T TRaw xs) of
([],_) -> "table" <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
(vs,e) -> prec d 0 ("\\\\" <> commaPunct pp vs <+> "=>" <+> ppTerm q 0 e)
ppTerm q d (T (TTyped t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (T (TComp t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (T (TWild t) xs) = "table" <+> ppTerm q 0 t <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
ppTerm q d (Prod bt x a b)= if x == identW && bt == Explicit
then prec d 0 (ppTerm q 4 a <+> "->" <+> ppTerm q 0 b)
else prec d 0 (parens (ppBind (bt,x) <+> ':' <+> ppTerm q 0 a) <+> "->" <+> ppTerm q 0 b)
ppTerm q d (Table kt vt)=prec d 0 (ppTerm q 3 kt <+> "=>" <+> ppTerm q 0 vt)
ppTerm q d (Let l e) = let (ls,e') = getLet e
in prec d 0 ("let" <+> vcat (map (ppLocDef q) (l:ls)) $$ "in" <+> ppTerm q 0 e')
ppTerm q d (Example e s)=prec d 0 ("in" <+> ppTerm q 5 e <+> str s)
ppTerm q d (C e1 e2) =prec d 1 (hang (ppTerm q 2 e1) 2 ("++" <+> ppTerm q 1 e2))
ppTerm q d (Glue e1 e2) =prec d 2 (ppTerm q 3 e1 <+> '+' <+> ppTerm q 2 e2)
ppTerm q d (S x y) = case x of
T annot xs -> let e = case annot of
TRaw -> y
TTyped t -> Typed y t
TComp t -> Typed y t
TWild t -> Typed y t
in "case" <+> ppTerm q 0 e <+>"of" <+> '{' $$
nest 2 (vcat (punctuate ';' (map (ppCase q) xs))) $$
'}'
_ -> prec d 3 (hang (ppTerm q 3 x) 2 ("!" <+> ppTerm q 4 y))
ppTerm q d (ExtR x y) = prec d 3 (ppTerm q 3 x <+> "**" <+> ppTerm q 4 y)
ppTerm q d (App x y) = prec d 4 (ppTerm q 4 x <+> ppTerm q 5 y)
ppTerm q d (V e es) = hang "table" 2 (sep [ppTerm q 6 e,brackets (fsep (punctuate ';' (map (ppTerm q 0) es)))])
ppTerm q d (FV es) = prec d 4 ("variants" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es))))
ppTerm q d (AdHocOverload es) = "overload" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
ppTerm q d (Alts e xs) = prec d 4 ("pre" <+> braces (ppTerm q 0 e <> ';' <+> fsep (punctuate ';' (map (ppAltern q) xs))))
ppTerm q d (Strs es) = "strs" <+> braces (fsep (punctuate ';' (map (ppTerm q 0) es)))
ppTerm q d (EPatt p) = prec d 4 ('#' <+> ppPatt q 2 p)
ppTerm q d (EPattType t)=prec d 4 ("pattern" <+> ppTerm q 0 t)
ppTerm q d (P t l) = prec d 5 (ppTerm q 5 t <> '.' <> l)
ppTerm q d (Cn id) = pp id
ppTerm q d (Vr id) = pp id
ppTerm q d (Q id) = ppQIdent q id
ppTerm q d (QC id) = ppQIdent q id
ppTerm q d (Sort id) = pp id
ppTerm q d (K s) = str s
ppTerm q d (EInt n) = pp n
ppTerm q d (EFloat f) = pp f
ppTerm q d (Meta i) = ppMeta i
ppTerm q d (Empty) = pp "[]"
ppTerm q d (R []) = pp "<>" -- to distinguish from {} empty RecType
ppTerm q d (R xs) = braces (fsep (punctuate ';' [l <+>
fsep [case mb_t of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty},
'=' <+> ppTerm q 0 e] | (l,(mb_t,e)) <- xs]))
ppTerm q d (RecType xs)
| q == Terse = case [cat | (l,_) <- xs, let (p,cat) = splitAt 5 (showIdent (label2ident l)), p == "lock_"] of
[cat] -> pp cat
_ -> doc
| otherwise = doc
where
doc = braces (fsep (punctuate ';' [l <+> ':' <+> ppTerm q 0 t | (l,t) <- xs]))
ppTerm q d (Typed e t) = '<' <> ppTerm q 0 e <+> ':' <+> ppTerm q 0 t <> '>'
ppTerm q d (ImplArg e) = braces (ppTerm q 0 e)
ppTerm q d (ELincat cat t) = prec d 4 ("lincat" <+> cat <+> ppTerm q 5 t)
ppTerm q d (ELin cat t) = prec d 4 ("lin" <+> cat <+> ppTerm q 5 t)
ppTerm q d (Error s) = prec d 4 ("Predef.error" <+> str s)
ppEquation q (ps,e) = hcat (map (ppPatt q 2) ps) <+> "->" <+> ppTerm q 0 e
ppCase q (p,e) = ppPatt q 0 p <+> "=>" <+> ppTerm q 0 e
instance Pretty Patt where pp = ppPatt Unqualified 0
ppPatt q d (PAlt p1 p2) = prec d 0 (ppPatt q 0 p1 <+> '|' <+> ppPatt q 1 p2)
ppPatt q d (PSeq p1 p2) = prec d 0 (ppPatt q 0 p1 <+> '+' <+> ppPatt q 1 p2)
ppPatt q d (PMSeq (_,p1) (_,p2)) = prec d 0 (ppPatt q 0 p1 <+> '+' <+> ppPatt q 1 p2)
ppPatt q d (PC f ps) = if null ps
then pp f
else prec d 1 (f <+> hsep (map (ppPatt q 3) ps))
ppPatt q d (PP f ps) = if null ps
then ppQIdent q f
else prec d 1 (ppQIdent q f <+> hsep (map (ppPatt q 3) ps))
ppPatt q d (PRep p) = prec d 1 (ppPatt q 3 p <> '*')
ppPatt q d (PAs f p) = prec d 2 (f <> '@' <> ppPatt q 3 p)
ppPatt q d (PNeg p) = prec d 2 ('-' <> ppPatt q 3 p)
ppPatt q d (PChar) = pp '?'
ppPatt q d (PChars s) = brackets (str s)
ppPatt q d (PMacro id) = '#' <> id
ppPatt q d (PM id) = '#' <> ppQIdent q id
ppPatt q d PW = pp '_'
ppPatt q d (PV id) = pp id
ppPatt q d (PInt n) = pp n
ppPatt q d (PFloat f) = pp f
ppPatt q d (PString s) = str s
ppPatt q d (PR xs) = braces (hsep (punctuate ';' [l <+> '=' <+> ppPatt q 0 e | (l,e) <- xs]))
ppPatt q d (PImplArg p) = braces (ppPatt q 0 p)
ppPatt q d (PTilde t) = prec d 2 ('~' <> ppTerm q 6 t)
ppValue :: TermPrintQual -> Int -> Val -> Doc
ppValue q d (VGen i x) = x <> "{-" <> i <> "-}" ---- latter part for debugging
ppValue q d (VApp u v) = prec d 4 (ppValue q 4 u <+> ppValue q 5 v)
ppValue q d (VCn (_,c)) = pp c
ppValue q d (VClos env e) = case e of
Meta _ -> ppTerm q d e <> ppEnv env
_ -> ppTerm q d e ---- ++ prEnv env ---- for debugging
ppValue q d (VRecType xs) = braces (hsep (punctuate ',' [l <> '=' <> ppValue q 0 v | (l,v) <- xs]))
ppValue q d VType = pp "Type"
ppConstrs :: Constraints -> [Doc]
ppConstrs = map (\(v,w) -> braces (ppValue Unqualified 0 v <+> "<>" <+> ppValue Unqualified 0 w))
ppEnv :: Env -> Doc
ppEnv e = hcat (map (\(x,t) -> braces (x <> ":=" <> ppValue Unqualified 0 t)) e)
str s = doubleQuotes s
ppDecl q (_,id,typ)
| id == identW = ppTerm q 3 typ
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
ppDDecl q (_,id,typ)
| id == identW = ppTerm q 6 typ
| otherwise = parens (id <+> ':' <+> ppTerm q 0 typ)
ppQIdent :: TermPrintQual -> QIdent -> Doc
ppQIdent q (m,id) =
case q of
Terse -> pp id
Unqualified -> pp id
Qualified -> m <> '.' <> id
Internal -> m <> '.' <> id
instance Pretty Label where pp = pp . label2ident
ppOpenSpec (OSimple id) = pp id
ppOpenSpec (OQualif id n) = parens (id <+> '=' <+> n)
ppInstSpec (id,n) = parens (id <+> '=' <+> n)
ppLocDef q (id, (mbt, e)) =
id <+>
(case mbt of {Just t -> ':' <+> ppTerm q 0 t; Nothing -> empty} <+> '=' <+> ppTerm q 0 e) <+> ';'
ppBind (Explicit,v) = pp v
ppBind (Implicit,v) = braces v
ppAltern q (x,y) = ppTerm q 0 x <+> '/' <+> ppTerm q 0 y
ppParams q ps = fsep (intersperse (pp '|') (map (ppParam q) ps))
ppParam q (id,cxt) = id <+> hsep (map (ppDDecl q) cxt)
ppProduction (Production fid funid args) =
ppFId fid <+> "->" <+> ppFunId funid <>
brackets (hcat (punctuate "," (map (hsep . intersperse (pp '|') . map ppFId) args)))
ppSequences q seqsArr
| null seqs || q /= Internal = empty
| otherwise = "sequences" <+> '{' $$
nest 2 (vcat (map ppSeq seqs)) $$
'}'
where
seqs = Array.assocs seqsArr
commaPunct f ds = (hcat (punctuate "," (map f ds)))
prec d1 d2 doc
| d1 > d2 = parens doc
| otherwise = doc
getAbs :: Term -> ([(BindType,Ident)], Term)
getAbs (Abs bt v e) = let (xs,e') = getAbs e
in ((bt,v):xs,e')
getAbs e = ([],e)
getCTable :: Term -> ([Ident], Term)
getCTable (T TRaw [(PV v,e)]) = let (vs,e') = getCTable e
in (v:vs,e')
getCTable (T TRaw [(PW, e)]) = let (vs,e') = getCTable e
in (identW:vs,e')
getCTable e = ([],e)
getLet :: Term -> ([LocalDef], Term)
getLet (Let l e) = let (ls,e') = getLet e
in (l:ls,e')
getLet e = ([],e)

View File

@@ -5,23 +5,22 @@
-- Stability : (stable)
-- Portability : (portable)
--
-- > CVS $Date: 2005/04/21 16:22:32 $
-- > CVS $Date: 2005/04/21 16:22:32 $
-- > CVS $Author: bringert $
-- > CVS $Revision: 1.7 $
--
-- (Description of the module)
-----------------------------------------------------------------------------
module GF.Grammar.Values (
-- ** Values used in TC type checking
Val(..), Env,
-- ** Annotated tree used in editing
module GF.Grammar.Values (-- ** Values used in TC type checking
Val(..), Env,
-- ** Annotated tree used in editing
Binds, Constraints, MetaSubst,
-- ** For TC
valAbsInt, valAbsFloat, valAbsString, vType,
isPredefCat,
eType,
) where
-- ** For TC
valAbsInt, valAbsFloat, valAbsString, vType,
isPredefCat,
eType,
) where
import GF.Infra.Ident
import GF.Grammar.Grammar

View File

@@ -14,3 +14,9 @@ buildInfo =
#ifdef SERVER_MODE
++" server"
#endif
#ifdef NEW_COMP
++" new-comp"
#endif
#ifdef C_RUNTIME
++" c-runtime"
#endif

Some files were not shown because too many files have changed in this diff Show More