mirror of
https://github.com/GrammaticalFramework/gf-core.git
synced 2026-04-09 04:59:31 -06:00
Compare commits
177 Commits
majestic-c
...
try-to-bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ec4625bc88 | ||
|
|
1166a49c26 | ||
|
|
64803cdd57 | ||
|
|
de1575cd31 | ||
|
|
32d73278af | ||
|
|
837e691643 | ||
|
|
0f4d13dd20 | ||
|
|
278397db20 | ||
|
|
f31a3496f5 | ||
|
|
b753912689 | ||
|
|
000fab7b52 | ||
|
|
fa2826d29a | ||
|
|
9325c8f9fb | ||
|
|
57dc5e9098 | ||
|
|
b42b0caa34 | ||
|
|
3ecb75d7d8 | ||
|
|
2b876b1aac | ||
|
|
5935119050 | ||
|
|
489424a1c6 | ||
|
|
9c72994c2b | ||
|
|
17ebcac84f | ||
|
|
7d018dde62 | ||
|
|
4dba12c0ce | ||
|
|
5ca230dd2a | ||
|
|
242cdcfa22 | ||
|
|
052916b454 | ||
|
|
d07646e753 | ||
|
|
3b69a28dbd | ||
|
|
aa004246d2 | ||
|
|
7c6f53d003 | ||
|
|
a6d5d9a50c | ||
|
|
7792c3cc90 | ||
|
|
a7d73a6861 | ||
|
|
646cfbea0c | ||
|
|
7ddb61eb48 | ||
|
|
dcae5f929e | ||
|
|
638ed39fa4 | ||
|
|
726fb3467c | ||
|
|
b02bb08532 | ||
|
|
c7e26d7cd2 | ||
|
|
4fea7cf37f | ||
|
|
9e5701b13c | ||
|
|
78beac7598 | ||
|
|
f96830f7de | ||
|
|
1c4cde7c66 | ||
|
|
e0ad7594dd | ||
|
|
a218903a2d | ||
|
|
f1c1d157b6 | ||
|
|
e7c0b6dada | ||
|
|
8f4e8c73d2 | ||
|
|
d983255326 | ||
|
|
288984d243 | ||
|
|
c23a03a2d1 | ||
|
|
183e421a0f | ||
|
|
3e0c0fa463 | ||
|
|
c2431e06b2 | ||
|
|
eeab15bee1 | ||
|
|
b36b95c4d6 | ||
|
|
2627e73b63 | ||
|
|
e2ff43da0b | ||
|
|
af09351b66 | ||
|
|
8c89ba4e76 | ||
|
|
218c61b004 | ||
|
|
52df0ed4fe | ||
|
|
2324fe795c | ||
|
|
703b1e5d92 | ||
|
|
f1a72a066f | ||
|
|
6f9f9642d7 | ||
|
|
f5752b345a | ||
|
|
5170668ff2 | ||
|
|
65e85c5a3c | ||
|
|
01c4f82e07 | ||
|
|
e81d668605 | ||
|
|
155b9da861 | ||
|
|
ab0f09e9f7 | ||
|
|
9fa8ac934a | ||
|
|
e84826ed2a | ||
|
|
bbf12458c7 | ||
|
|
b914a25de3 | ||
|
|
1037b209ae | ||
|
|
981d6b9bdd | ||
|
|
5776b567a2 | ||
|
|
643617ccc4 | ||
|
|
41f45e572b | ||
|
|
c7226cc11c | ||
|
|
bc56b54dd1 | ||
|
|
aa061aff0c | ||
|
|
934afc9655 | ||
|
|
33b0bab610 | ||
|
|
9492967fc6 | ||
|
|
5eab0a626d | ||
|
|
fc614cd48e | ||
|
|
eaec428a89 | ||
|
|
ed0a8ca0df | ||
|
|
c65dc70aaf | ||
|
|
2a654c085f | ||
|
|
b855a094f8 | ||
|
|
2f31bbab23 | ||
|
|
7e707508a7 | ||
|
|
c2182274df | ||
|
|
e11017abc0 | ||
|
|
b59fe24c11 | ||
|
|
9204884463 | ||
|
|
2c98075a0b | ||
|
|
7d9015e2e1 | ||
|
|
cf1ef40789 | ||
|
|
37f06a4ae8 | ||
|
|
30c1376232 | ||
|
|
ea3cef46b0 | ||
|
|
268a25f59c | ||
|
|
318b710a14 | ||
|
|
b90666455e | ||
|
|
88db715c3d | ||
|
|
003ab57576 | ||
|
|
ffd7b27abd | ||
|
|
096b36c21d | ||
|
|
86af7b12b3 | ||
|
|
e2c2763d59 | ||
|
|
fae2fc4c6c | ||
|
|
5131fadd1f | ||
|
|
0e1cbfaa7e | ||
|
|
95e5976b03 | ||
|
|
9dee033e2c | ||
|
|
83a4a0525e | ||
|
|
f58697f31f | ||
|
|
8f6dc916b6 | ||
|
|
6a36b486fa | ||
|
|
8190d9fe49 | ||
|
|
527a4451d3 | ||
|
|
2c13f529f9 | ||
|
|
8b82f1ab33 | ||
|
|
7bcc70e79d | ||
|
|
85038d0175 | ||
|
|
6edd449d68 | ||
|
|
a58c6d49d4 | ||
|
|
fef7b80d8e | ||
|
|
03df25bb7a | ||
|
|
3122590e35 | ||
|
|
0a16b76875 | ||
|
|
51b7117a3d | ||
|
|
fef03e755b | ||
|
|
223f92d4f6 | ||
|
|
83483b93ba | ||
|
|
dc8dce90a0 | ||
|
|
e9bbd38f68 | ||
|
|
3fac8415ca | ||
|
|
1294269cd6 | ||
|
|
3acb7d2da4 | ||
|
|
08fb29e6b8 | ||
|
|
f69babef6d | ||
|
|
a42cec2107 | ||
|
|
4d446fcd3f | ||
|
|
ae460e76b6 | ||
|
|
65308861bc | ||
|
|
b7672b67a3 | ||
|
|
e33de168fd | ||
|
|
fc5b3e9037 | ||
|
|
9b9905c0b2 | ||
|
|
ec70e4a83e | ||
|
|
e6ade90679 | ||
|
|
6414bc8923 | ||
|
|
b0b2a06f3b | ||
|
|
221597bd79 | ||
|
|
862aeb5d9b | ||
|
|
25dd1354c7 | ||
|
|
b762e24a82 | ||
|
|
20453193fe | ||
|
|
b53a102c98 | ||
|
|
bc14a56f83 | ||
|
|
3a1213ab37 | ||
|
|
1b41e94f83 | ||
|
|
308f4773dc | ||
|
|
05fc093b5e | ||
|
|
4caf6d684e | ||
|
|
bfd8f9c16d | ||
|
|
aefac84670 | ||
|
|
9f2a3de7a3 |
34
.github/workflows/build-all-versions.yml
vendored
34
.github/workflows/build-all-versions.yml
vendored
@@ -12,28 +12,34 @@ jobs:
|
||||
name: ${{ matrix.os }} / ghc ${{ matrix.ghc }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, macos-latest, windows-latest]
|
||||
cabal: ["latest"]
|
||||
ghc:
|
||||
- "8.6.5"
|
||||
- "8.8.3"
|
||||
- "8.10.1"
|
||||
- "8.10.7"
|
||||
- "9.6.7"
|
||||
exclude:
|
||||
- os: macos-latest
|
||||
ghc: 8.8.3
|
||||
- os: macos-latest
|
||||
ghc: 8.6.5
|
||||
- os: macos-latest
|
||||
ghc: 8.10.7
|
||||
- os: windows-latest
|
||||
ghc: 8.8.3
|
||||
- os: windows-latest
|
||||
ghc: 8.6.5
|
||||
- os: windows-latest
|
||||
ghc: 8.10.7
|
||||
|
||||
steps:
|
||||
- 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: haskell-actions/setup@v2
|
||||
id: setup-haskell-cabal
|
||||
name: Setup Haskell
|
||||
with:
|
||||
@@ -44,7 +50,7 @@ jobs:
|
||||
run: |
|
||||
cabal freeze
|
||||
|
||||
- uses: actions/cache@v1
|
||||
- uses: actions/cache@v4
|
||||
name: Cache ~/.cabal/store
|
||||
with:
|
||||
path: ${{ steps.setup-haskell-cabal.outputs.cabal-store }}
|
||||
@@ -62,34 +68,40 @@ jobs:
|
||||
|
||||
stack:
|
||||
name: stack / ghc ${{ matrix.ghc }}
|
||||
runs-on: ubuntu-latest
|
||||
runs-on: ${{ matrix.ghc == '7.10.3' && 'ubuntu-20.04' || 'ubuntu-latest' }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
stack: ["latest"]
|
||||
ghc: ["7.10.3","8.0.2", "8.2.2", "8.4.4", "8.6.5", "8.8.4"]
|
||||
# ghc: ["8.8.3"]
|
||||
ghc: ["8.4.4", "8.6.5", "8.8.4", "8.10.7", "9.0.2", "9.6.7"]
|
||||
|
||||
steps:
|
||||
- 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: haskell-actions/setup@v2
|
||||
name: Setup Haskell Stack
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
stack-version: 'latest'
|
||||
enable-stack: true
|
||||
|
||||
- uses: actions/cache@v1
|
||||
|
||||
# Fix linker errrors on ghc-7.10.3 for ubuntu (see https://github.com/commercialhaskell/stack/blob/255cd830627870cdef34b5e54d670ef07882523e/doc/faq.md#i-get-strange-ld-errors-about-recompiling-with--fpic)
|
||||
- run: sed -i.bak 's/"C compiler link flags", "/&-no-pie /' /home/runner/.ghcup/ghc/7.10.3/lib/ghc-7.10.3/settings
|
||||
if: matrix.ghc == '7.10.3'
|
||||
|
||||
- uses: actions/cache@v4
|
||||
name: Cache ~/.stack
|
||||
with:
|
||||
path: ~/.stack
|
||||
key: ${{ runner.os }}-${{ matrix.ghc }}-stack
|
||||
key: ${{ runner.os }}-${{ matrix.ghc }}-stack--${{ hashFiles(format('stack-ghc{0}', matrix.ghc)) }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-${{ matrix.ghc }}-stack
|
||||
|
||||
- name: Build
|
||||
run: |
|
||||
stack build --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml
|
||||
# stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks
|
||||
stack build --test --no-run-tests --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml
|
||||
|
||||
- name: Test
|
||||
run: |
|
||||
|
||||
122
.github/workflows/build-binary-packages.yml
vendored
122
.github/workflows/build-binary-packages.yml
vendored
@@ -2,7 +2,7 @@ name: Build Binary Packages
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
release:
|
||||
release:
|
||||
types: ["created"]
|
||||
|
||||
jobs:
|
||||
@@ -13,9 +13,9 @@ jobs:
|
||||
name: Build Ubuntu package
|
||||
strategy:
|
||||
matrix:
|
||||
os:
|
||||
- ubuntu-18.04
|
||||
- ubuntu-20.04
|
||||
ghc: ["9.6"]
|
||||
cabal: ["3.10"]
|
||||
os: ["ubuntu-24.04"]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
@@ -25,12 +25,13 @@ jobs:
|
||||
# Note: `haskell-platform` is listed as requirement in debian/control,
|
||||
# which is why it's installed using apt instead of the Setup Haskell action.
|
||||
|
||||
# - name: Setup Haskell
|
||||
# uses: actions/setup-haskell@v1
|
||||
# id: setup-haskell-cabal
|
||||
# with:
|
||||
# ghc-version: ${{ matrix.ghc }}
|
||||
# cabal-version: ${{ matrix.cabal }}
|
||||
- name: Setup Haskell
|
||||
uses: haskell-actions/setup@v2
|
||||
id: setup-haskell-cabal
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
cabal-version: ${{ matrix.cabal }}
|
||||
if: matrix.os == 'ubuntu-24.04'
|
||||
|
||||
- name: Install build tools
|
||||
run: |
|
||||
@@ -39,14 +40,15 @@ jobs:
|
||||
make \
|
||||
dpkg-dev \
|
||||
debhelper \
|
||||
haskell-platform \
|
||||
libghc-json-dev \
|
||||
python-dev \
|
||||
default-jdk \
|
||||
libtool-bin
|
||||
|
||||
python-dev-is-python3 \
|
||||
libtool-bin
|
||||
cabal install alex happy
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
export PYTHONPATH="/home/runner/work/gf-core/gf-core/debian/gf/usr/local/lib/python3.12/dist-packages/"
|
||||
make deb
|
||||
|
||||
- name: Copy package
|
||||
@@ -54,7 +56,7 @@ jobs:
|
||||
cp ../gf_*.deb dist/
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gf-${{ github.event.release.tag_name }}-${{ matrix.os }}.deb
|
||||
path: dist/gf_*.deb
|
||||
@@ -64,14 +66,14 @@ jobs:
|
||||
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
|
||||
#- 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
|
||||
|
||||
# ---
|
||||
|
||||
@@ -79,16 +81,16 @@ jobs:
|
||||
name: Build macOS package
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ["8.6.5"]
|
||||
cabal: ["2.4"]
|
||||
os: ["macos-10.15"]
|
||||
ghc: ["9.6"]
|
||||
cabal: ["3.10"]
|
||||
os: ["macos-latest", "macos-13"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: actions/setup-haskell@v1
|
||||
uses: haskell-actions/setup@v2
|
||||
id: setup-haskell-cabal
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
@@ -97,8 +99,10 @@ jobs:
|
||||
- name: Install build tools
|
||||
run: |
|
||||
brew install \
|
||||
automake
|
||||
automake \
|
||||
libtool
|
||||
cabal v1-install alex happy
|
||||
pip install setuptools
|
||||
|
||||
- name: Build package
|
||||
run: |
|
||||
@@ -107,24 +111,24 @@ jobs:
|
||||
make pkg
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gf-${{ github.event.release.tag_name }}-macos
|
||||
name: gf-${{ github.event.release.tag_name }}-${{ matrix.os }}
|
||||
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
|
||||
#- 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
|
||||
|
||||
# ---
|
||||
|
||||
@@ -132,9 +136,9 @@ jobs:
|
||||
name: Build Windows package
|
||||
strategy:
|
||||
matrix:
|
||||
ghc: ["8.6.5"]
|
||||
cabal: ["2.4"]
|
||||
os: ["windows-2019"]
|
||||
ghc: ["9.6.7"]
|
||||
cabal: ["3.10"]
|
||||
os: ["windows-2022"]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
steps:
|
||||
@@ -147,6 +151,7 @@ jobs:
|
||||
base-devel
|
||||
gcc
|
||||
python-devel
|
||||
autotools
|
||||
|
||||
- name: Prepare dist folder
|
||||
shell: msys2 {0}
|
||||
@@ -171,7 +176,8 @@ jobs:
|
||||
- name: Build Java bindings
|
||||
shell: msys2 {0}
|
||||
run: |
|
||||
export JDKPATH=/c/hostedtoolcache/windows/Java_Adopt_jdk/8.0.292-10/x64
|
||||
echo $JAVA_HOME_8_X64
|
||||
export JDKPATH="$(cygpath -u "${JAVA_HOME_8_X64}")"
|
||||
export PATH="${PATH}:${JDKPATH}/bin"
|
||||
cd src/runtime/java
|
||||
make \
|
||||
@@ -180,6 +186,9 @@ jobs:
|
||||
make install
|
||||
cp .libs/msys-jpgf-0.dll /c/tmp-dist/java/jpgf.dll
|
||||
cp jpgf.jar /c/tmp-dist/java
|
||||
if: false
|
||||
|
||||
# - uses: actions/setup-python@v5
|
||||
|
||||
- name: Build Python bindings
|
||||
shell: msys2 {0}
|
||||
@@ -188,12 +197,13 @@ jobs:
|
||||
EXTRA_LIB_DIRS: /mingw64/lib
|
||||
run: |
|
||||
cd src/runtime/python
|
||||
pacman --noconfirm -S python-setuptools
|
||||
python setup.py build
|
||||
python setup.py install
|
||||
cp /usr/lib/python3.9/site-packages/pgf* /c/tmp-dist/python
|
||||
cp -r /usr/lib/python3.12/site-packages/pgf* /c/tmp-dist/python
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: actions/setup-haskell@v1
|
||||
uses: haskell-actions/setup@v2
|
||||
id: setup-haskell-cabal
|
||||
with:
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
@@ -205,13 +215,13 @@ jobs:
|
||||
|
||||
- name: Build GF
|
||||
run: |
|
||||
cabal install --only-dependencies -fserver
|
||||
cabal install -fserver --only-dependencies
|
||||
cabal configure -fserver
|
||||
cabal build
|
||||
copy dist\build\gf\gf.exe C:\tmp-dist
|
||||
copy dist-newstyle/build/x86_64-windows/ghc-${{matrix.ghc}}/*/x/gf/build/gf/gf.exe C:/tmp-dist
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v2
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: gf-${{ github.event.release.tag_name }}-windows
|
||||
path: C:\tmp-dist\*
|
||||
@@ -220,11 +230,11 @@ jobs:
|
||||
- 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
|
||||
#- 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
|
||||
|
||||
162
.github/workflows/build-majestic.yml
vendored
162
.github/workflows/build-majestic.yml
vendored
@@ -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
|
||||
103
.github/workflows/build-python-package.yml
vendored
103
.github/workflows/build-python-package.yml
vendored
@@ -4,7 +4,7 @@ name: Build & Publish Python Package
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches: [master]
|
||||
branches: [master,try-to-build-python-module-for-win]
|
||||
|
||||
jobs:
|
||||
build_wheels:
|
||||
@@ -13,27 +13,28 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
os: [ubuntu-18.04, macos-10.15]
|
||||
os: [ubuntu-latest, macos-latest, macos-13, windows-latest]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v1
|
||||
- uses: actions/setup-python@v5
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
python-version: '3.x'
|
||||
|
||||
- name: Install cibuildwheel
|
||||
run: |
|
||||
python -m pip install git+https://github.com/joerick/cibuildwheel.git@main
|
||||
python -m pip install cibuildwheel
|
||||
|
||||
- name: Install build tools for OSX
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
run: |
|
||||
brew install automake
|
||||
brew install libtool
|
||||
|
||||
- name: Build wheels on Linux
|
||||
if: startsWith(matrix.os, 'macos') != true
|
||||
if: startsWith(matrix.os, 'ubuntu')
|
||||
env:
|
||||
CIBW_BEFORE_BUILD: cd src/runtime/c && autoreconf -i && ./configure && make && make install
|
||||
run: |
|
||||
@@ -42,57 +43,67 @@ jobs:
|
||||
- name: Build wheels on OSX
|
||||
if: startsWith(matrix.os, 'macos')
|
||||
env:
|
||||
CIBW_BEFORE_BUILD: cd src/runtime/c && glibtoolize && autoreconf -i && ./configure && make && make install
|
||||
CIBW_BEFORE_BUILD: cd src/runtime/c && glibtoolize && autoreconf -i && ./configure && make && sudo make install
|
||||
run: |
|
||||
python -m cibuildwheel src/runtime/python --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
- name: Build wheels on Windows
|
||||
if: startsWith(matrix.os, 'windows')
|
||||
# env:
|
||||
# CIBW_BEFORE_BUILD: cd src/runtime/c && glibtoolize && autoreconf -i && ./configure && make && sudo make install
|
||||
run: |
|
||||
python -m cibuildwheel src/runtime/python --output-dir wheelhouse
|
||||
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: wheel-${{ matrix.os }}
|
||||
path: ./wheelhouse
|
||||
|
||||
build_sdist:
|
||||
name: Build source distribution
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# build_sdist:
|
||||
# name: Build source distribution
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
|
||||
- uses: actions/setup-python@v2
|
||||
name: Install Python
|
||||
with:
|
||||
python-version: '3.7'
|
||||
# - uses: actions/setup-python@v5
|
||||
# name: Install Python
|
||||
# with:
|
||||
# python-version: '3.10'
|
||||
|
||||
- name: Build sdist
|
||||
run: cd src/runtime/python && python setup.py sdist
|
||||
# - name: Build sdist
|
||||
# run: cd src/runtime/python && python setup.py sdist
|
||||
|
||||
- uses: actions/upload-artifact@v2
|
||||
with:
|
||||
path: ./src/runtime/python/dist/*.tar.gz
|
||||
# - uses: actions/upload-artifact@v4
|
||||
# with:
|
||||
# name: wheel-source
|
||||
# path: ./src/runtime/python/dist/*.tar.gz
|
||||
|
||||
upload_pypi:
|
||||
name: Upload to PyPI
|
||||
needs: [build_wheels, build_sdist]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
# upload_pypi:
|
||||
# name: Upload to PyPI
|
||||
# needs: [build_wheels, build_sdist]
|
||||
# runs-on: ubuntu-latest
|
||||
# if: github.ref == 'refs/heads/master' && github.event_name == 'push'
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: '3.x'
|
||||
# - name: Set up Python
|
||||
# uses: actions/setup-python@v5
|
||||
# with:
|
||||
# python-version: '3.x'
|
||||
|
||||
- name: Install twine
|
||||
run: pip install twine
|
||||
# - name: Install twine
|
||||
# run: pip install twine
|
||||
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: artifact
|
||||
path: ./dist
|
||||
# - uses: actions/download-artifact@v4.1.7
|
||||
# with:
|
||||
# pattern: wheel-*
|
||||
# merge-multiple: true
|
||||
# path: ./dist
|
||||
|
||||
- name: Publish
|
||||
env:
|
||||
TWINE_USERNAME: __token__
|
||||
TWINE_PASSWORD: ${{ secrets.pypi_password }}
|
||||
run: |
|
||||
(cd ./src/runtime/python && curl -I --fail https://pypi.org/project/$(python setup.py --name)/$(python setup.py --version)/) || twine upload dist/*
|
||||
# - name: Publish
|
||||
# env:
|
||||
# TWINE_USERNAME: __token__
|
||||
# TWINE_PASSWORD: ${{ secrets.PYPI_PASSWORD }}
|
||||
# run: |
|
||||
# twine upload --verbose --non-interactive --skip-existing dist/*
|
||||
|
||||
9
.gitignore
vendored
9
.gitignore
vendored
@@ -5,7 +5,6 @@
|
||||
*.jar
|
||||
*.gfo
|
||||
*.pgf
|
||||
*.ngf
|
||||
debian/.debhelper
|
||||
debian/debhelper-build-stamp
|
||||
debian/gf
|
||||
@@ -47,8 +46,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
|
||||
@@ -76,3 +73,9 @@ doc/icfp-2012.html
|
||||
download/*.html
|
||||
gf-book/index.html
|
||||
src/www/gf-web-api.html
|
||||
.devenv
|
||||
.direnv
|
||||
result
|
||||
.vscode
|
||||
.envrc
|
||||
.pre-commit-config.yaml
|
||||
@@ -1,6 +1,7 @@
|
||||
### New since 3.11 (WIP)
|
||||
### New since 3.12 (WIP)
|
||||
|
||||
- Added a changelog!
|
||||
### 3.12
|
||||
See <https://www.grammaticalframework.org/download/release-3.12.html>
|
||||
|
||||
### 3.11
|
||||
|
||||
|
||||
2
Makefile
2
Makefile
@@ -50,7 +50,7 @@ html::
|
||||
# number to the top of debian/changelog.
|
||||
# (Tested on Ubuntu 15.04. You need to install dpkg-dev & debhelper.)
|
||||
deb:
|
||||
dpkg-buildpackage -b -uc
|
||||
dpkg-buildpackage -b -uc -d
|
||||
|
||||
# Make a macOS installer package
|
||||
pkg:
|
||||
|
||||
15
README.md
15
README.md
@@ -38,6 +38,21 @@ or:
|
||||
```
|
||||
stack install
|
||||
```
|
||||
Note that if you are unlucky to have Cabal 3.0 or later, then it uses
|
||||
the so-called Nix style commands. Using those for GF development is
|
||||
a pain. Every time when you change something in the source code, Cabal
|
||||
will generate a new folder for GF to look for the GF libraries and
|
||||
the GF cloud. Either reinstall everything with every change in the
|
||||
compiler, or be sane and stop using cabal-install. Instead you can do:
|
||||
```
|
||||
runghc Setup.hs configure
|
||||
runghc Setup.hs build
|
||||
sudo runghc Setup.hs install
|
||||
```
|
||||
The script will install the GF dependencies globally. The only solution
|
||||
to the Nix madness that I found is radical:
|
||||
|
||||
"No person, no problem" (Нет человека – нет проблемы).
|
||||
|
||||
For more information, including links to precompiled binaries, see the [download page](https://www.grammaticalframework.org/download/index.html).
|
||||
|
||||
|
||||
81
Setup.hs
81
Setup.hs
@@ -4,42 +4,68 @@ import Distribution.Simple.LocalBuildInfo(LocalBuildInfo(..),absoluteInstallDirs
|
||||
import Distribution.Simple.Setup(BuildFlags(..),Flag(..),InstallFlags(..),CopyDest(..),CopyFlags(..),SDistFlags(..))
|
||||
import Distribution.PackageDescription(PackageDescription(..),emptyHookedBuildInfo)
|
||||
import Distribution.Simple.BuildPaths(exeExtension)
|
||||
import System.Directory
|
||||
import System.FilePath((</>),(<.>))
|
||||
import System.Process
|
||||
import Control.Monad(forM_,unless)
|
||||
import Control.Exception(bracket_)
|
||||
import Data.Char(isSpace)
|
||||
|
||||
import WebSetup
|
||||
|
||||
-- | Notice about RGL not built anymore
|
||||
noRGLmsg :: IO ()
|
||||
noRGLmsg = putStrLn "Notice: the RGL is not built as part of GF anymore. See https://github.com/GrammaticalFramework/gf-rgl"
|
||||
|
||||
main :: IO ()
|
||||
main = defaultMainWithHooks simpleUserHooks
|
||||
{ preBuild = gfPreBuild
|
||||
{ preConf = gfPreConf
|
||||
, preBuild = gfPreBuild
|
||||
, postBuild = gfPostBuild
|
||||
, preInst = gfPreInst
|
||||
, postInst = gfPostInst
|
||||
, postCopy = gfPostCopy
|
||||
}
|
||||
where
|
||||
gfPreBuild args = gfPre args . buildDistPref
|
||||
gfPreInst args = gfPre args . installDistPref
|
||||
gfPreConf args flags = do
|
||||
pkgs <- fmap (map (dropWhile isSpace) . tail . lines)
|
||||
(readProcess "ghc-pkg" ["list"] "")
|
||||
forM_ dependencies $ \pkg -> do
|
||||
let name = takeWhile (/='/') (drop 36 pkg)
|
||||
unless (name `elem` pkgs) $ do
|
||||
let fname = name <.> ".tar.gz"
|
||||
callProcess "wget" [pkg,"-O",fname]
|
||||
callProcess "tar" ["-xzf",fname]
|
||||
removeFile fname
|
||||
bracket_ (setCurrentDirectory name) (setCurrentDirectory ".." >> removeDirectoryRecursive name) $ do
|
||||
exists <- doesFileExist "Setup.hs"
|
||||
unless exists $ do
|
||||
writeFile "Setup.hs" (unlines [
|
||||
"import Distribution.Simple",
|
||||
"main = defaultMain"
|
||||
])
|
||||
let to_descr = reverse .
|
||||
(++) (reverse ".cabal") .
|
||||
drop 1 .
|
||||
dropWhile (/='-') .
|
||||
reverse
|
||||
callProcess "wget" [to_descr pkg, "-O", to_descr name]
|
||||
callProcess "runghc" ["Setup.hs","configure"]
|
||||
callProcess "runghc" ["Setup.hs","build"]
|
||||
callProcess "sudo" ["runghc","Setup.hs","install"]
|
||||
|
||||
preConf simpleUserHooks args flags
|
||||
|
||||
gfPreBuild args = gfPre args . buildDistPref
|
||||
gfPreInst args = gfPre args . installDistPref
|
||||
|
||||
gfPre args distFlag = do
|
||||
return emptyHookedBuildInfo
|
||||
|
||||
gfPostBuild args flags pkg lbi = do
|
||||
-- noRGLmsg
|
||||
let gf = default_gf lbi
|
||||
buildWeb gf flags (pkg,lbi)
|
||||
|
||||
gfPostInst args flags pkg lbi = do
|
||||
-- noRGLmsg
|
||||
saveInstallPath args flags (pkg,lbi)
|
||||
installWeb (pkg,lbi)
|
||||
|
||||
gfPostCopy args flags pkg lbi = do
|
||||
-- noRGLmsg
|
||||
saveCopyPath args flags (pkg,lbi)
|
||||
copyWeb flags (pkg,lbi)
|
||||
|
||||
-- `cabal sdist` will not make a proper dist archive, for that see `make sdist`
|
||||
@@ -47,27 +73,16 @@ main = defaultMainWithHooks simpleUserHooks
|
||||
gfSDist pkg lbi hooks flags = do
|
||||
return ()
|
||||
|
||||
saveInstallPath :: [String] -> InstallFlags -> (PackageDescription, LocalBuildInfo) -> IO ()
|
||||
saveInstallPath args flags bi = do
|
||||
let
|
||||
dest = NoCopyDest
|
||||
dir = datadir (uncurry absoluteInstallDirs bi dest)
|
||||
writeFile dataDirFile dir
|
||||
|
||||
saveCopyPath :: [String] -> CopyFlags -> (PackageDescription, LocalBuildInfo) -> IO ()
|
||||
saveCopyPath args flags bi = do
|
||||
let
|
||||
dest = case copyDest flags of
|
||||
NoFlag -> NoCopyDest
|
||||
Flag d -> d
|
||||
dir = datadir (uncurry absoluteInstallDirs bi dest)
|
||||
writeFile dataDirFile dir
|
||||
|
||||
-- | Name of file where installation's data directory is recording
|
||||
-- This is a last-resort way in which the seprate RGL build script
|
||||
-- can determine where to put the compiled RGL files
|
||||
dataDirFile :: String
|
||||
dataDirFile = "DATA_DIR"
|
||||
dependencies = [
|
||||
"https://hackage.haskell.org/package/utf8-string-1.0.2/utf8-string-1.0.2.tar.gz",
|
||||
"https://hackage.haskell.org/package/json-0.10/json-0.10.tar.gz",
|
||||
"https://hackage.haskell.org/package/network-bsd-2.8.1.0/network-bsd-2.8.1.0.tar.gz",
|
||||
"https://hackage.haskell.org/package/httpd-shed-0.4.1.1/httpd-shed-0.4.1.1.tar.gz",
|
||||
"https://hackage.haskell.org/package/exceptions-0.10.5/exceptions-0.10.5.tar.gz",
|
||||
"https://hackage.haskell.org/package/stringsearch-0.3.6.6/stringsearch-0.3.6.6.tar.gz",
|
||||
"https://hackage.haskell.org/package/multipart-0.2.1/multipart-0.2.1.tar.gz",
|
||||
"https://hackage.haskell.org/package/cgi-3001.5.0.0/cgi-3001.5.0.0.tar.gz"
|
||||
]
|
||||
|
||||
-- | Get path to locally-built gf
|
||||
default_gf :: LocalBuildInfo -> FilePath
|
||||
|
||||
@@ -32,7 +32,7 @@ set -x # print commands before executing them
|
||||
pushd src/runtime/c
|
||||
bash setup.sh configure --prefix="$prefix"
|
||||
bash setup.sh build
|
||||
bash setup.sh install prefix="$prefix" # hack required for GF build on macOS
|
||||
# bash setup.sh install prefix="$prefix" # hack required for GF build on macOS
|
||||
bash setup.sh install prefix="$destdir$prefix"
|
||||
popd
|
||||
|
||||
@@ -46,7 +46,7 @@ if which >/dev/null python; then
|
||||
pyver=$(ls "$destdir$prefix/lib" | sed -n 's/^python//p')
|
||||
pydest="$destdir/Library/Python/$pyver/site-packages"
|
||||
mkdir -p "$pydest"
|
||||
ln "$destdir$prefix/lib/python$pyver/site-packages"/pgf* "$pydest"
|
||||
ln "$destdir$prefix/lib/python$pyver/site-packages"/pgf*.so "$pydest"
|
||||
fi
|
||||
popd
|
||||
else
|
||||
|
||||
5
debian/changelog
vendored
5
debian/changelog
vendored
@@ -1,3 +1,8 @@
|
||||
gf (3.12) noble; urgency=low
|
||||
|
||||
* GF 3.12
|
||||
|
||||
-- Inari Listenmaa <inari@digitalgrammars.com> Fri, 8 Aug 2025 18:29:29 +0100
|
||||
gf (3.11) bionic focal; urgency=low
|
||||
|
||||
* GF 3.11
|
||||
|
||||
2
debian/control
vendored
2
debian/control
vendored
@@ -3,7 +3,7 @@ Section: devel
|
||||
Priority: optional
|
||||
Maintainer: Thomas Hallgren <hallgren@chalmers.se>
|
||||
Standards-Version: 3.9.2
|
||||
Build-Depends: debhelper (>= 5), haskell-platform (>= 2011.2.0.1), libghc-haskeline-dev, libghc-mtl-dev, libghc-json-dev, autoconf, automake, libtool-bin, python-dev, java-sdk
|
||||
Build-Depends: debhelper (>= 5), libghc-haskeline-dev, libghc-mtl-dev, libghc-json-dev, autoconf, automake, libtool-bin, python-dev-is-python3, java-sdk
|
||||
Homepage: http://www.grammaticalframework.org/
|
||||
|
||||
Package: gf
|
||||
|
||||
12
debian/rules
vendored
12
debian/rules
vendored
@@ -16,7 +16,7 @@ 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 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
|
||||
|
||||
@@ -24,7 +24,7 @@ SET_LDL=LD_LIBRARY_PATH=$$LD_LIBRARY_PATH:$(CURDIR)/src/runtime/c/.libs
|
||||
|
||||
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
|
||||
# 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
|
||||
|
||||
@@ -32,13 +32,15 @@ override_dh_auto_install:
|
||||
$(SET_LDL) cabal v1-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
|
||||
D="`find debian/gf -name site-packages`" && [ -n "$$D" ] && cd $$D && cd .. && mv site-packages dist-packages
|
||||
# cd src/runtime/java && make INSTALL_PATH=$(CURDIR)/debian/gf/usr install
|
||||
# D="`find debian/gf -name dist-packages`" && [ -n "$$D" ] && cd $$D && cd .. && mv dist-packages dist-packages
|
||||
|
||||
override_dh_usrlocal:
|
||||
|
||||
override_dh_auto_clean:
|
||||
rm -fr dist/build
|
||||
-cd src/runtime/python && rm -fr build
|
||||
-cd src/runtime/java && make clean
|
||||
# -cd src/runtime/java && make clean
|
||||
-cd src/runtime/c && make clean
|
||||
|
||||
override_dh_auto_test:
|
||||
|
||||
@@ -150,11 +150,9 @@ Open a terminal, go to the top directory (``gf-core``), and type the following c
|
||||
$ 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.
|
||||
|
||||
If you prefer Cabal, then you just need to manually choose a suitable GHC to build GF. We recommend GHC 9.6.7, see other supported options in [gf.cabal https://github.com/GrammaticalFramework/gf-core/blob/master/gf.cabal#L14].
|
||||
|
||||
The actual installation process is similar to Stack: open a terminal, go to the top directory (``gf-core``), and type the following command.
|
||||
|
||||
@@ -162,7 +160,13 @@ The actual installation process is similar to Stack: open a terminal, go to the
|
||||
$ 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.//
|
||||
=== Nix ===
|
||||
|
||||
As of 3.12, GF can also be installed via Nix. You can install GF from github with the following command:
|
||||
|
||||
```
|
||||
nix profile install github:GrammaticalFramework/gf-core#gf
|
||||
```
|
||||
|
||||
== Compiling GF with C runtime system support ==
|
||||
|
||||
@@ -197,7 +201,7 @@ Depending on what you want to do with the C runtime, you can follow one or more
|
||||
|
||||
=== 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.**
|
||||
% **If you just want to use the C runtime from Python or Haskell, you don't need to change your GF installation.**
|
||||
|
||||
- **What —**
|
||||
This is the most common use case for the C runtime: compile
|
||||
@@ -230,20 +234,13 @@ 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].
|
||||
but if you want Python 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:
|
||||
|
||||
```
|
||||
cabal install -fc-runtime
|
||||
```
|
||||
|
||||
from the top directory (``gf-core``).
|
||||
|
||||
If you use stack, uncomment the following lines in the ``stack.yaml`` file:
|
||||
Add (or uncomment) the following lines in the ``stack.yaml`` file:
|
||||
|
||||
```
|
||||
flags:
|
||||
@@ -254,6 +251,32 @@ extra-lib-dirs:
|
||||
```
|
||||
and then run ``stack install`` from the top directory (``gf-core``).
|
||||
|
||||
Run the newly built executable with the flag ``-cshell``, and you should see the following welcome message:
|
||||
|
||||
```
|
||||
$ gf -cshell
|
||||
|
||||
* * *
|
||||
* *
|
||||
* *
|
||||
*
|
||||
*
|
||||
* * * * * * *
|
||||
* * *
|
||||
* * * * * *
|
||||
* * *
|
||||
* * *
|
||||
|
||||
This is GF version 3.12.0.
|
||||
Built on ...
|
||||
Git info: ...
|
||||
|
||||
Flags: interrupt server c-runtime
|
||||
License: see help -license.
|
||||
|
||||
This shell uses the C run-time system. See help for available commands.
|
||||
>
|
||||
```
|
||||
|
||||
//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.//
|
||||
@@ -266,14 +289,8 @@ With this feature, ``gf -server`` mode is extended with new requests to call the
|
||||
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:
|
||||
Add the following lines in the ``stack.yaml`` file:
|
||||
|
||||
```
|
||||
flags:
|
||||
|
||||
75
doc/gf-editor-modes.md
Normal file
75
doc/gf-editor-modes.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Editor modes & IDE integration for GF
|
||||
|
||||
We collect GF modes for various editors on this page. Contributions are welcome!
|
||||
|
||||
## Emacs
|
||||
|
||||
[gf.el](https://github.com/GrammaticalFramework/gf-emacs-mode) by Johan
|
||||
Bockgård provides syntax highlighting and automatic indentation and
|
||||
lets you run the GF Shell in an emacs buffer. See installation
|
||||
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.
|
||||
* [Grammatical Framework](https://marketplace.visualstudio.com/items?itemName=GrammaticalFramework.gf-vscode) is a simpler extension
|
||||
without any external dependencies which provides only syntax highlighting.
|
||||
|
||||
## Eclipse
|
||||
|
||||
[GF Eclipse Plugin](https://github.com/GrammaticalFramework/gf-eclipse-plugin/), by John J. Camilleri
|
||||
|
||||
## Gedit
|
||||
|
||||
By John J. Camilleri
|
||||
|
||||
Copy the file below to
|
||||
`~/.local/share/gtksourceview-3.0/language-specs/gf.lang` (under Ubuntu).
|
||||
|
||||
* [gf.lang](../src/tools/gf.lang)
|
||||
|
||||
Some helpful notes/links:
|
||||
|
||||
* The code is based heavily on the `haskell.lang` file which I found in
|
||||
`/usr/share/gtksourceview-2.0/language-specs/haskell.lang`.
|
||||
* Ruslan Osmanov recommends
|
||||
[registering your file extension as its own MIME type](http://osmanov-dev-notes.blogspot.com/2011/04/how-to-add-new-highlight-mode-in-gedit.html)
|
||||
(see also [here](https://help.ubuntu.com/community/AddingMimeTypes)),
|
||||
however on my system the `.gf` extension was already registered
|
||||
as a generic font (`application/x-tex-gf`) and I didn't want to risk
|
||||
messing any of that up.
|
||||
* This is a quick 5-minute job and might require some tweaking.
|
||||
[The GtkSourceView language definition tutorial](http://developer.gnome.org/gtksourceview/stable/lang-tutorial.html)
|
||||
is the place to start looking.
|
||||
* Contributions are welcome!
|
||||
|
||||
## Geany
|
||||
|
||||
By John J. Camilleri
|
||||
|
||||
[Custom filetype](http://www.geany.org/manual/dev/index.html#custom-filetypes)
|
||||
config files for syntax highlighting in [Geany](http://www.geany.org/).
|
||||
|
||||
For version 1.36 and above, copy one of the files below to
|
||||
`/usr/share/geany/filedefs/filetypes.GF.conf` (under Ubuntu).
|
||||
If you're using a version older than 1.36, copy the file to `/usr/share/geany/filetypes.GF.conf`.
|
||||
You will need to manually create the file.
|
||||
|
||||
* [light-filetypes.GF.conf](../src/tools/light-filetypes.GF.conf)
|
||||
* [dark-filetypes.GF.conf](../src/tools/dark-filetypes.GF.conf)
|
||||
|
||||
You will also need to edit the `filetype_extensions.conf` file and add the
|
||||
following line somewhere:
|
||||
|
||||
```
|
||||
GF=*.gf
|
||||
```
|
||||
|
||||
## Vim
|
||||
|
||||
[vim-gf](https://github.com/gdetrez/vim-gf)
|
||||
@@ -1,78 +0,0 @@
|
||||
Editor modes & IDE integration for GF
|
||||
|
||||
|
||||
We collect GF modes for various editors on this page. Contributions are
|
||||
welcome!
|
||||
|
||||
|
||||
==Emacs==
|
||||
|
||||
[gf.el https://github.com/GrammaticalFramework/gf-emacs-mode] by Johan
|
||||
Bockgård provides syntax highlighting and automatic indentation and
|
||||
lets you run the GF Shell in an emacs buffer. See installation
|
||||
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
|
||||
|
||||
==Gedit==
|
||||
|
||||
By John J. Camilleri
|
||||
|
||||
Copy the file below to
|
||||
``~/.local/share/gtksourceview-3.0/language-specs/gf.lang`` (under Ubuntu).
|
||||
|
||||
- [gf.lang ../src/tools/gf.lang]
|
||||
|
||||
|
||||
Some helpful notes/links:
|
||||
|
||||
- The code is based heavily on the ``haskell.lang`` file which I found in
|
||||
``/usr/share/gtksourceview-2.0/language-specs/haskell.lang``.
|
||||
- Ruslan Osmanov recommends
|
||||
[registering your file extension as its own MIME type http://osmanov-dev-notes.blogspot.com/2011/04/how-to-add-new-highlight-mode-in-gedit.html]
|
||||
(see also [here https://help.ubuntu.com/community/AddingMimeTypes]),
|
||||
however on my system the ``.gf`` extension was already registered
|
||||
as a generic font (``application/x-tex-gf``) and I didn't want to risk
|
||||
messing any of that up.
|
||||
- This is a quick 5-minute job and might require some tweaking.
|
||||
[The GtkSourceView language definition tutorial http://developer.gnome.org/gtksourceview/stable/lang-tutorial.html]
|
||||
is the place to start looking.
|
||||
- Contributions are welcome!
|
||||
|
||||
|
||||
==Geany==
|
||||
|
||||
By John J. Camilleri
|
||||
|
||||
[Custom filetype http://www.geany.org/manual/dev/index.html#custom-filetypes]
|
||||
config files for syntax highlighting in [Geany http://www.geany.org/].
|
||||
|
||||
Copy one of the files below to ``/usr/share/geany/filetypes.GF.conf``
|
||||
(under Ubuntu). You will need to manually create the file.
|
||||
|
||||
- [light-filetypes.GF.conf ../src/tools/light-filetypes.GF.conf]
|
||||
- [dark-filetypes.GF.conf ../src/tools/dark-filetypes.GF.conf]
|
||||
|
||||
|
||||
You will also need to edit the ``filetype_extensions.conf`` file and add the
|
||||
following line somewhere:
|
||||
|
||||
```
|
||||
GF=*.gf
|
||||
```
|
||||
|
||||
|
||||
==Vim==
|
||||
|
||||
[vim-gf https://github.com/gdetrez/vim-gf]
|
||||
@@ -46,7 +46,7 @@
|
||||
#TINY
|
||||
|
||||
The command has one argument which is either function, expression or
|
||||
a category defined in the abstract syntax of the current grammar.
|
||||
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
|
||||
@@ -303,7 +303,7 @@ but the resulting .gf file must be imported separately.
|
||||
|
||||
#TINY
|
||||
|
||||
Generates a list of random trees, by default one tree.
|
||||
Generates a list of random trees, by default one tree up to depth 5.
|
||||
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.
|
||||
@@ -315,13 +315,14 @@ given in a file in the -probs flag.
|
||||
| ``-cat`` | generation category
|
||||
| ``-lang`` | uses only functions that have linearizations in all these languages
|
||||
| ``-number`` | number of trees generated
|
||||
| ``-depth`` | the maximum generation depth
|
||||
| ``-depth`` | the maximum generation depth (default: 5)
|
||||
| ``-probs`` | file with biased probabilities (format 'f 0.4' one by line)
|
||||
|
||||
- Examples:
|
||||
|
||||
| ``gr`` | one tree in the startcat of the current grammar
|
||||
| ``gr -cat=NP -number=16`` | 16 trees in the category NP
|
||||
| ``gr -cat=NP -depth=2`` | one tree in the category NP, up to depth 2
|
||||
| ``gr -lang=LangHin,LangTha -cat=Cl`` | Cl, both in LangHin and LangTha
|
||||
| ``gr -probs=FILE`` | generate with bias
|
||||
| ``gr (AdjCN ? (UseN ?))`` | generate trees of form (AdjCN ? (UseN ?))
|
||||
@@ -338,8 +339,8 @@ given in a file in the -probs flag.
|
||||
|
||||
#TINY
|
||||
|
||||
Generates all trees of a given category. By default,
|
||||
the depth is limited to 4, but this can be changed by a flag.
|
||||
Generates all trees of a given category. By default,
|
||||
the depth is limited to 5, 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.
|
||||
|
||||
@@ -353,7 +354,7 @@ to all metavariables in the tree.
|
||||
|
||||
- Examples:
|
||||
|
||||
| ``gt`` | all trees in the startcat, to depth 4
|
||||
| ``gt`` | all trees in the startcat, to depth 5
|
||||
| ``gt -cat=NP -number=16`` | 16 trees in the category NP
|
||||
| ``gt -cat=NP -depth=2`` | trees in the category NP to depth 2
|
||||
| ``gt (AdjCN ? (UseN ?))`` | trees of form (AdjCN ? (UseN ?))
|
||||
@@ -582,7 +583,7 @@ trees where a function node is a metavariable.
|
||||
|
||||
- Examples:
|
||||
|
||||
| ``l -lang=LangSwe,LangNor -chunks ? a b (? c d)`` |
|
||||
| ``l -lang=LangSwe,LangNor -chunks ? a b (? c d)`` |
|
||||
|
||||
|
||||
#NORMAL
|
||||
@@ -647,7 +648,7 @@ 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.
|
||||
|
||||
The -openclass flag is experimental and allows some robustness in
|
||||
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.
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -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`.
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
|
||||
@@ -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`.
|
||||
@@ -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**
|
||||
@@ -1188,7 +1188,7 @@ use ``generate_trees = gt``.
|
||||
this wine is fresh
|
||||
this wine is warm
|
||||
```
|
||||
The default **depth** is 3; the depth can be
|
||||
The default **depth** is 5; the depth can be
|
||||
set by using the ``depth`` flag:
|
||||
```
|
||||
> generate_trees -depth=2 | l
|
||||
@@ -1265,10 +1265,16 @@ Human eye may prefer to see a visualization: ``visualize_tree = vt``:
|
||||
> parse "this delicious cheese is very Italian" | visualize_tree
|
||||
```
|
||||
The tree is generated in postscript (``.ps``) file. The ``-view`` option is used for
|
||||
telling what command to use to view the file. Its default is ``"open"``, which works
|
||||
on Mac OS X. On Ubuntu Linux, one can write
|
||||
telling what command to use to view the file.
|
||||
|
||||
This works on Mac OS X:
|
||||
```
|
||||
> parse "this delicious cheese is very Italian" | visualize_tree -view="eog"
|
||||
> parse "this delicious cheese is very Italian" | visualize_tree -view=open
|
||||
```
|
||||
On Linux, one can use one of the following commands.
|
||||
```
|
||||
> parse "this delicious cheese is very Italian" | visualize_tree -view=eog
|
||||
> parse "this delicious cheese is very Italian" | visualize_tree -view=xdg-open
|
||||
```
|
||||
|
||||
|
||||
@@ -1733,6 +1739,13 @@ A new module can **extend** an old one:
|
||||
Pizza : Kind ;
|
||||
}
|
||||
```
|
||||
Note that the extended grammar doesn't inherit the start
|
||||
category from the grammar it extends, so if you want to
|
||||
generate sentences with this grammar, you'll have to either
|
||||
add a startcat (e.g. ``flags startcat = Question ;``),
|
||||
or in the GF shell, specify the category to ``generate_random`` or ``geneate_trees``
|
||||
(e.g. ``gr -cat=Comment`` or ``gt -cat=Question``).
|
||||
|
||||
Parallel to the abstract syntax, extensions can
|
||||
be built for concrete syntaxes:
|
||||
```
|
||||
@@ -3733,7 +3746,7 @@ However, type-incorrect commands are rejected by the typecheck:
|
||||
The parsing is successful but the type checking failed with error(s):
|
||||
Couldn't match expected type Device light
|
||||
against the interred type Device fan
|
||||
In the expression: DKindOne fan
|
||||
In the expression: DKindOne fan
|
||||
```
|
||||
|
||||
#NEW
|
||||
@@ -4171,7 +4184,7 @@ division of integers.
|
||||
```
|
||||
abstract Calculator = {
|
||||
flags startcat = Exp ;
|
||||
|
||||
|
||||
cat Exp ;
|
||||
|
||||
fun
|
||||
@@ -4578,7 +4591,7 @@ in any multilingual grammar between any languages in the grammar.
|
||||
module Main where
|
||||
|
||||
import PGF
|
||||
import System (getArgs)
|
||||
import System.Environment (getArgs)
|
||||
|
||||
main :: IO ()
|
||||
main = do
|
||||
|
||||
@@ -139,6 +139,8 @@ stack install
|
||||
For more info on working with the GF source code, see the
|
||||
[GF Developers Guide](../doc/gf-developers.html).
|
||||
|
||||
For macOS Sequoia, you need to downgrade the LLVM package, see instructions [here](https://github.com/GrammaticalFramework/gf-core/issues/172#issuecomment-2599365457).
|
||||
|
||||
## Installing the Python bindings from PyPI
|
||||
|
||||
The Python library is available on PyPI as `pgf`, so it can be installed using:
|
||||
|
||||
191
download/index-3.12.md
Normal file
191
download/index-3.12.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Grammatical Framework Download and Installation
|
||||
date: 8 August 2025
|
||||
---
|
||||
|
||||
**GF 3.12** was released on 8 August 2025.
|
||||
|
||||
What's new? See the [release notes](release-3.12.html).
|
||||
|
||||
#### Note: GF core and the RGL
|
||||
|
||||
The following instructions explain how to install **GF core**, i.e. the compiler, shell and run-time systems.
|
||||
Obtaining the **Resource Grammar Library (RGL)** is done separately; see the section [at the bottom of this page](#installing-the-rgl-from-a-binary-release).
|
||||
|
||||
---
|
||||
|
||||
## Installing from a binary package
|
||||
|
||||
Binary packages are available for Debian/Ubuntu, macOS, and Windows and include:
|
||||
|
||||
- GF shell and grammar compiler
|
||||
- `gf -server` mode
|
||||
- C run-time system
|
||||
- Python bindings to the C run-time system
|
||||
|
||||
[Binary packages on GitHub](https://github.com/GrammaticalFramework/gf-core/releases/tag/release-3.12)
|
||||
|
||||
#### Debian/Ubuntu
|
||||
|
||||
The package targets Ubuntu 24.04 (Noble).
|
||||
To install it, use:
|
||||
|
||||
```
|
||||
sudo apt install ./gf-3.12-ubuntu-24.04.deb
|
||||
```
|
||||
|
||||
#### macOS
|
||||
|
||||
If you are on an Intel Mac (2019 or older), use `gf-3.12-macos-intel.pkg`.<br>
|
||||
For newer ARM-based Macs (Apple Silicon M1, M2, M3), use `gf-3.12-macos-arm.pkg`.
|
||||
|
||||
After downloading, right click on the file and click on Open.[^1]
|
||||
You will see a dialog saying that "macOS cannot verify the developer of "gf-3.12-macos-intel.pkg". Are you sure you want to open it?".
|
||||
Press Open.
|
||||
|
||||
[^1]: If you just double click on the file, you will get an error message "gf-3.12-macos-intel.pkg" cannot be opened because it is from an unidentified developer.
|
||||
|
||||
#### Windows
|
||||
|
||||
To install the package:
|
||||
|
||||
1. unpack it anywhere and take note of the full path to the folder containing the `.exe` file.
|
||||
2. add it to the `PATH` environment variable
|
||||
|
||||
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._
|
||||
|
||||
[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.12
|
||||
```
|
||||
|
||||
### Notes
|
||||
|
||||
#### GHC version
|
||||
|
||||
The GF source code is known to be compilable with GHC versions 7.10 through to 9.6.7.
|
||||
|
||||
#### Obtaining Haskell
|
||||
|
||||
There are various ways of obtaining Haskell, including:
|
||||
|
||||
- ghcup
|
||||
1. Install from https://www.haskell.org/ghcup/
|
||||
2. `ghcup install ghc 9.6.7`
|
||||
3. `ghcup set ghc 9.6.7`
|
||||
- Stack: https://haskellstack.org/
|
||||
|
||||
|
||||
#### Installation location
|
||||
|
||||
The above steps install 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):
|
||||
|
||||
```
|
||||
PATH=$HOME/.cabal/bin:$PATH
|
||||
```
|
||||
|
||||
#### 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.
|
||||
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
|
||||
|
||||
### Obtaining
|
||||
|
||||
To obtain the source code for the **release**,
|
||||
download it from [GitHub](https://github.com/GrammaticalFramework/gf-core/releases).
|
||||
|
||||
Alternatively, to obtain the **latest version** of the source code:
|
||||
|
||||
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:
|
||||
```
|
||||
git pull
|
||||
```
|
||||
|
||||
### Installing
|
||||
|
||||
You can then install with:
|
||||
```
|
||||
cabal install
|
||||
```
|
||||
|
||||
or, if you're a Stack user:
|
||||
|
||||
```
|
||||
stack install
|
||||
```
|
||||
|
||||
<!--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).
|
||||
|
||||
## Installing the Python bindings from PyPI
|
||||
|
||||
The Python library is available on PyPI as `pgf`, so it can be installed using:
|
||||
|
||||
```
|
||||
pip install pgf
|
||||
```
|
||||
|
||||
If this doesn't work, you will need to install the C runtime manually; see the instructions [here](https://www.grammaticalframework.org/doc/gf-developers.html#toc12).
|
||||
|
||||
---
|
||||
|
||||
## Installing the RGL from a binary release
|
||||
|
||||
Binary releases of the RGL are made available on [GitHub](https://github.com/GrammaticalFramework/gf-rgl/releases).
|
||||
In general the steps to follow are:
|
||||
|
||||
1. Download a binary release and extract it somewhere on your system.
|
||||
2. Set the environment variable `GF_LIB_PATH` to point to wherever you extracted the RGL.
|
||||
|
||||
For more information, see [Using GF on Windows](https://www.grammaticalframework.org/~inari/gf-windows.html) (latest updated for Windows 10).
|
||||
|
||||
## Installing the RGL from source
|
||||
|
||||
To compile the RGL, you will need to have GF already installed and in your path.
|
||||
|
||||
1. Obtain the RGL source code, either by:
|
||||
- cloning with `git clone https://github.com/GrammaticalFramework/gf-rgl.git`
|
||||
- downloading a source archive [here](https://github.com/GrammaticalFramework/gf-rgl/archive/master.zip)
|
||||
2. Run `make` in the source code folder.
|
||||
|
||||
For more options, see the [RGL README](https://github.com/GrammaticalFramework/gf-rgl/blob/master/README.md).
|
||||
|
||||
---
|
||||
|
||||
## Older releases
|
||||
|
||||
- [GF 3.11](index-3.11.html) (July 2021)
|
||||
- [GF 3.10](index-3.10.html) (December 2018)
|
||||
- [GF 3.9](index-3.9.html) (August 2017)
|
||||
- [GF 3.8](index-3.8.html) (June 2016)
|
||||
- [GF 3.7.1](index-3.7.1.html) (October 2015)
|
||||
- [GF 3.7](index-3.7.html) (June 2015)
|
||||
- [GF 3.6](index-3.6.html) (June 2014)
|
||||
- [GF 3.5](index-3.5.html) (August 2013)
|
||||
- [GF 3.4](index-3.4.html) (January 2013)
|
||||
- [GF 3.3.3](index-3.3.3.html) (March 2012)
|
||||
- [GF 3.3](index-3.3.html) (October 2011)
|
||||
- [GF 3.2.9](index-3.2.9.html) source-only snapshot (September 2011)
|
||||
- [GF 3.2](index-3.2.html) (December 2010)
|
||||
- [GF 3.1.6](index-3.1.6.html) (April 2010)
|
||||
@@ -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.12.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.12.html">the current version</a> of this page.
|
||||
</body>
|
||||
</html>
|
||||
|
||||
37
download/release-3.12.md
Normal file
37
download/release-3.12.md
Normal file
@@ -0,0 +1,37 @@
|
||||
---
|
||||
title: GF 3.12 Release Notes
|
||||
date: 08 August 2025
|
||||
---
|
||||
|
||||
## Installation
|
||||
|
||||
See the [download page](index-3.12.html).
|
||||
|
||||
## What's new
|
||||
This release adds support for Apple Silicon M1 Mac computers and newer versions of GHC, along with various improvements and bug fixes.
|
||||
|
||||
Over 70 commits have been merged to gf-core since the release of GF 3.11 in July 2021.
|
||||
|
||||
## General
|
||||
- Support for ARM, allowing to run GF on Mac computers with Apple Silicon M1
|
||||
- Support for newer versions of GHC (8.10.7, 9.0.2, 9.2.4, 9.4, 9.6.7)
|
||||
- Support compiling with Nix
|
||||
- Better error messages
|
||||
- Improvements to several GF shell commands
|
||||
- Several bug fixes and performance improvements
|
||||
- Temporarily dropped support for Java bindings
|
||||
|
||||
## GF compiler and run-time library
|
||||
- Syntactic sugar for table update: `table {cases ; vvv => t \! vvv}.t` can now be written as `t ** { cases }`
|
||||
- Adjust the `-view` command depending on the OS
|
||||
- Improve output of the `visualize_dependencies` (`vd`) command for large dependency trees
|
||||
- Reintroduce syntactic transfer with `pt -transfer` and fix a bug in `pt -compute`
|
||||
- Bug fix: apply `gt` to all arguments when piped
|
||||
- Fix many "Invalid character" messages by always encoding GF files in UTF-8
|
||||
- Improve performance with long extend-lists
|
||||
- Improve syntax error messages
|
||||
- Add support for BIND tokens in the Python bindings
|
||||
- Allow compilation with emscripten
|
||||
|
||||
## Other
|
||||
- Add support for Visual Studio Code
|
||||
43
flake.lock
generated
Normal file
43
flake.lock
generated
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1704290814,
|
||||
"narHash": "sha256-LWvKHp7kGxk/GEtlrGYV68qIvPHkU9iToomNFGagixU=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "70bdadeb94ffc8806c0570eb5c2695ad29f0e421",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs",
|
||||
"systems": "systems"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
50
flake.nix
Normal file
50
flake.nix
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-23.05";
|
||||
systems.url = "github:nix-systems/default";
|
||||
};
|
||||
|
||||
nixConfig = {
|
||||
# extra-trusted-public-keys =
|
||||
# "devenv.cachix.org-1:w1cLUi8dv3hnoSPGAuibQv+f9TZLr6cv/Hm9XgU50cw=";
|
||||
# extra-substituters = "https://devenv.cachix.org";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, systems, ... }@inputs:
|
||||
let forEachSystem = nixpkgs.lib.genAttrs (import systems);
|
||||
in {
|
||||
packages = forEachSystem (system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
haskellPackages = pkgs.haskell.packages.ghc925.override {
|
||||
overrides = self: _super: {
|
||||
cgi = pkgs.haskell.lib.unmarkBroken (pkgs.haskell.lib.dontCheck
|
||||
(self.callHackage "cgi" "3001.5.0.1" { }));
|
||||
};
|
||||
};
|
||||
|
||||
in {
|
||||
gf = pkgs.haskell.lib.overrideCabal
|
||||
(haskellPackages.callCabal2nixWithOptions "gf" self "--flag=-server"
|
||||
{ }) (_old: {
|
||||
# Fix utf8 encoding problems
|
||||
patches = [
|
||||
# Already applied in master
|
||||
# (
|
||||
# pkgs.fetchpatch {
|
||||
# url = "https://github.com/anka-213/gf-core/commit/6f1ca05fddbcbc860898ddf10a557b513dfafc18.patch";
|
||||
# sha256 = "17vn3hncxm1dwbgpfmrl6gk6wljz3r28j191lpv5zx741pmzgbnm";
|
||||
# }
|
||||
# )
|
||||
./nix/expose-all.patch
|
||||
./nix/revert-new-cabal-madness.patch
|
||||
];
|
||||
jailbreak = true;
|
||||
# executableSystemDepends = [
|
||||
# (pkgs.ncurses.override { enableStatic = true; })
|
||||
# ];
|
||||
# executableHaskellDepends = [ ];
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,7 @@ concrete FoodIta of Food = {
|
||||
lincat
|
||||
Comment, Item, Kind, Quality = Str ;
|
||||
lin
|
||||
Pred item quality = item ++ "è" ++ quality ;
|
||||
Pred item quality = item ++ "è" ++ quality ;
|
||||
This kind = "questo" ++ kind ;
|
||||
That kind = "quel" ++ kind ;
|
||||
Mod quality kind = kind ++ quality ;
|
||||
|
||||
@@ -32,5 +32,5 @@ resource ResIta = open Prelude in {
|
||||
in
|
||||
adjective nero (ner+"a") (ner+"i") (ner+"e") ;
|
||||
copula : Number => Str =
|
||||
table {Sg => "è" ; Pl => "sono"} ;
|
||||
table {Sg => "è" ; Pl => "sono"} ;
|
||||
}
|
||||
|
||||
@@ -8,13 +8,13 @@ instance LexFoodsFin of LexFoods =
|
||||
cheese_N = mkN "juusto" ;
|
||||
fish_N = mkN "kala" ;
|
||||
fresh_A = mkA "tuore" ;
|
||||
warm_A = mkA
|
||||
(mkN "lämmin" "lämpimän" "lämmintä" "lämpimänä" "lämpimään"
|
||||
"lämpiminä" "lämpimiä" "lämpimien" "lämpimissä" "lämpimiin"
|
||||
)
|
||||
"lämpimämpi" "lämpimin" ;
|
||||
warm_A = mkA
|
||||
(mkN "lämmin" "lämpimän" "lämmintä" "lämpimänä" "lämpimään"
|
||||
"lämpiminä" "lämpimiä" "lämpimien" "lämpimissä" "lämpimiin"
|
||||
)
|
||||
"lämpimämpi" "lämpimin" ;
|
||||
italian_A = mkA "italialainen" ;
|
||||
expensive_A = mkA "kallis" ;
|
||||
delicious_A = mkA "herkullinen" ;
|
||||
boring_A = mkA "tylsä" ;
|
||||
boring_A = mkA "tylsä" ;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
-- (c) 2009 Aarne Ranta under LGPL
|
||||
|
||||
instance LexFoodsGer of LexFoods =
|
||||
instance LexFoodsGer of LexFoods =
|
||||
open SyntaxGer, ParadigmsGer in {
|
||||
oper
|
||||
wine_N = mkN "Wein" ;
|
||||
pizza_N = mkN "Pizza" "Pizzen" feminine ;
|
||||
cheese_N = mkN "Käse" "Käse" masculine ;
|
||||
cheese_N = mkN "Käse" "Käse" masculine ;
|
||||
fish_N = mkN "Fisch" ;
|
||||
fresh_A = mkA "frisch" ;
|
||||
warm_A = mkA "warm" "wärmer" "wärmste" ;
|
||||
warm_A = mkA "warm" "wärmer" "wärmste" ;
|
||||
italian_A = mkA "italienisch" ;
|
||||
expensive_A = mkA "teuer" ;
|
||||
delicious_A = mkA "köstlich" ;
|
||||
delicious_A = mkA "köstlich" ;
|
||||
boring_A = mkA "langweilig" ;
|
||||
}
|
||||
|
||||
@@ -7,10 +7,10 @@ instance LexFoodsSwe of LexFoods =
|
||||
pizza_N = mkN "pizza" ;
|
||||
cheese_N = mkN "ost" ;
|
||||
fish_N = mkN "fisk" ;
|
||||
fresh_A = mkA "färsk" ;
|
||||
fresh_A = mkA "färsk" ;
|
||||
warm_A = mkA "varm" ;
|
||||
italian_A = mkA "italiensk" ;
|
||||
expensive_A = mkA "dyr" ;
|
||||
delicious_A = mkA "läcker" ;
|
||||
boring_A = mkA "tråkig" ;
|
||||
delicious_A = mkA "läcker" ;
|
||||
boring_A = mkA "tråkig" ;
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ concrete QueryFin of Query = {
|
||||
Odd = pred "pariton" ;
|
||||
Prime = pred "alkuluku" ;
|
||||
Number i = i.s ;
|
||||
Yes = "kyllä" ;
|
||||
Yes = "kyllä" ;
|
||||
No = "ei" ;
|
||||
oper
|
||||
pred : Str -> Str -> Str = \f,x -> "onko" ++ x ++ f ;
|
||||
|
||||
@@ -43,10 +43,10 @@ oper
|
||||
} ;
|
||||
|
||||
auxVerb : Aux -> Verb = \a -> case a of {
|
||||
Avere =>
|
||||
Avere =>
|
||||
mkVerb "avere" "ho" "hai" "ha" "abbiamo" "avete" "hanno" "avuto" Avere ;
|
||||
Essere =>
|
||||
mkVerb "essere" "sono" "sei" "è" "siamo" "siete" "sono" "stato" Essere
|
||||
Essere =>
|
||||
mkVerb "essere" "sono" "sei" "è" "siamo" "siete" "sono" "stato" Essere
|
||||
} ;
|
||||
|
||||
agrPart : Verb -> Agr -> ClitAgr -> Str = \v,a,c -> case v.aux of {
|
||||
|
||||
275
gf.cabal
275
gf.cabal
@@ -1,19 +1,24 @@
|
||||
name: gf
|
||||
version: 3.11.0-git
|
||||
version: 3.12.0
|
||||
|
||||
cabal-version: 1.22
|
||||
build-type: Custom
|
||||
build-type: Simple
|
||||
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
|
||||
maintainer: John J. Camilleri <john@digitalgrammars.com>
|
||||
homepage: https://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
|
||||
tested-with: GHC==7.10.3, GHC==8.0.2, GHC==8.10.4, GHC==9.0.2, GHC==9.2.4, GHC==9.6.7
|
||||
|
||||
data-dir: src
|
||||
extra-source-files: WebSetup.hs
|
||||
extra-source-files:
|
||||
README.md
|
||||
CHANGELOG.md
|
||||
WebSetup.hs
|
||||
doc/Logos/gf0.png
|
||||
data-files:
|
||||
www/*.html
|
||||
www/*.css
|
||||
@@ -39,14 +44,6 @@ data-files:
|
||||
www/translator/*.css
|
||||
www/translator/*.js
|
||||
|
||||
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
|
||||
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/GrammaticalFramework/gf-core.git
|
||||
@@ -63,38 +60,129 @@ 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
|
||||
default-language: Haskell2010
|
||||
build-depends: pgf2,
|
||||
base >= 4.6 && <5,
|
||||
array,
|
||||
containers,
|
||||
bytestring,
|
||||
utf8-string,
|
||||
random,
|
||||
pretty,
|
||||
mtl,
|
||||
exceptions,
|
||||
ghc-prim,
|
||||
filepath, directory>=1.2, time,
|
||||
process, haskeline, parallel>=3, json
|
||||
ghc-options: -threaded
|
||||
--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:
|
||||
-- GHC 8.0.2 to GHC 8.10.4
|
||||
array >= 0.5.1 && < 0.6,
|
||||
base >= 4.9.1 && < 4.22,
|
||||
bytestring >= 0.10.8 && < 0.12,
|
||||
containers >= 0.5.7 && < 0.7,
|
||||
exceptions >= 0.8.3 && < 0.11,
|
||||
ghc-prim >= 0.5.0 && <= 0.10.0,
|
||||
mtl >= 2.2.1 && <= 2.3.1,
|
||||
pretty >= 1.1.3 && < 1.2,
|
||||
random >= 1.1 && < 1.3,
|
||||
utf8-string >= 1.0.1.1 && < 1.1
|
||||
|
||||
if impl(ghc<8.0)
|
||||
build-depends:
|
||||
-- We need this in order for ghc-7.10 to build
|
||||
transformers-compat >= 0.6.3 && < 0.7,
|
||||
fail >= 4.9.0 && < 4.10
|
||||
|
||||
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
|
||||
|
||||
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:
|
||||
directory >= 1.3.0 && < 1.4,
|
||||
filepath >= 1.4.1 && < 1.5,
|
||||
haskeline >= 0.7.3 && < 0.9,
|
||||
json >= 0.9.1 && <= 0.11,
|
||||
parallel >= 3.2.1.1 && < 3.3,
|
||||
process >= 1.4.3 && < 1.7,
|
||||
time >= 1.6.0 && <= 1.12.2,
|
||||
template-haskell >= 2.13.0.0 && < 2.21
|
||||
|
||||
hs-source-dirs: src/compiler
|
||||
exposed-modules:
|
||||
GF
|
||||
GF.Support
|
||||
GF.Text.Pretty
|
||||
GF.Text.Lexing
|
||||
GF.Grammar.Canonical
|
||||
|
||||
GF.Main GF.Compiler GF.Interactive
|
||||
other-modules:
|
||||
GF.Main
|
||||
GF.Compiler
|
||||
GF.Interactive
|
||||
|
||||
GF.Compile GF.CompileInParallel GF.CompileOne GF.Compile.GetGrammar
|
||||
GF.Compile
|
||||
GF.CompileInParallel
|
||||
GF.CompileOne
|
||||
GF.Compile.GetGrammar
|
||||
GF.Grammar
|
||||
|
||||
GF.Data.Operations GF.Infra.Option GF.Infra.UseIO
|
||||
GF.Data.Operations
|
||||
GF.Infra.Option
|
||||
GF.Infra.UseIO
|
||||
|
||||
GF.Command.Abstract
|
||||
GF.Command.CommandInfo
|
||||
@@ -110,20 +198,25 @@ executable gf
|
||||
GF.Compile.CFGtoPGF
|
||||
GF.Compile.CheckGrammar
|
||||
GF.Compile.Compute.Concrete
|
||||
GF.Compile.Compute.Predef
|
||||
GF.Compile.Compute.Value
|
||||
GF.Compile.ExampleBased
|
||||
GF.Compile.Export
|
||||
GF.Compile.GenerateBC
|
||||
GF.Compile.GeneratePMCFG
|
||||
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
|
||||
@@ -190,35 +283,123 @@ executable gf
|
||||
GF.System.Directory
|
||||
GF.System.Process
|
||||
GF.System.Signal
|
||||
GF.System.NoSignal
|
||||
GF.Text.Clitics
|
||||
GF.Text.Coding
|
||||
GF.Text.Lexing
|
||||
GF.Text.Transliterations
|
||||
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 flag(c-runtime)
|
||||
cpp-options: -DC_RUNTIME
|
||||
|
||||
if flag(server)
|
||||
build-depends:
|
||||
cgi >= 3001.3.0.2 && < 3001.6,
|
||||
httpd-shed >= 0.4.0 && < 0.5,
|
||||
network>=2.3 && <3.2
|
||||
if flag(network-uri)
|
||||
build-depends:
|
||||
network-uri >= 2.6.1.0 && < 2.7,
|
||||
network>=2.6 && <3.2
|
||||
else
|
||||
build-depends:
|
||||
network >= 2.5 && <3.2
|
||||
|
||||
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 >= 2.3.1.1 && < 2.7
|
||||
else
|
||||
build-depends:
|
||||
terminfo >=0.4.0 && < 0.5,
|
||||
unix >= 2.7.2 && < 2.8
|
||||
terminfo >=0.4.0 && < 0.5
|
||||
|
||||
if impl(ghc >= 9.6)
|
||||
build-depends: unix >= 2.8 && < 2.9
|
||||
|
||||
else
|
||||
build-depends: unix >= 2.7.2 && < 2.8
|
||||
|
||||
|
||||
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 >= 4.9.1 && < 4.22
|
||||
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.9.1 && < 4.15,
|
||||
base >= 4.9.1 && < 4.22,
|
||||
Cabal >= 1.8,
|
||||
directory >= 1.3.0 && < 1.4,
|
||||
filepath >= 1.4.1 && < 1.5,
|
||||
|
||||
43
index.html
43
index.html
@@ -57,11 +57,12 @@
|
||||
<li><a href="doc/gf-shell-reference.html">Shell Reference</a></li>
|
||||
<li><a href="http://www.molto-project.eu/sites/default/files/MOLTO_D2.3.pdf">Best Practices</a> <small>[PDF]</small></li>
|
||||
<li><a href="https://www.mitpressjournals.org/doi/pdf/10.1162/COLI_a_00378">Scaling Up (Computational Linguistics 2020)</a></li>
|
||||
<li><a href="https://inariksit.github.io/blog/">GF blog</a></li>
|
||||
</ul>
|
||||
|
||||
<a href="lib/doc/synopsis/index.html" class="btn btn-primary ml-3">
|
||||
<i class="fab fa-readme mr-1"></i>
|
||||
RGL Synopsis
|
||||
RGL API
|
||||
</a>
|
||||
</div>
|
||||
|
||||
@@ -71,8 +72,12 @@
|
||||
<li><a href="doc/gf-developers.html">Developers Guide</a></li>
|
||||
<!-- <li><a href="/~hallgren/gf-experiment/browse/">Browse Source Code</a></li> -->
|
||||
<li>PGF library API:<br>
|
||||
<a href="http://hackage.haskell.org/package/gf/docs/PGF.html">Haskell</a> /
|
||||
<a href="doc/runtime-api.html">C runtime</a>
|
||||
<ul>
|
||||
<li><a href="http://hackage.haskell.org/package/gf/docs/PGF.html">Haskell</a>
|
||||
</li><li><a href="doc/runtime-api.html#python">Python</a>
|
||||
</li><li><a href="doc/runtime-api.html">C runtime</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li><a href="http://hackage.haskell.org/package/gf/docs/GF.html">GF compiler API</a></li>
|
||||
<!-- <li><a href="src/ui/android/README">GF on Android (new)</a></li>
|
||||
@@ -86,11 +91,6 @@
|
||||
<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
|
||||
@@ -104,7 +104,7 @@
|
||||
</li>
|
||||
<li><a href="https://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="//school.grammaticalframework.org/">Summer School</a></li>
|
||||
<li><a href="doc/gf-people.html">Authors</a></li>
|
||||
</ul>
|
||||
<a href="https://github.com/GrammaticalFramework/" class="btn btn-primary ml-3">
|
||||
@@ -231,14 +231,10 @@ 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>.
|
||||
</p>
|
||||
<p>
|
||||
There is also a <a href="https://discord.gg/EvfUsjzmaz">GF server on Discord</a>.
|
||||
We run the <a href="https://discord.gg/EvfUsjzmaz">GF server on Discord</a>, where you are welcome to look for help with small questions or just start a general discussion.
|
||||
</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
|
||||
@@ -253,6 +249,19 @@ least one, it may help you to get a first idea of what GF is.
|
||||
<div class="col-md-6">
|
||||
<h2>News</h2>
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3 text-center text-nowrap">2025-08-08</dt>
|
||||
<dd class="col-sm-9">
|
||||
<strong>GF 3.12 released.</strong>
|
||||
<a href="download/release-3.12.html">Release notes</a>
|
||||
</dd>
|
||||
<dt class="col-sm-3 text-center text-nowrap">2025-01-18</dt>
|
||||
<dd class="col-sm-9">
|
||||
<a href="//school.grammaticalframework.org/2025/">9th GF Summer School</a>, in Gothenburg, Sweden, 18 – 29 August 2025.
|
||||
</dd>
|
||||
<dt class="col-sm-3 text-center text-nowrap">2023-01-24</dt>
|
||||
<dd class="col-sm-9">
|
||||
<a href="//school.grammaticalframework.org/2023/">8th GF Summer School</a>, in Tampere, Finland, 14 – 25 August 2023.
|
||||
</dd>
|
||||
<dt class="col-sm-3 text-center text-nowrap">2021-07-25</dt>
|
||||
<dd class="col-sm-9">
|
||||
<strong>GF 3.11 released.</strong>
|
||||
@@ -262,10 +271,6 @@ least one, it may help you to get a first idea of what GF is.
|
||||
<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 – 6 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.
|
||||
|
||||
12
nix/expose-all.patch
Normal file
12
nix/expose-all.patch
Normal file
@@ -0,0 +1,12 @@
|
||||
diff --git a/gf.cabal b/gf.cabal
|
||||
index 0076e7638..8d3fe4b49 100644
|
||||
--- a/gf.cabal
|
||||
+++ b/gf.cabal
|
||||
@@ -168,7 +168,6 @@ Library
|
||||
GF.Text.Lexing
|
||||
GF.Grammar.Canonical
|
||||
|
||||
- other-modules:
|
||||
GF.Main
|
||||
GF.Compiler
|
||||
GF.Interactive
|
||||
193
nix/revert-new-cabal-madness.patch
Normal file
193
nix/revert-new-cabal-madness.patch
Normal file
@@ -0,0 +1,193 @@
|
||||
commit 45e5473fcd5707af93646d9a116867a4d4e3e9c9
|
||||
Author: Andreas Källberg <anka.213@gmail.com>
|
||||
Date: Mon Oct 10 14:57:12 2022 +0200
|
||||
|
||||
Revert "workaround for the Nix madness"
|
||||
|
||||
This reverts commit 1294269cd60f3db7b056135104615625baeb528c.
|
||||
|
||||
There are easier workarounds, like using
|
||||
|
||||
cabal v1-build
|
||||
|
||||
etc. instead of just `cabal build`
|
||||
|
||||
These changes also broke a whole bunch of other stuff
|
||||
|
||||
diff --git a/README.md b/README.md
|
||||
index ba35795a4..79e6ab68f 100644
|
||||
--- a/README.md
|
||||
+++ b/README.md
|
||||
@@ -38,21 +38,6 @@ or:
|
||||
```
|
||||
stack install
|
||||
```
|
||||
-Note that if you are unlucky to have Cabal 3.0 or later, then it uses
|
||||
-the so-called Nix style commands. Using those for GF development is
|
||||
-a pain. Every time when you change something in the source code, Cabal
|
||||
-will generate a new folder for GF to look for the GF libraries and
|
||||
-the GF cloud. Either reinstall everything with every change in the
|
||||
-compiler, or be sane and stop using cabal-install. Instead you can do:
|
||||
-```
|
||||
-runghc Setup.hs configure
|
||||
-runghc Setup.hs build
|
||||
-sudo runghc Setup.hs install
|
||||
-```
|
||||
-The script will install the GF dependencies globally. The only solution
|
||||
-to the Nix madness that I found is radical:
|
||||
-
|
||||
- "No person, no problem" (Нет человека – нет проблемы).
|
||||
|
||||
For more information, including links to precompiled binaries, see the [download page](https://www.grammaticalframework.org/download/index.html).
|
||||
|
||||
diff --git a/Setup.hs b/Setup.hs
|
||||
index 58dc3e0c6..f8309cc00 100644
|
||||
--- a/Setup.hs
|
||||
+++ b/Setup.hs
|
||||
@@ -4,68 +4,42 @@ import Distribution.Simple.LocalBuildInfo(LocalBuildInfo(..),absoluteInstallDirs
|
||||
import Distribution.Simple.Setup(BuildFlags(..),Flag(..),InstallFlags(..),CopyDest(..),CopyFlags(..),SDistFlags(..))
|
||||
import Distribution.PackageDescription(PackageDescription(..),emptyHookedBuildInfo)
|
||||
import Distribution.Simple.BuildPaths(exeExtension)
|
||||
-import System.Directory
|
||||
import System.FilePath((</>),(<.>))
|
||||
-import System.Process
|
||||
-import Control.Monad(forM_,unless)
|
||||
-import Control.Exception(bracket_)
|
||||
-import Data.Char(isSpace)
|
||||
|
||||
import WebSetup
|
||||
|
||||
+-- | Notice about RGL not built anymore
|
||||
+noRGLmsg :: IO ()
|
||||
+noRGLmsg = putStrLn "Notice: the RGL is not built as part of GF anymore. See https://github.com/GrammaticalFramework/gf-rgl"
|
||||
+
|
||||
main :: IO ()
|
||||
main = defaultMainWithHooks simpleUserHooks
|
||||
- { preConf = gfPreConf
|
||||
- , preBuild = gfPreBuild
|
||||
+ { preBuild = gfPreBuild
|
||||
, postBuild = gfPostBuild
|
||||
, preInst = gfPreInst
|
||||
, postInst = gfPostInst
|
||||
, postCopy = gfPostCopy
|
||||
}
|
||||
where
|
||||
- gfPreConf args flags = do
|
||||
- pkgs <- fmap (map (dropWhile isSpace) . tail . lines)
|
||||
- (readProcess "ghc-pkg" ["list"] "")
|
||||
- forM_ dependencies $ \pkg -> do
|
||||
- let name = takeWhile (/='/') (drop 36 pkg)
|
||||
- unless (name `elem` pkgs) $ do
|
||||
- let fname = name <.> ".tar.gz"
|
||||
- callProcess "wget" [pkg,"-O",fname]
|
||||
- callProcess "tar" ["-xzf",fname]
|
||||
- removeFile fname
|
||||
- bracket_ (setCurrentDirectory name) (setCurrentDirectory ".." >> removeDirectoryRecursive name) $ do
|
||||
- exists <- doesFileExist "Setup.hs"
|
||||
- unless exists $ do
|
||||
- writeFile "Setup.hs" (unlines [
|
||||
- "import Distribution.Simple",
|
||||
- "main = defaultMain"
|
||||
- ])
|
||||
- let to_descr = reverse .
|
||||
- (++) (reverse ".cabal") .
|
||||
- drop 1 .
|
||||
- dropWhile (/='-') .
|
||||
- reverse
|
||||
- callProcess "wget" [to_descr pkg, "-O", to_descr name]
|
||||
- callProcess "runghc" ["Setup.hs","configure"]
|
||||
- callProcess "runghc" ["Setup.hs","build"]
|
||||
- callProcess "sudo" ["runghc","Setup.hs","install"]
|
||||
-
|
||||
- preConf simpleUserHooks args flags
|
||||
-
|
||||
- gfPreBuild args = gfPre args . buildDistPref
|
||||
- gfPreInst args = gfPre args . installDistPref
|
||||
+ gfPreBuild args = gfPre args . buildDistPref
|
||||
+ gfPreInst args = gfPre args . installDistPref
|
||||
|
||||
gfPre args distFlag = do
|
||||
return emptyHookedBuildInfo
|
||||
|
||||
gfPostBuild args flags pkg lbi = do
|
||||
+ -- noRGLmsg
|
||||
let gf = default_gf lbi
|
||||
buildWeb gf flags (pkg,lbi)
|
||||
|
||||
gfPostInst args flags pkg lbi = do
|
||||
+ -- noRGLmsg
|
||||
+ saveInstallPath args flags (pkg,lbi)
|
||||
installWeb (pkg,lbi)
|
||||
|
||||
gfPostCopy args flags pkg lbi = do
|
||||
+ -- noRGLmsg
|
||||
+ saveCopyPath args flags (pkg,lbi)
|
||||
copyWeb flags (pkg,lbi)
|
||||
|
||||
-- `cabal sdist` will not make a proper dist archive, for that see `make sdist`
|
||||
@@ -73,16 +47,27 @@ main = defaultMainWithHooks simpleUserHooks
|
||||
gfSDist pkg lbi hooks flags = do
|
||||
return ()
|
||||
|
||||
-dependencies = [
|
||||
- "https://hackage.haskell.org/package/utf8-string-1.0.2/utf8-string-1.0.2.tar.gz",
|
||||
- "https://hackage.haskell.org/package/json-0.10/json-0.10.tar.gz",
|
||||
- "https://hackage.haskell.org/package/network-bsd-2.8.1.0/network-bsd-2.8.1.0.tar.gz",
|
||||
- "https://hackage.haskell.org/package/httpd-shed-0.4.1.1/httpd-shed-0.4.1.1.tar.gz",
|
||||
- "https://hackage.haskell.org/package/exceptions-0.10.5/exceptions-0.10.5.tar.gz",
|
||||
- "https://hackage.haskell.org/package/stringsearch-0.3.6.6/stringsearch-0.3.6.6.tar.gz",
|
||||
- "https://hackage.haskell.org/package/multipart-0.2.1/multipart-0.2.1.tar.gz",
|
||||
- "https://hackage.haskell.org/package/cgi-3001.5.0.0/cgi-3001.5.0.0.tar.gz"
|
||||
- ]
|
||||
+saveInstallPath :: [String] -> InstallFlags -> (PackageDescription, LocalBuildInfo) -> IO ()
|
||||
+saveInstallPath args flags bi = do
|
||||
+ let
|
||||
+ dest = NoCopyDest
|
||||
+ dir = datadir (uncurry absoluteInstallDirs bi dest)
|
||||
+ writeFile dataDirFile dir
|
||||
+
|
||||
+saveCopyPath :: [String] -> CopyFlags -> (PackageDescription, LocalBuildInfo) -> IO ()
|
||||
+saveCopyPath args flags bi = do
|
||||
+ let
|
||||
+ dest = case copyDest flags of
|
||||
+ NoFlag -> NoCopyDest
|
||||
+ Flag d -> d
|
||||
+ dir = datadir (uncurry absoluteInstallDirs bi dest)
|
||||
+ writeFile dataDirFile dir
|
||||
+
|
||||
+-- | Name of file where installation's data directory is recording
|
||||
+-- This is a last-resort way in which the seprate RGL build script
|
||||
+-- can determine where to put the compiled RGL files
|
||||
+dataDirFile :: String
|
||||
+dataDirFile = "DATA_DIR"
|
||||
|
||||
-- | Get path to locally-built gf
|
||||
default_gf :: LocalBuildInfo -> FilePath
|
||||
diff --git a/gf.cabal b/gf.cabal
|
||||
index a055b86be..d00a5b935 100644
|
||||
--- a/gf.cabal
|
||||
+++ b/gf.cabal
|
||||
@@ -2,7 +2,7 @@ name: gf
|
||||
version: 3.11.0-git
|
||||
|
||||
cabal-version: 1.22
|
||||
-build-type: Simple
|
||||
+build-type: Custom
|
||||
license: OtherLicense
|
||||
license-file: LICENSE
|
||||
category: Natural Language Processing, Compiler
|
||||
@@ -44,6 +44,14 @@ data-files:
|
||||
www/translator/*.css
|
||||
www/translator/*.js
|
||||
|
||||
+custom-setup
|
||||
+ setup-depends:
|
||||
+ base >= 4.9.1 && < 4.16,
|
||||
+ Cabal >= 1.22.0.0,
|
||||
+ directory >= 1.3.0 && < 1.4,
|
||||
+ filepath >= 1.4.1 && < 1.5,
|
||||
+ process >= 1.0.1.1 && < 1.7
|
||||
+
|
||||
source-repository head
|
||||
type: git
|
||||
location: https://github.com/GrammaticalFramework/gf-core.git
|
||||
@@ -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 =
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
{-# LANGUAGE FlexibleInstances, UndecidableInstances, CPP #-}
|
||||
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 System.Info(os)
|
||||
|
||||
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
|
||||
@@ -16,6 +22,7 @@ import GF.Infra.SIO
|
||||
import GF.Command.Abstract
|
||||
import GF.Command.CommandInfo
|
||||
import GF.Command.CommonCommands
|
||||
import qualified GF.Command.CommonCommands as Common
|
||||
import GF.Text.Clitics
|
||||
import GF.Quiz
|
||||
|
||||
@@ -23,28 +30,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 +64,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 +76,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 +98,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 +108,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,80 +149,82 @@ 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",
|
||||
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 ?))"
|
||||
mkEx $ "gr -- one tree in the startcat of the current grammar, up to depth " ++ Common.default_depth_str,
|
||||
mkEx "gr -cat=NP -number=16 -- 16 trees in the category NP",
|
||||
mkEx "gr -cat=NP -depth=2 -- one tree in the category NP, up to depth 2",
|
||||
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.",
|
||||
"Generates a list of random trees, by default one tree up to depth " ++ Common.default_depth_str ++ ".",
|
||||
"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 (default: " ++ Common.default_depth_str ++ ")"),
|
||||
("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" Common.default_depth 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 " ++ Common.default_depth_str ++ ", 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 (default: " ++ Common.default_depth_str ++ ")"),
|
||||
("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 -cat=NP -number=16 -- 16 trees in the category NP",
|
||||
mkEx "gt (AdjCN ? (UseN ?)) -- trees of form (AdjCN ? (UseN ?))"
|
||||
mkEx $ "gt -- all trees in the startcat, to depth " ++ Common.default_depth_str,
|
||||
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" Common.default_depth opts
|
||||
let ts = case toExprs arg of
|
||||
[] -> generateAllDepth pgfr (optType pgf opts) (Just dp)
|
||||
es -> concat [generateFromDepth pgfr e (Just dp) | e <- es]
|
||||
returnFromExprs $ take (optNumInf opts) ts
|
||||
}),
|
||||
|
||||
("i", emptyCommandInfo {
|
||||
longname = "import",
|
||||
synopsis = "import a grammar from source code or compiled .pgf file",
|
||||
@@ -238,28 +245,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 +279,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 +313,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 +338,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 +360,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 +397,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 +419,6 @@ pgfCommands = Map.fromList [
|
||||
mkEx ("pg -funs | ? grep \" S ;\" -- show functions with value cat S")
|
||||
]
|
||||
}),
|
||||
|
||||
("pt", emptyCommandInfo {
|
||||
longname = "put_tree",
|
||||
syntax = "pt OPT? TREE",
|
||||
@@ -399,14 +430,14 @@ pgfCommands = Map.fromList [
|
||||
"are type checking and semantic computation."
|
||||
],
|
||||
examples = [
|
||||
mkEx "pt -compute (plus one two) -- compute value"
|
||||
mkEx "pt -compute (plus one two) -- compute value",
|
||||
mkEx ("p \"the 4 dogs\" | pt -transfer=digits2numeral | l -- \"the four dogs\" ")
|
||||
],
|
||||
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 +450,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 +462,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 +476,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 +533,7 @@ pgfCommands = Map.fromList [
|
||||
]
|
||||
}),
|
||||
|
||||
|
||||
("vd", emptyCommandInfo {
|
||||
longname = "visualize_dependency",
|
||||
synopsis = "show word dependency tree graphically",
|
||||
@@ -486,9 +549,9 @@ pgfCommands = Map.fromList [
|
||||
"which is processed by dot (graphviz) and displayed by the program indicated",
|
||||
"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).",
|
||||
"See also 'vp -showdep' for another visualization of dependencies."
|
||||
"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 +564,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 +600,7 @@ pgfCommands = Map.fromList [
|
||||
]
|
||||
}),
|
||||
|
||||
|
||||
("vp", emptyCommandInfo {
|
||||
longname = "visualize_parse",
|
||||
synopsis = "show parse tree graphically",
|
||||
@@ -548,8 +612,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 +627,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 +665,7 @@ pgfCommands = Map.fromList [
|
||||
]
|
||||
}),
|
||||
|
||||
|
||||
("vt", emptyCommandInfo {
|
||||
longname = "visualize_tree",
|
||||
synopsis = "show a set of trees graphically",
|
||||
@@ -613,7 +678,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 +690,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 +712,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 +724,209 @@ 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 -> errorWithoutStackTrace $ 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" Common.default_depth 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
|
||||
optViewGraph opts = valStrOpts "view" open_cmd 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
|
||||
open_cmd | os == "linux" = "xdg-open"
|
||||
| os == "mingw32" = "start"
|
||||
| otherwise = "open"
|
||||
|
||||
prGrammar pgf opts
|
||||
returnFromExprs es = return $ case es of
|
||||
[] -> pipeMessage "no trees found"
|
||||
_ -> fromExprs es
|
||||
|
||||
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 +939,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 +959,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
|
||||
@@ -893,13 +1008,13 @@ viewLatex view name grphs = do
|
||||
restrictedSystem $ "pdflatex " ++ texfile
|
||||
restrictedSystem $ view ++ " " ++ pdffile
|
||||
return void
|
||||
|
||||
|
||||
---- copied from VisualizeTree ; not sure about proper place AR Nov 2015
|
||||
latexDoc :: [String] -> String
|
||||
latexDoc body = unlines $
|
||||
"\\batchmode"
|
||||
: "\\documentclass{article}"
|
||||
: "\\usepackage[utf8]{inputenc}"
|
||||
: "\\usepackage[utf8]{inputenc}"
|
||||
: "\\begin{document}"
|
||||
: spaces body
|
||||
++ ["\\end{document}"]
|
||||
|
||||
831
src/compiler/GF/Command/Commands2.hs
Normal file
831
src/compiler/GF/Command/Commands2.hs
Normal 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"
|
||||
@@ -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
|
||||
@@ -16,7 +17,13 @@ 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(..))
|
||||
|
||||
-- store default generation depth in a variable and use everywhere
|
||||
default_depth :: Int
|
||||
default_depth = 5
|
||||
default_depth_str = show default_depth
|
||||
|
||||
|
||||
extend old new = Map.union (Map.fromList new) old -- Map.union is left-biased
|
||||
|
||||
@@ -102,7 +109,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 +124,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 = [
|
||||
@@ -175,6 +186,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 +239,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 +264,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.Compute.Concrete(normalForm,resourceValues)
|
||||
import GF.Compile.TypeCheck.Concrete 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 = normalForm (resourceValues opts sgr) (L NoLoc identW) t
|
||||
t2 = evalStr t1
|
||||
checkPredefError t2
|
||||
where
|
||||
-- ** Try to compute pre{...} tokens in token sequences
|
||||
evalStr t =
|
||||
|
||||
@@ -1,29 +1,34 @@
|
||||
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 PGF.Data(Expr(EApp,EFun))
|
||||
import PGF.TypeCheck(inferExpr)
|
||||
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))),
|
||||
("transfer",("apply this transfer function to all maximal subtrees of suitable type",
|
||||
Right $ \f -> map (transfer pgf f))), -- HL 12/24, modified from gf-3.3
|
||||
("largest",("sort trees from largest to smallest, in number of nodes",
|
||||
Left $ largest)),
|
||||
("nub",("remove duplicate trees",
|
||||
("nub\t",("remove duplicate trees",
|
||||
Left $ nub)),
|
||||
("smallest",("sort trees from smallest to largest, in number of nodes",
|
||||
Left $ smallest)),
|
||||
("subtrees",("return all fully applied subtrees (stopping at abstractions), by default sorted from the largest",
|
||||
Left $ concatMap subtrees)),
|
||||
("funs",("return all fun functions appearing in the tree, with duplications",
|
||||
("funs\t",("return all fun functions appearing in the tree, with duplications",
|
||||
Left $ \es -> [mkApp f [] | e <- es, f <- exprFunctions e]))
|
||||
]
|
||||
|
||||
@@ -33,7 +38,32 @@ 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
|
||||
_ -> [] -- don't go under abstractions
|
||||
|
||||
-- Apply transfer function f:C -> D to all maximal subtrees s:C of tree e and replace
|
||||
-- these s by the values of f(s). This modifies the 'simple-minded transfer' of gf-3.3.
|
||||
-- If applied to strict subtrees s of e, better use with f:C -> C only. HL 12/2024
|
||||
|
||||
transfer :: PGF -> CId -> Expr -> Expr
|
||||
transfer pgf f e = case inferExpr pgf (appf e) of
|
||||
Left _err -> case e of
|
||||
EApp g a -> EApp (transfer pgf f g) (transfer pgf f a)
|
||||
_ -> e
|
||||
Right _ty -> case (compute pgf (appf e)) of
|
||||
v | v /= (appf e) -> v
|
||||
_ -> e -- default case of f, or f has no computation rule
|
||||
where
|
||||
appf = EApp (EFun f)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module GF.Compile (compileToPGF, link, batchCompile, srcAbsName) where
|
||||
|
||||
import GF.Compile.GrammarToPGF(grammar2PGF)
|
||||
import GF.Compile.GrammarToPGF(mkCanon2pgf)
|
||||
import GF.Compile.ReadFiles(ModEnv,getOptionsFromFile,getAllFiles,
|
||||
importsOfModule)
|
||||
import GF.CompileOne(compileOne)
|
||||
@@ -14,7 +14,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,liftM)
|
||||
import GF.System.Directory(doesFileExist,getModificationTime)
|
||||
import System.FilePath((</>),isRelative,dropFileName)
|
||||
import qualified Data.Map as Map(empty,insert,elems) --lookup
|
||||
@@ -22,7 +22,8 @@ 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)
|
||||
|
||||
-- | Compiles a number of source files and builds a 'PGF' structure for them.
|
||||
-- This is a composition of 'link' and 'batchCompile'.
|
||||
@@ -35,10 +36,11 @@ 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
|
||||
|
||||
-- | 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 +78,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 +98,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
|
||||
|
||||
@@ -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 ) =
|
||||
@@ -116,13 +105,22 @@ cf2concr opts abstr cfg =
|
||||
|
||||
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 ) =
|
||||
@@ -132,6 +130,5 @@ cf2concr opts abstr cfg =
|
||||
|
||||
mkRuleName rule =
|
||||
case ruleName rule of
|
||||
CFObj n _ -> n
|
||||
_ -> "_"
|
||||
-}
|
||||
CFObj n _ -> n
|
||||
_ -> wildCId
|
||||
|
||||
@@ -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 GF.Compile.TypeCheck.Concrete(computeLType,checkLType,inferLType,ppType)
|
||||
import qualified GF.Compile.TypeCheck.ConcreteNew as CN(checkLType,inferLType)
|
||||
import GF.Compile.Compute.Concrete(normalForm)
|
||||
import qualified GF.Compile.Compute.Concrete as CN(normalForm,resourceValues)
|
||||
|
||||
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
|
||||
@@ -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
|
||||
@@ -135,8 +140,9 @@ checkCompleteGrammar opts cwd gr (am,abs) (cm,cnc) = checkInModule cwd cnc NoLoc
|
||||
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,30 +158,37 @@ 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" $
|
||||
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)) ->
|
||||
@@ -195,11 +208,11 @@ checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ 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))
|
||||
@@ -210,55 +223,55 @@ checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ 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
|
||||
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 $
|
||||
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
|
||||
@@ -268,7 +281,7 @@ checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ do
|
||||
_ -> return ()
|
||||
|
||||
mkCheck loc cat ss = case ss of
|
||||
[] -> return sm
|
||||
[] -> return info
|
||||
_ -> chIn loc cat $ checkError (vcat ss)
|
||||
|
||||
compAbsTyp g t = case t of
|
||||
@@ -281,9 +294,7 @@ checkInfo opts cwd sgr sm (c,info) = checkInModule cwd (snd sm) NoLoc empty $ do
|
||||
t' <- compAbsTyp ((x,Vr x):g) t
|
||||
return $ Prod b x a' t'
|
||||
Abs _ _ _ -> return t
|
||||
_ -> composOp (compAbsTyp g) t
|
||||
|
||||
update (mn,mi) c info = return (mn,mi{jments=Map.insert c info (jments mi)})
|
||||
_ -> composOp (compAbsTyp g) t
|
||||
|
||||
|
||||
-- | for grammars obtained otherwise than by parsing ---- update!!
|
||||
@@ -295,13 +306,12 @@ 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
|
||||
@@ -313,8 +323,8 @@ linTypeOfType cnc m (L loc typ) = do
|
||||
"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
|
||||
]
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
172
src/compiler/GF/Compile/Compute/Predef.hs
Normal file
172
src/compiler/GF/Compile/Compute/Predef.hs
Normal file
@@ -0,0 +1,172 @@
|
||||
-- | 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
|
||||
fromValue v = case v of
|
||||
VCApp (mn,i) [] | mn == cPredef && i == cPTrue -> return True
|
||||
VCApp (mn,i) [] | mn == cPredef && i == cPFalse -> return False
|
||||
_ -> verror "Bool" v
|
||||
|
||||
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
|
||||
-}
|
||||
56
src/compiler/GF/Compile/Compute/Value.hs
Normal file
56
src/compiler/GF/Compile/Compute/Value.hs
Normal 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.Concrete
|
||||
| VProd BindType Value Ident Binding -- used in Compute.Concrete
|
||||
| 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)
|
||||
@@ -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
|
||||
@@ -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 = showRawIdent name ++ ".hs" :: FilePath
|
||||
]
|
||||
|
||||
-- | Generate Haskell code for the given concrete module.
|
||||
-- The only options that make a difference are
|
||||
@@ -183,9 +181,9 @@ 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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.Concrete(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 a = CM (\gr c s -> c a s)
|
||||
(<*>) = ap
|
||||
|
||||
instance Monad CnvMonad where
|
||||
return = pure
|
||||
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
|
||||
|
||||
@@ -42,21 +42,29 @@ getSourceModule opts file0 =
|
||||
raw <- liftIO $ keepTemp tmp
|
||||
--ePutStrLn $ "1 "++file0
|
||||
(optCoding,parsed) <- parseSource opts pModDef raw
|
||||
let indentLines = unlines . map (" "++) . lines
|
||||
case parsed of
|
||||
Left (Pn l c,msg) -> do file <- liftIO $ writeTemp tmp
|
||||
cwd <- getCurrentDirectory
|
||||
let location = makeRelative cwd file++":"++show l++":"++show c
|
||||
raise (location++":\n "++msg)
|
||||
raise (location++":\n" ++ indentLines msg)
|
||||
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
|
||||
|
||||
@@ -15,12 +15,12 @@ import GF.Grammar.Lookup(lookupOrigInfo,allOrigInfos,allParamValues)
|
||||
import GF.Grammar.Macros(typeForm,collectOp,collectPattOp,composSafeOp,mkAbs,mkApp,term2patt,sortRec)
|
||||
import GF.Grammar.Lockfield(isLockLabel)
|
||||
import GF.Grammar.Predef(cPredef,cInts)
|
||||
-- import GF.Compile.Compute.Value(Predefined(..))
|
||||
import GF.Compile.Compute.Predef(predef)
|
||||
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 PGF.Internal(Literal(..))
|
||||
import GF.Compile.Compute.Concrete(GlobalEnv,normalForm,resourceValues)
|
||||
import GF.Grammar.Canonical as C
|
||||
import System.FilePath ((</>), (<.>))
|
||||
import qualified Debug.Trace as T
|
||||
@@ -28,16 +28,15 @@ import qualified Debug.Trace as T
|
||||
|
||||
-- | 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 -> G.Grammar -> 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 :: ModuleName -> G.Grammar -> 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 +49,7 @@ abstract2canonical absname gr =
|
||||
convHypo (bt,name,t) =
|
||||
case typeForm t of
|
||||
([],(_,cat),[]) -> gId cat -- !!
|
||||
tf -> error ("abstract2canonical convHypo: " ++ show tf)
|
||||
tf -> error $ "abstract2canonical convHypo: " ++ show tf
|
||||
|
||||
convType t =
|
||||
case typeForm t of
|
||||
@@ -63,24 +62,27 @@ abstract2canonical absname gr =
|
||||
|
||||
-- | 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 :: Options -> ModuleName -> G.Grammar -> [(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"
|
||||
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 :: G.Grammar -> GlobalEnv -> ModuleName -> ModuleName -> ModuleInfo -> Concrete
|
||||
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,25 +92,32 @@ 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 :: G.Grammar -> ModuleName -> GlobalEnv -> (Ident, Info) -> [(S.Set QIdent, Either LincatDef LinDef)]
|
||||
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' = cleanupRecordFields lincat $
|
||||
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)
|
||||
|
||||
unAbs 0 t = t
|
||||
unAbs n (Abs _ _ t) = unAbs (n-1) t
|
||||
unAbs _ t = t
|
||||
@@ -184,18 +193,18 @@ convert' gr vs = ppT
|
||||
Cn x -> VarValue (gId x) -- hmm
|
||||
Con c -> ParamConstant (Param (gId c) [])
|
||||
Sort k -> VarValue (gId k)
|
||||
EInt n -> LiteralValue (LInt n)
|
||||
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 (LStr s)
|
||||
Empty -> LiteralValue (LStr "")
|
||||
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
|
||||
|
||||
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"
|
||||
@@ -205,7 +214,7 @@ convert' gr vs = ppT
|
||||
_ -> VarValue (gQId cPredef n) -- hmm
|
||||
where
|
||||
p = PredefValue . PredefId . rawIdentS
|
||||
-}
|
||||
|
||||
ppP p =
|
||||
case p of
|
||||
PC c ps -> ParamPattern (Param (gId c) (map ppP ps))
|
||||
@@ -234,12 +243,12 @@ convert' gr vs = ppT
|
||||
pre (K s) = [s]
|
||||
pre Empty = [""] -- Empty == K ""
|
||||
pre (Strs ts) = concatMap pre ts
|
||||
pre (EPatt _ _ p) = pat p
|
||||
pre (EPatt p) = pat p
|
||||
pre t = error $ "convert' alts 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 (PSeq p1 p2) = [s1++s2 | s1<-pat p1, s2<-pat p2]
|
||||
pat p = error $ "convert' alts pat: "++show p
|
||||
|
||||
fields = map field . filter (not.isLockLabel.fst)
|
||||
@@ -256,8 +265,8 @@ convert' gr vs = ppT
|
||||
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
|
||||
@@ -420,5 +429,11 @@ unqual n = Unqual (ident2raw n)
|
||||
|
||||
convFlags :: G.Grammar -> ModuleName -> Flags
|
||||
convFlags gr mn =
|
||||
Flags [(rawIdentS n,v) |
|
||||
Flags [(rawIdentS 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
|
||||
|
||||
@@ -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
|
||||
-}
|
||||
|
||||
232
src/compiler/GF/Compile/Optimize.hs
Normal file
232
src/compiler/GF/Compile/Optimize.hs
Normal 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.Concrete(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
|
||||
@@ -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
|
||||
-}
|
||||
@@ -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 qualified Data.Map as Map
|
||||
|
||||
type Prefix = String -> String
|
||||
@@ -52,7 +51,7 @@ 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 Control.Monad", "import Data.Monoid"]
|
||||
| dataExt = ["import Data.Data"]
|
||||
| otherwise = []
|
||||
pgfImports | pgf2 = ["import PGF2 hiding (Tree)", "", "showCId :: CId -> String", "showCId = id"]
|
||||
@@ -259,7 +258,7 @@ fInstance gId lexical m (cat,rules) =
|
||||
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)
|
||||
@@ -280,22 +279,19 @@ fInstance gId lexical m (cat,rules) =
|
||||
|
||||
--type HSkeleton = [(OIdent, [(OIdent, [OIdent])])]
|
||||
hSkeleton :: PGF -> (String,HSkeleton)
|
||||
hSkeleton gr =
|
||||
(abstractName 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)]
|
||||
hSkeleton gr =
|
||||
(showCId (absname gr),
|
||||
let fs =
|
||||
[(showCId c, [(showCId f, map showCId cs) | (f, (cs,_)) <- fs]) |
|
||||
fs@((_, (_,c)):_) <- fns]
|
||||
in fs ++ [(sc, []) | c <- cts, let sc = showCId c, sc `notElem` (["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 =
|
||||
|
||||
105
src/compiler/GF/Compile/PGFtoJS.hs
Normal file
105
src/compiler/GF/Compile/PGFtoJS.hs
Normal 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 ]
|
||||
@@ -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
|
||||
|
||||
@@ -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]]
|
||||
|
||||
|
||||
262
src/compiler/GF/Compile/PGFtoProlog.hs
Normal file
262
src/compiler/GF/Compile/PGFtoProlog.hs
Normal 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
|
||||
122
src/compiler/GF/Compile/PGFtoPython.hs
Normal file
122
src/compiler/GF/Compile/PGFtoPython.hs
Normal 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 = ""
|
||||
@@ -130,8 +130,8 @@ renameIdentTerm' env@(act,imps) t0 =
|
||||
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
|
||||
@@ -168,9 +168,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
|
||||
@@ -237,9 +237,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 +306,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
|
||||
|
||||
@@ -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 ++
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -13,7 +13,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
|
||||
|
||||
@@ -265,10 +264,9 @@ 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
|
||||
@@ -292,7 +290,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 +302,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,44 +314,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
|
||||
getOverload :: SourceGrammar -> Context -> Maybe Type -> Term -> Check (Maybe (Term,Type))
|
||||
@@ -630,8 +596,7 @@ checkLType gr g trm typ0 = do
|
||||
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
|
||||
@@ -668,11 +633,11 @@ pattContext env g typ p = case p of
|
||||
fsep pts <+>
|
||||
"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!
|
||||
|
||||
@@ -11,6 +11,7 @@ import GF.Grammar.Lookup
|
||||
import GF.Grammar.Predef
|
||||
import GF.Grammar.Lockfield
|
||||
import GF.Compile.Compute.Concrete
|
||||
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
|
||||
@@ -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
|
||||
@@ -643,7 +644,7 @@ data TcResult a
|
||||
newtype TcM a = TcM {unTcM :: MetaStore -> [Message] -> TcResult a}
|
||||
|
||||
instance Monad TcM where
|
||||
return x = TcM (\ms msgs -> TcOk x ms msgs)
|
||||
return = pure
|
||||
f >>= g = TcM (\ms msgs -> case unTcM f ms msgs of
|
||||
TcOk x ms msgs -> unTcM (g x) ms msgs
|
||||
TcFail msgs -> TcFail msgs)
|
||||
@@ -658,7 +659,7 @@ instance Fail.MonadFail TcM where
|
||||
|
||||
|
||||
instance Applicative TcM where
|
||||
pure = return
|
||||
pure x = TcM (\ms msgs -> TcOk x ms msgs)
|
||||
(<*>) = ap
|
||||
|
||||
instance Functor TcM where
|
||||
@@ -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)
|
||||
-}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -35,7 +35,7 @@ data AExp =
|
||||
AVr Ident Val
|
||||
| ACn QIdent Val
|
||||
| AType
|
||||
| AInt Integer
|
||||
| AInt Int
|
||||
| AFloat Double
|
||||
| AStr String
|
||||
| AMeta MetaId Val
|
||||
|
||||
@@ -78,7 +78,7 @@ extendModule cwd gr (name,m)
|
||||
-- | 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
|
||||
@@ -115,7 +115,7 @@ rebuildModule cwd gr mo@(i,mi@(ModInfo mt stat fs_ me mw ops_ med_ msrc_ js_)) =
|
||||
else 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')
|
||||
|
||||
@@ -168,7 +168,7 @@ extendMod gr isCompl ((name,mi),cond) base new = foldM try new $ Map.toList (jme
|
||||
indirInfo :: ModuleName -> Info -> Info
|
||||
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)
|
||||
AnyInd b k -> (b,k)
|
||||
@@ -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
|
||||
@@ -201,9 +201,9 @@ unifyAnyInfo m i j = case (i,j) of
|
||||
|
||||
(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) ->
|
||||
|
||||
@@ -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"
|
||||
@@ -58,11 +61,11 @@ parallelBatchCompile jobs opts rootfiles0 =
|
||||
|
||||
usesPresent (_,paths) = take 1 libs==["present"]
|
||||
where
|
||||
libs = [p|path<-paths,
|
||||
let (d,p0) = splitAt n path
|
||||
p = dropSlash p0,
|
||||
d==lib_dir,p `elem` all_modes]
|
||||
n = length lib_dir
|
||||
libs = [p | path<-paths,
|
||||
let (d,p0) = splitAt n path
|
||||
p = dropSlash p0,
|
||||
d==lib_dir, p `elem` all_modes]
|
||||
n = length lib_dir
|
||||
|
||||
all_modes = ["alltenses","present"]
|
||||
|
||||
@@ -172,7 +175,7 @@ batchCompile1 lib_dir (opts,filepaths) =
|
||||
" from being compiled."
|
||||
else return (maximum ts,(cnc,gr))
|
||||
|
||||
splitEither es = ([x|Left x<-es],[y|Right y<-es])
|
||||
splitEither es = ([x | Left x<-es], [y | Right y<-es])
|
||||
|
||||
canonical path = liftIO $ D.canonicalizePath path `catch` const (return path)
|
||||
|
||||
@@ -235,12 +238,12 @@ runCO (CO m) = do (o,x) <- m
|
||||
instance Functor m => Functor (CollectOutput m) where
|
||||
fmap f (CO m) = CO (fmap (fmap f) m)
|
||||
|
||||
instance (Functor m,Monad m) => Applicative (CollectOutput m) where
|
||||
pure = return
|
||||
instance (Functor m,Monad m) => Applicative (CollectOutput m) where
|
||||
pure x = CO (return (return (),x))
|
||||
(<*>) = ap
|
||||
|
||||
instance Monad m => Monad (CollectOutput m) where
|
||||
return x = CO (return (return (),x))
|
||||
return = pure
|
||||
CO m >>= f = CO $ do (o1,x) <- m
|
||||
let CO m2 = f x
|
||||
(o2,y) <- m2
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
module GF.Compiler (mainGFC, linkGrammars, writeGrammar, writeOutputs) where
|
||||
module GF.Compiler (mainGFC, linkGrammars, writePGF, writeOutputs) where
|
||||
|
||||
import PGF2
|
||||
import PGF2.Internal(unionPGF,writeConcr)
|
||||
import PGF
|
||||
import PGF.Internal(concretes,optimizePGF,unionPGF)
|
||||
import PGF.Internal(putSplitAbs,encodeFile,runPut)
|
||||
import GF.Compile as S(batchCompile,link,srcAbsName)
|
||||
import GF.CompileInParallel as P(parallelBatchCompile)
|
||||
import GF.Compile.Export
|
||||
@@ -15,7 +16,6 @@ import GF.Grammar.CFG
|
||||
--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)
|
||||
@@ -68,25 +68,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 +92,7 @@ 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 opts (t_src,~cnc_grs@(~(cnc,gr):_)) =
|
||||
do let abs = render (srcAbsName gr cnc)
|
||||
pgfFile = outputPath opts (grammarName' opts abs<.>"pgf")
|
||||
@@ -105,8 +102,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 +115,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 +138,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 writePGF opts pgf
|
||||
writeOutputs opts pgf
|
||||
|
||||
readPGFVerbose f =
|
||||
@@ -159,24 +162,21 @@ writeOutputs opts pgf = do
|
||||
-- | 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 ()
|
||||
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
|
||||
|
||||
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)
|
||||
forM_ (Map.toList (concretes pgf)) $ \cnc -> do
|
||||
let outfile = outputPath opts (showCId (fst cnc) <.> "pgf_c")
|
||||
writing opts outfile $ encodeFile outfile cnc
|
||||
|
||||
|
||||
writeOutput :: Options -> FilePath-> String -> IOE ()
|
||||
@@ -186,7 +186,7 @@ writeOutput opts file str = writing opts path $ writeUTF8File path str
|
||||
-- * 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)
|
||||
|
||||
@@ -64,11 +64,11 @@ finalStates :: BacktrackM s () -> s -> [s]
|
||||
finalStates bm = map fst . runBM bm
|
||||
|
||||
instance Applicative (BacktrackM s) where
|
||||
pure = return
|
||||
pure a = BM (\c s b -> c a s b)
|
||||
(<*>) = ap
|
||||
|
||||
instance Monad (BacktrackM s) where
|
||||
return a = BM (\c s b -> c a s b)
|
||||
return = pure
|
||||
BM m >>= k = BM (\c s b -> m (\a s b -> unBM (k a) c s b) s b)
|
||||
where unBM (BM m) = m
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ fromErr :: a -> Err a -> a
|
||||
fromErr a = err (const a) id
|
||||
|
||||
instance Monad Err where
|
||||
return = Ok
|
||||
return = pure
|
||||
Ok a >>= f = f a
|
||||
Bad s >>= f = Bad s
|
||||
|
||||
@@ -54,7 +54,7 @@ instance Functor Err where
|
||||
fmap f (Bad s) = Bad s
|
||||
|
||||
instance Applicative Err where
|
||||
pure = return
|
||||
pure = Ok
|
||||
(<*>) = ap
|
||||
|
||||
-- | added by KJ
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) []
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,11 +9,9 @@
|
||||
|
||||
{-# 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
|
||||
@@ -60,7 +58,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 +74,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]
|
||||
@@ -117,8 +120,9 @@ newtype FunId = FunId Id deriving (Eq,Show)
|
||||
|
||||
data VarId = Anonymous | VarId Id deriving 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
|
||||
@@ -239,13 +243,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 "="
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -8,7 +8,7 @@ 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)
|
||||
@@ -171,13 +171,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:
|
||||
@@ -262,6 +262,15 @@ instance JSON Flags where
|
||||
where fromRow (lbl, jsvalue) = do value <- readJSON jsvalue
|
||||
return (rawIdentS 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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,12 +73,12 @@ 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)
|
||||
import qualified Data.Map as Map
|
||||
import qualified Data.Set as Set
|
||||
import GF.Text.Pretty
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@ data ModuleInfo = ModInfo {
|
||||
mopens :: [OpenSpec],
|
||||
mexdeps :: [ModuleName],
|
||||
msrc :: FilePath,
|
||||
mseqs :: Maybe (Array SeqId Sequence),
|
||||
jments :: Map.Map Ident Info
|
||||
}
|
||||
|
||||
@@ -125,10 +126,20 @@ extends :: ModuleInfo -> [ModuleName]
|
||||
extends = map fst . mextend
|
||||
|
||||
isInherited :: MInclude -> Ident -> Bool
|
||||
isInherited c i = case c of
|
||||
MIAll -> True
|
||||
MIOnly is -> elem i is
|
||||
MIExcept is -> notElem i is
|
||||
isInherited c =
|
||||
case c of
|
||||
MIAll -> const True
|
||||
MIOnly is -> elemOrd is
|
||||
MIExcept is -> not . elemOrd is
|
||||
|
||||
-- | Faster version of `elem`, using a `Set`.
|
||||
-- Make sure you give this the first argument _outside_ of the inner loop
|
||||
--
|
||||
-- Example:
|
||||
-- > myIntersection xs ys = filter (elemOrd xs) ys
|
||||
elemOrd :: Ord a => [a] -> a -> Bool
|
||||
elemOrd list = (`Set.member` set)
|
||||
where set = Set.fromList list
|
||||
|
||||
inheritAll :: ModuleName -> (ModuleName,MInclude)
|
||||
inheritAll i = (i,MIAll)
|
||||
@@ -304,11 +315,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 +339,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 +364,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 +396,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 +408,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 +419,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 +431,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 +459,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)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
module GF.Grammar.Lexer
|
||||
( Token(..), Posn(..)
|
||||
, P, runP, runPartial, token, lexer, getPosn, failLoc
|
||||
, isReservedWord
|
||||
, isReservedWord, invMap
|
||||
) where
|
||||
|
||||
import Control.Applicative
|
||||
@@ -130,11 +130,11 @@ 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
|
||||
-- deriving Show -- debug
|
||||
deriving (Eq, Ord, Show) -- debug
|
||||
|
||||
res = eitherResIdent
|
||||
eitherResIdent :: (Ident -> Token) -> Ident -> Token
|
||||
@@ -224,6 +224,13 @@ resWords = Map.fromList
|
||||
]
|
||||
where b s t = (identS s, t)
|
||||
|
||||
invMap :: Map.Map Token String
|
||||
invMap = res
|
||||
where
|
||||
lst = Map.toList resWords
|
||||
flp = map (\(k,v) -> (v,showIdent k)) lst
|
||||
res = Map.fromList flp
|
||||
|
||||
unescapeInitTail :: String -> String
|
||||
unescapeInitTail = unesc . tail where
|
||||
unesc s = case s of
|
||||
@@ -276,11 +283,11 @@ instance Functor P where
|
||||
fmap = liftA
|
||||
|
||||
instance Applicative P where
|
||||
pure = return
|
||||
pure a = a `seq` (P $ \s -> POk s a)
|
||||
(<*>) = ap
|
||||
|
||||
instance Monad P where
|
||||
return a = a `seq` (P $ \s -> POk s a)
|
||||
return = pure
|
||||
(P m) >>= k = P $ \ s -> case m s of
|
||||
POk s a -> unP (k a) s
|
||||
PFailed posn err -> PFailed posn err
|
||||
|
||||
@@ -78,12 +78,12 @@ lookupResDefLoc gr (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
|
||||
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
|
||||
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] ++
|
||||
@@ -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 =
|
||||
@@ -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))]
|
||||
_ -> []
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
@@ -416,9 +422,9 @@ patt2term pt = case pt of
|
||||
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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
}
|
||||
|
||||
@@ -36,6 +37,9 @@ import qualified Data.Map as Map
|
||||
%name pBNFCRules ListCFRule
|
||||
%name pEBNFRules ListEBNFRule
|
||||
|
||||
%errorhandlertype explist
|
||||
%error { happyError }
|
||||
|
||||
-- no lexer declaration
|
||||
%monad { P } { >>= } { return }
|
||||
%lexer { lexer } { T_EOF }
|
||||
@@ -132,14 +136,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 +271,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)] }
|
||||
@@ -429,6 +433,7 @@ Exp3
|
||||
RecType xs -> RecType (xs ++ [(tupleLabel (length xs+1),$3)])
|
||||
t -> RecType [(tupleLabel 1,$1), (tupleLabel 2,$3)] }
|
||||
| Exp3 '**' Exp4 { ExtR $1 $3 }
|
||||
| Exp3 '**' '{' ListCase '}' { let v = identS "$vvv" in T TRaw ($4 ++ [(PV v, S $1 (Vr v))]) }
|
||||
| Exp4 { $1 }
|
||||
|
||||
Exp4 :: { Term }
|
||||
@@ -444,7 +449,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 +490,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 +629,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 +642,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] }
|
||||
@@ -700,8 +705,18 @@ Posn
|
||||
|
||||
{
|
||||
|
||||
happyError :: P a
|
||||
happyError = fail "syntax error"
|
||||
happyError :: (Token, [String]) -> P a
|
||||
happyError (t,strs) = fail $
|
||||
"Syntax error:\n Unexpected " ++ showToken t ++ ".\n Expected one of:\n"
|
||||
++ unlines (map ((" - "++).cleanupToken) strs)
|
||||
|
||||
where
|
||||
cleanupToken "Ident" = "an identifier"
|
||||
cleanupToken x = x
|
||||
showToken (T_Ident i) = "identifier '" ++ showIdent i ++ "'"
|
||||
showToken t = case Map.lookup t invMap of
|
||||
Nothing -> show t
|
||||
Just s -> "token '" ++ s ++"'"
|
||||
|
||||
mkListId,mkConsId,mkBaseId :: Ident -> Ident
|
||||
mkListId = prefixIdent "List"
|
||||
@@ -774,7 +789,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
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
module GF.Grammar.PatternMatch (
|
||||
matchPattern,
|
||||
testOvershadow,
|
||||
findMatch
|
||||
findMatch,
|
||||
measurePatt
|
||||
) where
|
||||
|
||||
import GF.Data.Operations
|
||||
@@ -24,7 +25,7 @@ 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
|
||||
@@ -121,10 +122,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 +140,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
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user