1
0
forked from GitHub/gf-core

Compare commits

..

118 Commits

Author SHA1 Message Date
John J. Camilleri
b47eb18f86 Minor additions to LPGF readme 2021-08-27 09:32:43 +02:00
John J. Camilleri
1ce1cea068 Add bounds check (probably unnecessary) 2021-08-10 16:45:42 +02:00
John J. Camilleri
48dba4ade5 Pass all missing test cases, including Phrasebook, except PhrasebookSnd 2021-08-10 11:46:28 +02:00
John J. Camilleri
b96fa7e08a Add more unit tests for missing lin functions 2021-08-10 11:32:51 +02:00
John J. Camilleri
42bdee1e5f Remove workarounds for bugs in canonical format 2021-08-04 10:58:49 +02:00
John J. Camilleri
e2ed512bbb Merge branch 'master' into lpgf 2021-08-01 09:38:42 +02:00
John J. Camilleri
1b8a9b37b0 Remove auto/wagen variant from PhrasebookGer 2021-07-22 22:22:56 +02:00
John J. Camilleri
e681e4dbb0 Remove --show-details flag in CI/cabal 2021-07-08 15:05:47 +02:00
John J. Camilleri
639f1f043a Cabal file fixes. Fix tests in CI with cabal too. 2021-07-08 14:26:41 +02:00
John J. Camilleri
c02a3e0617 Merge branch 'master' into lpgf 2021-07-08 13:57:18 +02:00
John J. Camilleri
d6e26e0577 Do not run lpgf tests in CI, since they require RGL 2021-07-08 13:45:26 +02:00
John J. Camilleri
89a01d81cc Add ghc-prim to build-depends for LPGF testsuite 2021-07-08 12:46:49 +02:00
John J. Camilleri
2315641e77 Don't build benchmarks in CI 2021-07-08 12:33:39 +02:00
John J. Camilleri
7dc396e841 Merge branch 'master' into lpgf
# Conflicts:
#	gf.cabal
2021-07-08 12:25:46 +02:00
John J. Camilleri
d6416089d6 Move some definitions into LPGF.Internal, clean up public API. 2021-07-08 11:34:29 +02:00
John J. Camilleri
7b0637850c Add explicit exports to LPGF module 2021-07-07 13:25:41 +02:00
John J. Camilleri
2b8d792e09 Updates to cabal file 2021-07-07 12:38:01 +02:00
John J. Camilleri
045def61d8 Merge branch 'master' into lpgf
# Conflicts:
#	gf.cabal
#	src/compiler/GF/Grammar/Canonical.hs
2021-07-07 09:42:44 +02:00
John J. Camilleri
2be54ffb12 Update ConcreteNew, RConcrete in gf.cabal 2021-07-07 08:55:35 +02:00
John J. Camilleri
4bd26eae6d Merge branch 'master' into lpgf
# Conflicts:
#	gf.cabal
#	src/compiler/GF/Compile/GrammarToCanonical.hs
#	src/compiler/GF/Grammar/Canonical.hs
#	src/compiler/GF/Infra/Option.hs
2021-07-07 08:36:09 +02:00
John J. Camilleri
c1af40532c Add some stuff to gitignore 2021-04-30 13:06:50 +02:00
John J. Camilleri
d0c27cdaae Make GF.Grammar.Canonical.Id a type synonym for GF.Infra.Ident.RawIdent
This avoids a lot of conversion back and forth between Strings and ByteStrings
2021-04-06 22:15:07 +02:00
John J. Camilleri
f7df62a445 Add support for literals 2021-03-22 09:12:34 +01:00
John J. Camilleri
2d066853f1 Add unit test for literals (fails) 2021-03-22 08:37:59 +01:00
John J. Camilleri
f900ea3885 Don't process impossible values at all (not even for finding their types) 2021-03-16 16:59:58 +01:00
John J. Camilleri
d9c37fc093 Minors to LPGF readme 2021-03-12 13:47:36 +01:00
John J. Camilleri
c9f0867491 Remove state Map from compilation 2021-03-12 13:46:50 +01:00
John J. Camilleri
6c6a201d96 Introduce state with Map for caching compilation, but results are worse 2021-03-12 13:39:56 +01:00
John J. Camilleri
8f5033e4ce Add notes on profiling 2021-03-09 08:36:35 +01:00
John J. Camilleri
126b61ea03 Merge branch 'master' into lpgf 2021-03-08 13:52:34 +01:00
John J. Camilleri
99abb9b2a5 Add Phrasebook benchmark snippet to LPGF README 2021-03-08 13:37:02 +01:00
John J. Camilleri
3e9d12854a Switch to 10000-tree Phrasebook treebank. All errors to do with missing functions, plus variants in German. 2021-03-08 11:19:06 +01:00
John J. Camilleri
fd07946a50 Remove commented line 2021-03-08 10:42:16 +01:00
John J. Camilleri
c76efcf916 Use C runtime in mkTreebank script 2021-03-08 10:17:03 +01:00
John J. Camilleri
785d6069e2 Fix lin2string and pass all unittests and Phrasebook 2021-03-08 09:53:10 +01:00
John J. Camilleri
0f4b349b0b Remove old commented code 2021-03-05 16:51:59 +01:00
John J. Camilleri
dbf369aae5 Make removal of record fields recursive. Latest results with Phrasebook:
Bul ✓
Cat ✗
Chi ✓
Dan ✓
Dut ✓
Eng ✓
Est ✓
Fin ✓
Fre ✗
Ger ✓
Hin ✓
Ita ✗
Jpn ✓
Lav ✓
Nor ✓
Pol ✓
Ron ✓
Snd ✗
Spa ✓
Swe ✓
Tha ✓
Urd ✓

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

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

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

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

View File

@@ -53,12 +53,12 @@ jobs:
- name: Build - name: Build
run: | run: |
cabal configure --enable-tests --enable-benchmarks --test-show-details=direct cabal configure --enable-tests --test-show-details=direct
cabal build all cabal build
# - name: Test - name: Test
# run: | run: |
# cabal test all PATH="$PWD/dist/build/gf:$PATH" cabal test gf-tests
stack: stack:
name: stack / ghc ${{ matrix.ghc }} name: stack / ghc ${{ matrix.ghc }}
@@ -88,9 +88,8 @@ jobs:
- name: Build - name: Build
run: | run: |
stack build --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml stack build --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml --test --no-run-tests
# stack build --system-ghc --test --bench --no-run-tests --no-run-benchmarks
- name: Test - name: Test
run: | run: |
stack test --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml stack test --system-ghc --stack-yaml stack-ghc${{ matrix.ghc }}.yaml gf:test:gf-tests

10
.gitignore vendored
View File

@@ -5,6 +5,7 @@
*.jar *.jar
*.gfo *.gfo
*.pgf *.pgf
*.lpgf
debian/.debhelper debian/.debhelper
debian/debhelper-build-stamp debian/debhelper-build-stamp
debian/gf debian/gf
@@ -48,7 +49,7 @@ src/runtime/java/.libs/
src/runtime/python/build/ src/runtime/python/build/
.cabal-sandbox .cabal-sandbox
cabal.sandbox.config cabal.sandbox.config
.stack-work .stack-work*
DATA_DIR DATA_DIR
stack*.yaml.lock stack*.yaml.lock
@@ -73,3 +74,10 @@ doc/icfp-2012.html
download/*.html download/*.html
gf-book/index.html gf-book/index.html
src/www/gf-web-api.html src/www/gf-web-api.html
DEBUG/
PROF/
*.aux
*.hp
*.prof
*.ps

655
gf.cabal
View File

@@ -63,38 +63,133 @@ flag network-uri
description: Get Network.URI from the network-uri package description: Get Network.URI from the network-uri package
default: True default: True
executable gf --flag new-comp
hs-source-dirs: src/programs, src/compiler -- Description: Make -new-comp the default
main-is: gf-main.hs -- 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 default-language: Haskell2010
build-depends: pgf2, build-depends:
base >= 4.6 && <5, -- GHC 8.0.2 to GHC 8.10.4
array, array >= 0.5.1 && < 0.6,
containers, base >= 4.9.1 && < 4.15,
bytestring, bytestring >= 0.10.8 && < 0.11,
utf8-string, containers >= 0.5.7 && < 0.7,
random, exceptions >= 0.8.3 && < 0.11,
pretty, ghc-prim >= 0.5.0 && < 0.7,
mtl, hashable >= 1.2.6 && < 1.4,
exceptions, mtl >= 2.2.1 && < 2.3,
ghc-prim, pretty >= 1.1.3 && < 1.2,
filepath, directory>=1.2, time, random >= 1.1 && < 1.3,
process, haskeline, parallel>=3, json text >= 1.2.2 && < 1.3,
ghc-options: -threaded unordered-containers >= 0.2.8 && < 0.3,
utf8-string >= 1.0.1.1 && < 1.1,
-- We need transformers-compat >= 0.6.3, but that is only in newer snapshots where it is redundant.
transformers-compat >= 0.5.1.4 && < 0.7
if impl(ghc<8.0)
build-depends:
fail >= 4.9.0 && < 4.10
hs-source-dirs: src/runtime/haskell
other-modules: 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:
LPGF
PGF
PGF.Internal
PGF.Haskell
other-modules:
LPGF.Internal
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.10
hs-source-dirs: src/compiler
exposed-modules:
GF GF
GF.Support GF.Support
GF.Text.Pretty GF.Text.Pretty
GF.Text.Lexing GF.Text.Lexing
GF.Grammar.Canonical 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.Grammar
GF.Data.Operations GF.Infra.Option GF.Infra.UseIO GF.Data.Operations
GF.Infra.Option
GF.Infra.UseIO
GF.Command.Abstract GF.Command.Abstract
GF.Command.CommandInfo GF.Command.CommandInfo
@@ -109,24 +204,27 @@ executable gf
GF.Command.TreeOperations GF.Command.TreeOperations
GF.Compile.CFGtoPGF GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar GF.Compile.CheckGrammar
GF.Compile.Compute.Concrete
GF.Compile.Compute.Predef GF.Compile.Compute.Predef
GF.Compile.Compute.Value GF.Compile.Compute.Value
GF.Compile.Compute.Concrete
GF.Compile.ExampleBased GF.Compile.ExampleBased
GF.Compile.Export GF.Compile.Export
GF.Compile.GenerateBC GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG GF.Compile.GeneratePMCFG
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF GF.Compile.GrammarToPGF
GF.Compile.Multi GF.Compile.Multi
GF.Compile.Optimize GF.Compile.Optimize
GF.Compile.OptimizePGF
GF.Compile.PGFtoHaskell GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJava GF.Compile.PGFtoJava
GF.Haskell GF.Haskell
GF.Compile.ConcreteToHaskell GF.Compile.ConcreteToHaskell
GF.Compile.GrammarToCanonical GF.Compile.GrammarToCanonical
GF.Grammar.CanonicalJSON GF.Grammar.CanonicalJSON
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON GF.Compile.PGFtoJSON
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles GF.Compile.ReadFiles
GF.Compile.Rename GF.Compile.Rename
GF.Compile.SubExOpt GF.Compile.SubExOpt
@@ -142,6 +240,7 @@ executable gf
GF.Data.ErrM GF.Data.ErrM
GF.Data.Graph GF.Data.Graph
GF.Data.Graphviz GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Relation GF.Data.Relation
GF.Data.Str GF.Data.Str
GF.Data.Utilities GF.Data.Utilities
@@ -193,20 +292,64 @@ executable gf
GF.System.Directory GF.System.Directory
GF.System.Process GF.System.Process
GF.System.Signal GF.System.Signal
GF.System.NoSignal
GF.Text.Clitics GF.Text.Clitics
GF.Text.Coding GF.Text.Coding
GF.Text.Lexing
GF.Text.Transliterations GF.Text.Transliterations
Paths_gf Paths_gf
-- not really part of GF but I have changed the original binary library if flag(c-runtime)
-- and we have to keep the copy for now. cpp-options: -DC_RUNTIME
Data.Binary
Data.Binary.Put if flag(server)
Data.Binary.Get build-depends:
Data.Binary.Builder cgi >= 3001.3.0.2 && < 3001.6,
Data.Binary.IEEE754 httpd-shed >= 0.4.0 && < 0.5,
network>=2.3 && <2.7
if flag(network-uri)
build-depends:
network-uri >= 2.6.1.0 && < 2.7,
network>=2.6 && <2.7
else
build-depends:
network >= 2.5 && <2.6
cpp-options: -DSERVER_MODE
other-modules:
GF.Server
PGFService
RunHTTP
SimpleEditor.Convert
SimpleEditor.JSON
SimpleEditor.Syntax
URLEncoding
CGI
CGIUtils
Cache
Fold
ExampleDemo
ExampleService
hs-source-dirs:
src/server
src/server/transfer
src/example-based
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
other-modules: GF.System.NoSignal
if impl(ghc>=7.8)
build-tools:
happy>=1.19,
alex>=3.1
-- ghc-options: +RTS -A20M -RTS
else
build-tools:
happy,
alex>=3
ghc-options: -fno-warn-tabs
if os(windows) if os(windows)
build-depends: build-depends:
@@ -216,6 +359,44 @@ executable gf
terminfo >=0.4.0 && < 0.5, terminfo >=0.4.0 && < 0.5,
unix >= 2.7.2 && < 2.8 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
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 test-suite gf-tests
type: exitcode-stdio-1.0 type: exitcode-stdio-1.0
main-is: run.hs main-is: run.hs
@@ -228,3 +409,415 @@ test-suite gf-tests
process >= 1.4.3 && < 1.7 process >= 1.4.3 && < 1.7
build-tool-depends: gf:gf build-tool-depends: gf:gf
default-language: Haskell2010 default-language: Haskell2010
test-suite lpgf
type: exitcode-stdio-1.0
main-is: test.hs
hs-source-dirs:
src/compiler
src/runtime/haskell
testsuite/lpgf
other-modules:
Data.Binary
Data.Binary.Builder
Data.Binary.Get
Data.Binary.IEEE754
Data.Binary.Put
GF
GF.Command.Abstract
GF.Command.CommandInfo
GF.Command.Commands
GF.Command.CommonCommands
GF.Command.Help
GF.Command.Importing
GF.Command.Interpreter
GF.Command.Messages
GF.Command.Parse
GF.Command.SourceCommands
GF.Command.TreeOperations
GF.Compile
GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar
GF.Compile.Compute.Concrete
GF.Compile.Compute.Predef
GF.Compile.Compute.Value
GF.Compile.ConcreteToHaskell
GF.Compile.ExampleBased
GF.Compile.Export
GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG
GF.Compile.GetGrammar
GF.Compile.GrammarToCanonical
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF
GF.Compile.Multi
GF.Compile.Optimize
GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJava
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles
GF.Compile.Rename
GF.Compile.SubExOpt
GF.Compile.Tags
GF.Compile.ToAPI
GF.Compile.TypeCheck.Abstract
GF.Compile.TypeCheck.Concrete
GF.Compile.TypeCheck.ConcreteNew
GF.Compile.TypeCheck.Primitives
GF.Compile.TypeCheck.TC
GF.Compile.Update
GF.CompileInParallel
GF.CompileOne
GF.Compiler
GF.Data.BacktrackM
GF.Data.ErrM
GF.Data.Graph
GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Operations
GF.Data.Relation
GF.Data.Str
GF.Data.Utilities
GF.Data.XML
GF.Grammar
GF.Grammar.Analyse
GF.Grammar.Binary
GF.Grammar.BNFC
GF.Grammar.Canonical
GF.Grammar.CanonicalJSON
GF.Grammar.CFG
GF.Grammar.EBNF
GF.Grammar.Grammar
GF.Grammar.Lexer
GF.Grammar.Lockfield
GF.Grammar.Lookup
GF.Grammar.Macros
GF.Grammar.Parser
GF.Grammar.PatternMatch
GF.Grammar.Predef
GF.Grammar.Printer
GF.Grammar.ShowTerm
GF.Grammar.Unify
GF.Grammar.Values
GF.Haskell
GF.Infra.BuildInfo
GF.Infra.CheckM
GF.Infra.Concurrency
GF.Infra.Dependencies
GF.Infra.GetOpt
GF.Infra.Ident
GF.Infra.Location
GF.Infra.Option
GF.Infra.SIO
GF.Infra.UseIO
GF.Interactive
GF.JavaScript.AbsJS
GF.JavaScript.PrintJS
GF.Main
GF.Quiz
GF.Speech.CFGToFA
GF.Speech.FiniteState
GF.Speech.GSL
GF.Speech.JSGF
GF.Speech.PGFToCFG
GF.Speech.PrRegExp
GF.Speech.RegExp
GF.Speech.SISR
GF.Speech.SLF
GF.Speech.SRG
GF.Speech.SRGS_ABNF
GF.Speech.SRGS_XML
GF.Speech.VoiceXML
GF.Support
GF.System.Catch
GF.System.Concurrency
GF.System.Console
GF.System.Directory
GF.System.Process
GF.System.Signal
GF.Text.Clitics
GF.Text.Coding
GF.Text.Lexing
GF.Text.Pretty
GF.Text.Transliterations
LPGF
LPGF.Internal
PGF
PGF.Binary
PGF.ByteCode
PGF.CId
PGF.Data
PGF.Expr
PGF.Forest
PGF.Generate
PGF.Internal
PGF.Linearize
PGF.Macros
PGF.Morphology
PGF.OldBinary
PGF.Optimize
PGF.Paraphrase
PGF.Parse
PGF.Printer
PGF.Probabilistic
PGF.Tree
PGF.TrieMap
PGF.Type
PGF.TypeCheck
PGF.Utilities
PGF.VisualizeTree
Paths_gf
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
other-modules: GF.System.NoSignal
build-depends:
ansi-terminal >= 0.6.3 && < 0.12,
array >= 0.5.1 && < 0.6,
base >=4.6 && < 5,
bytestring >= 0.10.8 && < 0.11,
containers >= 0.5.7 && < 0.7,
directory >= 1.3.0 && < 1.4,
filepath >= 1.4.1 && < 1.5,
ghc-prim >= 0.5.0 && < 0.7,
hashable >= 1.2.6 && < 1.4,
haskeline >= 0.7.3 && < 0.9,
json >= 0.9.1 && < 0.11,
mtl >= 2.2.1 && < 2.3,
parallel >= 3.2.1.1 && < 3.3,
pretty >= 1.1.3 && < 1.2,
process >= 1.4.3 && < 1.7,
random >= 1.1 && < 1.3,
text >= 1.2.2 && < 1.3,
time >= 1.6.0 && < 1.10,
transformers-compat >= 0.5.1.4 && < 0.7,
unordered-containers >= 0.2.8 && < 0.3,
utf8-string >= 1.0.1.1 && < 1.1
if impl(ghc<8.0)
build-depends:
fail >= 4.9.0 && < 4.10
if os(windows)
build-depends:
Win32 >= 2.3.1.1 && < 2.7
else
build-depends:
unix >= 2.7.2 && < 2.8,
terminfo >=0.4.0 && < 0.5
default-language: Haskell2010
benchmark lpgf-bench
type: exitcode-stdio-1.0
main-is: bench.hs
hs-source-dirs:
src/compiler
src/runtime/haskell
testsuite/lpgf
other-modules:
Data.Binary
Data.Binary.Builder
Data.Binary.Get
Data.Binary.IEEE754
Data.Binary.Put
GF
GF.Command.Abstract
GF.Command.CommandInfo
GF.Command.Commands
GF.Command.CommonCommands
GF.Command.Help
GF.Command.Importing
GF.Command.Interpreter
GF.Command.Messages
GF.Command.Parse
GF.Command.SourceCommands
GF.Command.TreeOperations
GF.Compile
GF.Compile.CFGtoPGF
GF.Compile.CheckGrammar
GF.Compile.Compute.Concrete
GF.Compile.Compute.Predef
GF.Compile.Compute.Value
GF.Compile.ConcreteToHaskell
GF.Compile.ExampleBased
GF.Compile.Export
GF.Compile.GenerateBC
GF.Compile.GeneratePMCFG
GF.Compile.GetGrammar
GF.Compile.GrammarToCanonical
GF.Compile.GrammarToLPGF
GF.Compile.GrammarToPGF
GF.Compile.Multi
GF.Compile.Optimize
GF.Compile.PGFtoHaskell
GF.Compile.PGFtoJS
GF.Compile.PGFtoJSON
GF.Compile.PGFtoJava
GF.Compile.PGFtoProlog
GF.Compile.PGFtoPython
GF.Compile.ReadFiles
GF.Compile.Rename
GF.Compile.SubExOpt
GF.Compile.Tags
GF.Compile.ToAPI
GF.Compile.TypeCheck.Abstract
GF.Compile.TypeCheck.Concrete
GF.Compile.TypeCheck.ConcreteNew
GF.Compile.TypeCheck.Primitives
GF.Compile.TypeCheck.TC
GF.Compile.Update
GF.CompileInParallel
GF.CompileOne
GF.Compiler
GF.Data.BacktrackM
GF.Data.ErrM
GF.Data.Graph
GF.Data.Graphviz
GF.Data.IntMapBuilder
GF.Data.Operations
GF.Data.Relation
GF.Data.Str
GF.Data.Utilities
GF.Data.XML
GF.Grammar
GF.Grammar.Analyse
GF.Grammar.BNFC
GF.Grammar.Binary
GF.Grammar.CFG
GF.Grammar.Canonical
GF.Grammar.CanonicalJSON
GF.Grammar.EBNF
GF.Grammar.Grammar
GF.Grammar.Lexer
GF.Grammar.Lockfield
GF.Grammar.Lookup
GF.Grammar.Macros
GF.Grammar.Parser
GF.Grammar.PatternMatch
GF.Grammar.Predef
GF.Grammar.Printer
GF.Grammar.ShowTerm
GF.Grammar.Unify
GF.Grammar.Values
GF.Haskell
GF.Infra.BuildInfo
GF.Infra.CheckM
GF.Infra.Concurrency
GF.Infra.Dependencies
GF.Infra.GetOpt
GF.Infra.Ident
GF.Infra.Location
GF.Infra.Option
GF.Infra.SIO
GF.Infra.UseIO
GF.Interactive
GF.JavaScript.AbsJS
GF.JavaScript.PrintJS
GF.Main
GF.Quiz
GF.Speech.CFGToFA
GF.Speech.FiniteState
GF.Speech.GSL
GF.Speech.JSGF
GF.Speech.PGFToCFG
GF.Speech.PrRegExp
GF.Speech.RegExp
GF.Speech.SISR
GF.Speech.SLF
GF.Speech.SRG
GF.Speech.SRGS_ABNF
GF.Speech.SRGS_XML
GF.Speech.VoiceXML
GF.Support
GF.System.Catch
GF.System.Concurrency
GF.System.Console
GF.System.Directory
GF.System.Process
GF.System.Signal
GF.Text.Clitics
GF.Text.Coding
GF.Text.Lexing
GF.Text.Pretty
GF.Text.Transliterations
LPGF
LPGF.Internal
PGF
PGF.Binary
PGF.ByteCode
PGF.CId
PGF.Data
PGF.Expr
PGF.Expr
PGF.Forest
PGF.Generate
PGF.Internal
PGF.Linearize
PGF.Macros
PGF.Morphology
PGF.OldBinary
PGF.Optimize
PGF.Paraphrase
PGF.Parse
PGF.Printer
PGF.Probabilistic
PGF.Tree
PGF.TrieMap
PGF.Type
PGF.TypeCheck
PGF.Utilities
PGF.VisualizeTree
PGF2
PGF2.Expr
PGF2.Type
PGF2.FFI
Paths_gf
if flag(interrupt)
cpp-options: -DUSE_INTERRUPT
other-modules: GF.System.UseSignal
else
other-modules: GF.System.NoSignal
hs-source-dirs:
src/runtime/haskell-bind
other-modules:
PGF2
PGF2.FFI
PGF2.Expr
PGF2.Type
build-tools: hsc2hs
extra-libraries: pgf gu
c-sources: src/runtime/haskell-bind/utils.c
cc-options: -std=c99
build-depends:
ansi-terminal,
array,
base>=4.6 && <5,
bytestring,
containers,
deepseq,
directory,
filepath,
ghc-prim,
hashable,
haskeline,
json,
mtl,
parallel>=3,
pretty,
process,
random,
terminfo,
text,
time,
transformers-compat,
unix,
unordered-containers,
utf8-string
default-language: Haskell2010

View File

@@ -1,6 +1,6 @@
module GF.Command.Abstract(module GF.Command.Abstract,Expr,showExpr,Term) where 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) import GF.Grammar.Grammar(Term)
type Ident = String type Ident = String
@@ -11,7 +11,7 @@ type Pipe = [Command]
data Command data Command
= Command Ident [Option] Argument = Command Ident [Option] Argument
deriving Show deriving (Eq,Ord,Show)
data Option data Option
= OOpt Ident = OOpt Ident
@@ -29,7 +29,13 @@ data Argument
| ATerm Term | ATerm Term
| ANoArg | ANoArg
| AMacro Ident | 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 :: String -> Int -> [Option] -> Int
valIntOpts flag def opts = valIntOpts flag def opts =
@@ -43,18 +49,6 @@ valStrOpts flag def opts =
v:_ -> valueString v v:_ -> valueString v
_ -> def _ -> 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] listFlags flag opts = [v | OFlag f v <- opts, f == flag]
valueString v = valueString v =

View File

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

View File

@@ -1,12 +1,16 @@
{-# LANGUAGE FlexibleInstances, UndecidableInstances, CPP #-} {-# LANGUAGE FlexibleInstances, UndecidableInstances, CPP #-}
module GF.Command.Commands ( module GF.Command.Commands (
HasPGF(..),pgfCommands, PGFEnv,HasPGFEnv(..),pgf,mos,pgfEnv,pgfCommands,
options,flags, options,flags,
) where ) where
import Prelude hiding (putStrLn,(<>)) import Prelude hiding (putStrLn,(<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import PGF2 import PGF
import PGF2.Internal(writePGF)
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.Export
import GF.Compile.ToAPI import GF.Compile.ToAPI
@@ -24,28 +28,28 @@ import GF.Command.TreeOperations ---- temporary place for typecheck and compute
import GF.Data.Operations import GF.Data.Operations
import Data.Char import PGF.Internal (encodeFile)
import Data.List(intersperse,nub) import Data.List(intersperse,nub)
import Data.Maybe import Data.Maybe
import qualified Data.Map as Map import qualified Data.Map as Map
import GF.Text.Pretty import GF.Text.Pretty
import Data.List (sort) import Data.List (sort)
import Control.Monad(mplus)
import qualified Control.Monad.Fail as Fail import qualified Control.Monad.Fail as Fail
--import Debug.Trace --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 pgfEnv pgf = Env pgf mos
typeCheckArg e = do mb_pgf <- getPGF where mos = Map.fromList [(la,buildMorpho pgf la) | la <- languages pgf]
case mb_pgf of
Just pgf -> either fail
(return . fst)
(inferExpr pgf e)
Nothing -> fail "Import a grammar before using this command"
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 [ pgfCommands = Map.fromList [
("aw", emptyCommandInfo { ("aw", emptyCommandInfo {
longname = "align_words", longname = "align_words",
@@ -58,7 +62,7 @@ pgfCommands = Map.fromList [
"by the view flag. The target format is png, unless overridden by the", "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)." "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 es = toExprs arg
let langs = optLangs pgf opts let langs = optLangs pgf opts
if isOpt "giza" opts if isOpt "giza" opts
@@ -70,7 +74,7 @@ pgfCommands = Map.fromList [
let grph = if null es then [] else lsrc ++ "\n--end_source--\n\n"++ltrg++"\n-end_target--\n\n"++align let grph = if null es then [] else lsrc ++ "\n--end_source--\n\n"++ltrg++"\n-end_target--\n\n"++align
return $ fromString grph return $ fromString grph
else do else do
let grphs = map (graphvizWordAlignment langs graphvizDefaults) es let grphs = map (graphvizAlignment pgf langs) es
if isFlag "view" opts || isFlag "format" opts if isFlag "view" opts || isFlag "format" opts
then do then do
let view = optViewGraph opts let view = optViewGraph opts
@@ -92,7 +96,6 @@ pgfCommands = Map.fromList [
("view", "program to open the resulting file") ("view", "program to open the resulting file")
] ]
}), }),
("ca", emptyCommandInfo { ("ca", emptyCommandInfo {
longname = "clitic_analyse", longname = "clitic_analyse",
synopsis = "print the analyses of all words into stems and clitics", synopsis = "print the analyses of all words into stems and clitics",
@@ -103,16 +106,15 @@ pgfCommands = Map.fromList [
"by the flag '-clitics'. The list of stems is given as the list of words", "by the flag '-clitics'. The list of stems is given as the list of words",
"of the language given by the '-lang' flag." "of the language given by the '-lang' flag."
], ],
exec = needPGF $ \opts ts pgf -> do exec = getEnv $ \opts ts env -> case opts of
concr <- optLang pgf opts
case opts of
_ | isOpt "raw" opts -> _ | isOpt "raw" opts ->
return . fromString . return . fromString .
unlines . map (unwords . map (concat . intersperse "+")) . unlines . map (unwords . map (concat . intersperse "+")) .
map (getClitics (not . null . lookupMorpho concr) (optClitics opts)) . map (getClitics (isInMorpho (optMorpho env opts)) (optClitics opts)) .
concatMap words $ toStrings ts concatMap words $ toStrings ts
_ -> return . fromStrings . _ ->
getCliticsText (not . null . lookupMorpho concr) (optClitics opts) . return . fromStrings .
getCliticsText (isInMorpho (optMorpho env opts)) (optClitics opts) .
concatMap words $ toStrings ts, concatMap words $ toStrings ts,
flags = [ flags = [
("clitics","the list of possible clitics (comma-separated, no spaces)"), ("clitics","the list of possible clitics (comma-separated, no spaces)"),
@@ -145,19 +147,19 @@ pgfCommands = Map.fromList [
], ],
flags = [ flags = [
("file","the file to be converted (suffix .gfe must be given)"), ("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 let file = optFile opts
pgf <- optProbs opts pgf
let printer = if (isOpt "api" opts) then exprToAPI else (showExpr []) let printer = if (isOpt "api" opts) then exprToAPI else (showExpr [])
concr <- optLang pgf opts let conf = configureExBased pgf (optMorpho env opts) (optLang pgf opts) printer
let conf = configureExBased pgf concr printer
(file',ws) <- restricted $ parseExamplesInGrammar conf file (file',ws) <- restricted $ parseExamplesInGrammar conf file
if null ws then return () else putStrLn ("unknown words: " ++ unwords ws) if null ws then return () else putStrLn ("unknown words: " ++ unwords ws)
return (fromString ("wrote " ++ file')), return (fromString ("wrote " ++ file')),
needsTypeCheck = False needsTypeCheck = False
}), }),
("gr", emptyCommandInfo { ("gr", emptyCommandInfo {
longname = "generate_random", longname = "generate_random",
synopsis = "generate random trees in the current abstract syntax", synopsis = "generate random trees in the current abstract syntax",
@@ -172,53 +174,54 @@ pgfCommands = Map.fromList [
explanation = unlines [ explanation = unlines [
"Generates a list of random trees, by default one tree.", "Generates a list of random trees, by default one tree.",
"If a tree argument is given, the command completes the Tree with values to", "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", "all metavariables in the tree. The generation can be biased by probabilities,",
"if the grammar was compiled with option -probs" "given in a file in the -probs flag."
],
options = [
("show_probs", "show the probability of each result")
], ],
flags = [ flags = [
("cat","generation category"), ("cat","generation category"),
("lang","uses only functions that have linearizations in all these languages"), ("lang","uses only functions that have linearizations in all these languages"),
("number","number of trees generated") ("number","number of trees generated"),
("depth","the maximum generation depth"),
("probs", "file with biased probabilities (format 'f 0.4' one by line)")
], ],
exec = needPGF $ \opts arg pgf -> do exec = getEnv $ \ opts arg (Env pgf mos) -> do
pgf <- optProbs opts (optRestricted opts pgf)
gen <- newStdGen gen <- newStdGen
let dp = valIntOpts "depth" 4 opts
let ts = case mexp (toExprs arg) of let ts = case mexp (toExprs arg) of
Just ex -> generateRandomFrom gen pgf ex Just ex -> generateRandomFromDepth gen pgf ex (Just dp)
Nothing -> generateRandom gen pgf (optType pgf opts) Nothing -> generateRandomDepth gen pgf (optType pgf opts) (Just dp)
returnFromExprs (isOpt "show_probs" opts) $ take (optNum opts) ts returnFromExprs $ take (optNum opts) ts
}), }),
("gt", emptyCommandInfo { ("gt", emptyCommandInfo {
longname = "generate_trees", longname = "generate_trees",
synopsis = "generates a list of trees, by default exhaustive", synopsis = "generates a list of trees, by default exhaustive",
explanation = unlines [ explanation = unlines [
"Generates all trees of a given category.", "Generates all trees of a given category. By default, ",
"the depth is limited to 4, but this can be changed by a flag.",
"If a Tree argument is given, the command completes the Tree with values", "If a Tree argument is given, the command completes the Tree with values",
"to all metavariables in the tree." "to all metavariables in the tree."
], ],
options = [
("show_probs", "show the probability of each result")
],
flags = [ flags = [
("cat","the generation category"), ("cat","the generation category"),
("depth","the maximum generation depth"),
("lang","excludes functions that have no linearization in this language"), ("lang","excludes functions that have no linearization in this language"),
("number","the number of trees generated") ("number","the number of trees generated")
], ],
examples = [ examples = [
mkEx "gt -- all trees in the startcat", mkEx "gt -- all trees in the startcat, to depth 4",
mkEx "gt -cat=NP -number=16 -- 16 trees in the category NP", mkEx "gt -cat=NP -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 ?))" mkEx "gt (AdjCN ? (UseN ?)) -- trees of form (AdjCN ? (UseN ?))"
], ],
exec = needPGF $ \opts arg pgf -> do exec = getEnv $ \ opts arg (Env pgf mos) -> do
let es = case mexp (toExprs arg) of let pgfr = optRestricted opts pgf
Just ex -> generateAllFrom pgf ex let dp = valIntOpts "depth" 4 opts
Nothing -> generateAll pgf (optType pgf opts) let ts = case mexp (toExprs arg) of
returnFromExprs (isOpt "show_probs" opts) $ takeOptNum opts es Just ex -> generateFromDepth pgfr ex (Just dp)
Nothing -> generateAllDepth pgfr (optType pgf opts) (Just dp)
returnFromExprs $ take (optNumInf opts) ts
}), }),
("i", emptyCommandInfo { ("i", emptyCommandInfo {
longname = "import", longname = "import",
synopsis = "import a grammar from source code or compiled .pgf file", synopsis = "import a grammar from source code or compiled .pgf file",
@@ -239,28 +242,33 @@ pgfCommands = Map.fromList [
("probs","file with biased probabilities for generation") ("probs","file with biased probabilities for generation")
], ],
options = [ options = [
-- ["gfo", "src", "no-cpu", "cpu", "quiet", "verbose"]
("retain","retain operations (used for cc command)"), ("retain","retain operations (used for cc command)"),
("src", "force compilation from source"), ("src", "force compilation from source"),
("v", "be verbose - show intermediate status information") ("v", "be verbose - show intermediate status information")
], ],
needsTypeCheck = False needsTypeCheck = False
}), }),
("l", emptyCommandInfo { ("l", emptyCommandInfo {
longname = "linearize", longname = "linearize",
synopsis = "convert an abstract syntax expression to string", synopsis = "convert an abstract syntax expression to string",
explanation = unlines [ 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.", "The -lang flag can be used to restrict this to fewer languages.",
"A sequence of string operations (see command ps) can be given", "A sequence of string operations (see command ps) can be given",
"as options, and works then like a pipe to the ps command, except", "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 = [ examples = [
mkEx "l -lang=LangSwe,LangNor no_Utt -- linearize tree to LangSwe and LangNor", 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 = [ options = [
("all", "show all forms and variants, one by line (cf. l -list)"), ("all", "show all forms and variants, one by line (cf. l -list)"),
("bracket","show tree structure with brackets and paths to nodes"), ("bracket","show tree structure with brackets and paths to nodes"),
@@ -268,13 +276,33 @@ pgfCommands = Map.fromList [
("list","show all forms and variants, comma-separated on one line (cf. l -all)"), ("list","show all forms and variants, comma-separated on one line (cf. l -all)"),
("multi","linearize to all languages (default)"), ("multi","linearize to all languages (default)"),
("table","show all forms labelled by parameters"), ("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") ("treebank","show the tree and tag linearizations with language names")
] ++ stringOpOptions, ] ++ stringOpOptions,
flags = [ flags = [
("lang","the languages of linearization (comma-separated, no spaces)") ("lang","the languages of linearization (comma-separated, no spaces)")
] ],
needsTypeCheck = False
}), }),
("ma", emptyCommandInfo { ("ma", emptyCommandInfo {
longname = "morpho_analyse", longname = "morpho_analyse",
synopsis = "print the morphological analyses of all words in the string", synopsis = "print the morphological analyses of all words in the string",
@@ -282,19 +310,17 @@ pgfCommands = Map.fromList [
"Prints all the analyses of space-separated words in the input string,", "Prints all the analyses of space-separated words in the input string,",
"using the morphological analyser of the actual grammar (see command pg)" "using the morphological analyser of the actual grammar (see command pg)"
], ],
exec = needPGF $ \opts ts pgf -> do exec = getEnv $ \opts ts env -> case opts of
concr <- optLang pgf opts
case opts of
_ | isOpt "missing" opts -> _ | isOpt "missing" opts ->
return . fromString . unwords . return . fromString . unwords .
morphoMissing concr . morphoMissing (optMorpho env opts) .
concatMap words $ toStrings ts concatMap words $ toStrings ts
_ | isOpt "known" opts -> _ | isOpt "known" opts ->
return . fromString . unwords . return . fromString . unwords .
morphoKnown concr . morphoKnown (optMorpho env opts) .
concatMap words $ toStrings ts concatMap words $ toStrings ts
_ -> return . fromString . unlines . _ -> return . fromString . unlines .
map prMorphoAnalysis . concatMap (morphos pgf opts) . map prMorphoAnalysis . concatMap (morphos env opts) .
concatMap words $ toStrings ts, concatMap words $ toStrings ts,
flags = [ flags = [
("lang","the languages of analysis (comma-separated, no spaces)") ("lang","the languages of analysis (comma-separated, no spaces)")
@@ -309,16 +335,18 @@ pgfCommands = Map.fromList [
longname = "morpho_quiz", longname = "morpho_quiz",
synopsis = "start a morphology quiz", synopsis = "start a morphology quiz",
syntax = "mq (-cat=CAT)? (-probs=FILE)? TREE?", syntax = "mq (-cat=CAT)? (-probs=FILE)? TREE?",
exec = needPGF $ \ opts arg pgf -> do exec = getEnv $ \ opts arg (Env pgf mos) -> do
lang <- optLang pgf opts let lang = optLang pgf opts
let typ = optType pgf opts let typ = optType pgf opts
pgf <- optProbs opts pgf
let mt = mexp (toExprs arg) let mt = mexp (toExprs arg)
restricted $ morphologyQuiz mt pgf lang typ restricted $ morphologyQuiz mt pgf lang typ
return void, return void,
flags = [ flags = [
("lang","language of the quiz"), ("lang","language of the quiz"),
("cat","category 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")
] ]
}), }),
@@ -329,25 +357,24 @@ pgfCommands = Map.fromList [
"Shows all trees returned by parsing a string in the grammars in scope.", "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 -lang flag can be used to restrict this to fewer languages.",
"The default start category can be overridden by the -cat flag.", "The default start category can be overridden by the -cat flag.",
"See also the ps command for lexing and character encoding." "See also the ps command for lexing and character encoding.",
], "",
exec = needPGF $ \opts ts pgf -> "The -openclass flag is experimental and allows some robustness in ",
return $ "the parser. For example if -openclass=\"A,N,V\" is given, the parser",
foldr (joinPiped . fromParse1 opts) void "will accept unknown adjectives, nouns and verbs with the resource grammar."
(concat [
[(s,parse concr (optType pgf opts) s) |
concr <- optLangs pgf opts]
| s <- toStrings ts]),
options = [
("show_probs", "show the probability of each result")
], ],
exec = getEnv $ \ opts ts (Env pgf mos) ->
return $ fromParse opts (concat [map ((,) s) (par pgf opts s) | s <- toStrings ts]),
flags = [ flags = [
("cat","target category of parsing"), ("cat","target category of parsing"),
("lang","the languages of parsing (comma-separated, no spaces)"), ("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 { ----- ("pg", emptyCommandInfo { -----
longname = "print_grammar", longname = "print_grammar",
synopsis = "print the actual grammar with the given printer", synopsis = "print the actual grammar with the given printer",
@@ -367,8 +394,9 @@ pgfCommands = Map.fromList [
" " ++ opt ++ "\t\t" ++ expl | " " ++ opt ++ "\t\t" ++ expl |
((opt,_),expl) <- outputFormatsExpl, take 1 expl /= "*" ((opt,_),expl) <- outputFormatsExpl, take 1 expl /= "*"
]), ]),
exec = needPGF $ \opts _ pgf -> prGrammar pgf opts, exec = getEnv $ \opts _ env -> prGrammar env opts,
flags = [ flags = [
--"cat",
("file", "set the file name when printing with -pgf option"), ("file", "set the file name when printing with -pgf option"),
("lang", "select languages for the some options (default all languages)"), ("lang", "select languages for the some options (default all languages)"),
("printer","select the printing format (see flag values above)") ("printer","select the printing format (see flag values above)")
@@ -388,7 +416,6 @@ pgfCommands = Map.fromList [
mkEx ("pg -funs | ? grep \" S ;\" -- show functions with value cat S") mkEx ("pg -funs | ? grep \" S ;\" -- show functions with value cat S")
] ]
}), }),
("pt", emptyCommandInfo { ("pt", emptyCommandInfo {
longname = "put_tree", longname = "put_tree",
syntax = "pt OPT? TREE", syntax = "pt OPT? TREE",
@@ -402,12 +429,11 @@ pgfCommands = Map.fromList [
examples = [ examples = [
mkEx "pt -compute (plus one two) -- compute value" mkEx "pt -compute (plus one two) -- compute value"
], ],
exec = needPGF $ \opts arg pgf -> exec = getEnv $ \ opts arg (Env pgf mos) ->
returnFromExprs False . takeOptNum opts . map (flip (,) 0) . treeOps pgf opts $ toExprs arg, returnFromExprs . takeOptNum opts . treeOps pgf opts $ toExprs arg,
options = treeOpOptions undefined{-pgf-}, options = treeOpOptions undefined{-pgf-},
flags = [("number","take at most this many trees")] ++ treeOpFlags undefined{-pgf-} flags = [("number","take at most this many trees")] ++ treeOpFlags undefined{-pgf-}
}), }),
("rf", emptyCommandInfo { ("rf", emptyCommandInfo {
longname = "read_file", longname = "read_file",
synopsis = "read string or tree input from a file", synopsis = "read string or tree input from a file",
@@ -420,9 +446,10 @@ pgfCommands = Map.fromList [
], ],
options = [ options = [
("lines","return the list of lines, instead of the singleton of all contents"), ("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") ("tree","convert strings into trees")
], ],
exec = needPGF $ \ opts _ pgf -> do exec = getEnv $ \ opts _ (Env pgf mos) -> do
let file = valStrOpts "file" "_gftmp" opts let file = valStrOpts "file" "_gftmp" opts
let exprs [] = ([],empty) let exprs [] = ([],empty)
exprs ((n,s):ls) | null s exprs ((n,s):ls) | null s
@@ -431,12 +458,12 @@ pgfCommands = Map.fromList [
Just e -> let (es,err) = exprs ls Just e -> let (es,err) = exprs ls
in case inferExpr pgf e of in case inferExpr pgf e of
Right (e,t) -> (e:es,err) 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 Nothing -> let (es,err) = exprs ls
in (es,"on line" <+> n <> ':' <+> "parse error" $$ err) in (es,"on line" <+> n <> ':' <+> "parse error" $$ err)
returnFromLines ls = case exprs ls of returnFromLines ls = case exprs ls of
(es, err) | null es -> return $ pipeMessage $ render (err $$ "no trees found") (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 s <- restricted $ readFile file
case opts of case opts of
@@ -445,26 +472,56 @@ pgfCommands = Map.fromList [
_ | isOpt "tree" opts -> _ | isOpt "tree" opts ->
returnFromLines [(1::Int,s)] returnFromLines [(1::Int,s)]
_ | isOpt "lines" opts -> return (fromStrings $ lines s) _ | isOpt "lines" opts -> return (fromStrings $ lines s)
_ | isOpt "paragraphs" opts -> return (fromStrings $ toParagraphs $ lines s)
_ -> return (fromString s), _ -> return (fromString s),
flags = [("file","the input file name")] 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 { ("tq", emptyCommandInfo {
longname = "translation_quiz", longname = "translation_quiz",
syntax = "tq -from=LANG -to=LANG (-cat=CAT)? (-probs=FILE)? TREE?", syntax = "tq -from=LANG -to=LANG (-cat=CAT)? (-probs=FILE)? TREE?",
synopsis = "start a translation quiz", synopsis = "start a translation quiz",
exec = needPGF $ \ opts arg pgf -> do exec = getEnv $ \ opts arg (Env pgf mos) -> do
from <- optLangFlag "from" pgf opts let from = optLangFlag "from" pgf opts
to <- optLangFlag "to" pgf opts let to = optLangFlag "to" pgf opts
let typ = optType pgf opts let typ = optType pgf opts
let mt = mexp (toExprs arg) let mt = mexp (toExprs arg)
pgf <- optProbs opts pgf
restricted $ translationQuiz mt pgf from to typ restricted $ translationQuiz mt pgf from to typ
return void, return void,
flags = [ flags = [
("from","translate from this language"), ("from","translate from this language"),
("to","translate to this language"), ("to","translate to this language"),
("cat","translate in this category"), ("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 = [ examples = [
mkEx ("tq -from=Eng -to=Swe -- any trees in startcat"), mkEx ("tq -from=Eng -to=Swe -- any trees in startcat"),
@@ -472,6 +529,7 @@ pgfCommands = Map.fromList [
] ]
}), }),
("vd", emptyCommandInfo { ("vd", emptyCommandInfo {
longname = "visualize_dependency", longname = "visualize_dependency",
synopsis = "show word dependency tree graphically", synopsis = "show word dependency tree graphically",
@@ -489,7 +547,7 @@ pgfCommands = Map.fromList [
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick).", "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 absname = abstractName pgf
let es = toExprs arg let es = toExprs arg
let debug = isOpt "v" opts let debug = isOpt "v" opts
@@ -502,8 +560,8 @@ pgfCommands = Map.fromList [
mclab <- case cnclabels of mclab <- case cnclabels of
"" -> return Nothing "" -> return Nothing
_ -> (Just . getCncDepLabels) `fmap` restricted (readFile cnclabels) _ -> (Just . getCncDepLabels) `fmap` restricted (readFile cnclabels)
concr <- optLang pgf opts let lang = optLang pgf opts
let grphs = map (graphvizDependencyTree outp debug mlab mclab concr) es let grphs = map (graphvizDependencyTree outp debug mlab mclab pgf lang) es
if isOpt "conll2latex" opts if isOpt "conll2latex" opts
then return $ fromString $ conlls2latexDoc $ stanzas $ unlines $ toStrings arg then return $ fromString $ conlls2latexDoc $ stanzas $ unlines $ toStrings arg
else if isFlag "view" opts && valStrOpts "output" "" opts == "latex" else if isFlag "view" opts && valStrOpts "output" "" opts == "latex"
@@ -538,6 +596,7 @@ pgfCommands = Map.fromList [
] ]
}), }),
("vp", emptyCommandInfo { ("vp", emptyCommandInfo {
longname = "visualize_parse", longname = "visualize_parse",
synopsis = "show parse tree graphically", synopsis = "show parse tree graphically",
@@ -549,8 +608,9 @@ pgfCommands = Map.fromList [
"by the view flag. The target format is png, unless overridden by the", "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)." "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 es = toExprs arg
let lang = optLang pgf opts
let gvOptions = GraphvizOptions {noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts), let gvOptions = GraphvizOptions {noLeaves = isOpt "noleaves" opts && not (isOpt "showleaves" opts),
noFun = isOpt "nofun" opts || not (isOpt "showfun" opts), noFun = isOpt "nofun" opts || not (isOpt "showfun" opts),
noCat = isOpt "nocat" opts && not (isOpt "showcat" opts), noCat = isOpt "nocat" opts && not (isOpt "showcat" opts),
@@ -563,11 +623,10 @@ pgfCommands = Map.fromList [
leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts leafEdgeStyle = valStrOpts "leafedgestyle" "dashed" opts
} }
let depfile = valStrOpts "file" "" opts let depfile = valStrOpts "file" "" opts
concr <- optLang pgf opts
mlab <- case depfile of mlab <- case depfile of
"" -> return Nothing "" -> return Nothing
_ -> (Just . getDepLabels) `fmap` restricted (readFile depfile) _ -> (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 if isFlag "view" opts || isFlag "format" opts
then do then do
let view = optViewGraph opts let view = optViewGraph opts
@@ -602,6 +661,7 @@ pgfCommands = Map.fromList [
] ]
}), }),
("vt", emptyCommandInfo { ("vt", emptyCommandInfo {
longname = "visualize_tree", longname = "visualize_tree",
synopsis = "show a set of trees graphically", synopsis = "show a set of trees graphically",
@@ -614,7 +674,7 @@ pgfCommands = Map.fromList [
"flag -format. Results from multiple trees are combined to pdf with convert (ImageMagick).", "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'." "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 let es = toExprs arg in
if isOpt "mk" opts if isOpt "mk" opts
then return $ fromString $ unlines $ map (tree2mk pgf) es then return $ fromString $ unlines $ map (tree2mk pgf) es
@@ -626,7 +686,7 @@ pgfCommands = Map.fromList [
else do else do
let funs = not (isOpt "nofun" opts) let funs = not (isOpt "nofun" opts)
let cats = not (isOpt "nocat" 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 if isFlag "view" opts || isFlag "format" opts
then do then do
let view = optViewGraph opts let view = optViewGraph opts
@@ -648,7 +708,6 @@ pgfCommands = Map.fromList [
("view","program to open the resulting file (default \"open\")") ("view","program to open the resulting file (default \"open\")")
] ]
}), }),
("ai", emptyCommandInfo { ("ai", emptyCommandInfo {
longname = "abstract_info", longname = "abstract_info",
syntax = "ai IDENTIFIER or ai EXPR", syntax = "ai IDENTIFIER or ai EXPR",
@@ -661,28 +720,31 @@ pgfCommands = Map.fromList [
"If a whole expression is given it prints the expression with refined", "If a whole expression is given it prints the expression with refined",
"metavariables and the type of the expression." "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 case toExprs arg of
[e] -> case unApp e of [EFun id] -> case Map.lookup id (funs (abstract pgf)) of
Just (id, []) -> case functionType pgf id of Just fd -> do putStrLn $ render (ppFun id fd)
Just ty -> do putStrLn (showFun pgf id ty) let (_,_,_,prob) = fd
putStrLn ("Probability: "++show (treeProbability pgf e)) putStrLn ("Probability: "++show prob)
return void return void
Nothing -> case categoryContext pgf id of Nothing -> case Map.lookup id (cats (abstract pgf)) of
Just hypos -> do putStrLn ("cat "++id++if null hypos then "" else ' ':showContext [] hypos) Just cd -> do putStrLn $
let ls = [showFun pgf fn ty | fn <- functionsByCat pgf id, Just ty <- [functionType pgf fn]] render (ppCat id cd $$
if null ls if null (functionsToCat pgf id)
then return () then empty
else putStrLn (unlines ("":ls)) else ' ' $$
putStrLn ("Probability: "++show (categoryProbability pgf id)) vcat [ppFun fid (ty,0,Just ([],[]),0) | (fid,ty) <- functionsToCat pgf id] $$
' ')
let (_,_,prob) = cd
putStrLn ("Probability: "++show prob)
return void return void
Nothing -> do putStrLn ("unknown category of function identifier "++show id) Nothing -> do putStrLn ("unknown category of function identifier "++show id)
return void return void
_ -> case inferExpr pgf e of [e] -> case inferExpr pgf e of
Left err -> error err Left tcErr -> errorWithoutStackTrace $ render (ppTcError tcErr)
Right (e,ty) -> do putStrLn ("Expression: "++showExpr [] e) Right (e,ty) -> do putStrLn ("Expression: "++showExpr [] e)
putStrLn ("Type: "++showType [] ty) putStrLn ("Type: "++showType [] ty)
putStrLn ("Probability: "++show (treeProbability pgf e)) putStrLn ("Probability: "++show (probTree pgf e))
return void return void
_ -> do putStrLn "a single identifier or expression is expected from the command" _ -> do putStrLn "a single identifier or expression is expected from the command"
return void, return void,
@@ -690,121 +752,173 @@ pgfCommands = Map.fromList [
}) })
] ]
where where
needPGF exec opts ts = do getEnv exec opts ts = liftSIO . exec opts ts =<< getPGFEnv
mb_pgf <- getPGF
case mb_pgf of par pgf opts s = case optOpenTypes opts of
Just pgf -> liftSIO $ exec opts ts pgf [] -> [parse_ pgf lang (optType pgf opts) (Just dp) s | lang <- optLangs pgf opts]
_ -> fail "Import a grammar before using this command" open_typs -> [parseWithRecovery pgf lang (optType pgf opts) open_typs (Just dp) s | lang <- optLangs pgf opts]
where
dp = valIntOpts "depth" 4 opts
fromParse opts = foldr (joinPiped . fromParse1 opts) void
joinPiped (Piped (es1,ms1)) (Piped (es2,ms2)) = Piped (jA es1 es2,ms1+++-ms2) joinPiped (Piped (es1,ms1)) (Piped (es2,ms2)) = Piped (jA es1 es2,ms1+++-ms2)
where where
jA (Exprs es1) (Exprs es2) = Exprs (es1++es2) jA (Exprs es1) (Exprs es2) = Exprs (es1++es2)
-- ^ fromParse1 always output Exprs
fromParse1 opts (s,po) = fromParse1 opts (s,(po,bs))
| isOpt "bracket" opts = pipeMessage (showBracketedString bs)
| otherwise =
case po of case po of
ParseOk ts -> fromExprs (isOpt "show_probs" opts) (takeOptNum opts ts) ParseOk ts -> fromExprs ts
ParseFailed i t -> pipeMessage $ "The parser failed at token " ParseFailed i -> pipeMessage $ "The parser failed at token "
++ show i ++": " ++ show i ++": "
++ show t ++ show (words s !! max 0 (i-1))
-- ++ " in " ++ show s
ParseIncomplete -> pipeMessage "The sentence is not complete" 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 = optLin pgf opts t =
case opts of 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 -> _ | isOpt "treebank" opts ->
(abstractName pgf ++ ": " ++ showExpr [] t) : (showCId (abstractName pgf) ++ ": " ++ showExpr [] t) :
[concreteName concr ++ ": " ++ s | concr <- optLangs pgf opts, s<-linear opts concr t] [showCId lang ++ ": " ++ s | lang <- optLangs pgf opts, s<-linear pgf opts lang t]
_ -> [s | concr <- optLangs pgf opts, s <- linear opts concr 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 :: PGF -> [Option] -> CId -> Expr -> [String]
linear opts concr = case opts of linear pgf opts lang = let unl = unlex opts lang in case opts of
_ | isOpt "all" opts -> concat . _ | isOpt "all" opts -> concat . -- intersperse [[]] .
map (map snd) . tabularLinearizeAll concr map (map (unl . snd)) . tabularLinearizes pgf lang
_ | isOpt "list" opts -> (:[]) . commaList . concat . _ | isOpt "list" opts -> (:[]) . commaList . concat .
map (map snd) . tabularLinearizeAll concr map (map (unl . snd)) . tabularLinearizes pgf lang
_ | isOpt "table" opts -> concat . _ | isOpt "table" opts -> concat . -- intersperse [[]] .
map (map (\(p,v) -> p+++":"+++v)) . tabularLinearizeAll concr map (map (\(p,v) -> p+++":"+++unl v)) . tabularLinearizes pgf lang
_ | isOpt "bracket" opts -> (:[]) . unwords . map showBracketedString . bracketedLinearize concr _ | isOpt "bracket" opts -> (:[]) . unwords . map showBracketedString . bracketedLinearize pgf lang
_ -> (:[]) . linearize concr _ -> (:[]) . unl . linearize pgf lang
-- replace each non-atomic constructor with mkC, where C is the val cat -- replace each non-atomic constructor with mkC, where C is the val cat
tree2mk pgf = showExpr [] . t2m where tree2mk pgf = showExpr [] . t2m where
t2m t = case unApp t of t2m t = case unApp t of
Just (cid,ts@(_:_)) -> mkApp (mk cid) (map t2m ts) Just (cid,ts@(_:_)) -> mkApp (mk cid) (map t2m ts)
_ -> t _ -> t
mk f = case functionType pgf f of mk = mkCId . ("mk" ++) . showCId . lookValCat (abstract pgf)
Just ty -> let (_,cat,_) = unType ty
in "mk" ++ cat unlex opts lang = stringOps Nothing (getUnlex opts lang ++ map prOpt opts) ----
Nothing -> f
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 [] = []
commaList ws = concat $ head ws : map (", " ++) (tail ws) 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" optLang = optLangFlag "lang"
optLangs = optLangsFlag "lang" optLangs = optLangsFlag "lang"
optLangFlag flag pgf opts = optLangsFlag f pgf opts = case valStrOpts f "" opts of
case optLangsFlag flag pgf opts of "" -> languages pgf
[] -> fail "no language specified" lang -> map (completeLang pgf) (chunks ',' lang)
(l:ls) -> return l 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 = optLangFlag f pgf opts = head $ optLangsFlag f pgf opts ++ [wildCId]
case valStrOpts flag "" opts of
"" -> Map.elems langs
str -> mapMaybe (completeLang pgf) (chunks ',' str)
where
langs = languages pgf
completeLang pgf la = optOpenTypes opts = case valStrOpts "openclass" "" opts of
mplus (Map.lookup la langs) "" -> []
(Map.lookup (abstractName pgf ++ la) langs) 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 optFile opts = valStrOpts "file" "_gftmp" opts
optType pgf opts = optType pgf opts =
let readOpt str = case readType str of let str = valStrOpts "cat" (showCId $ lookStartCat pgf) opts
in case readType str of
Just ty -> case checkType pgf ty of Just ty -> case checkType pgf ty of
Left err -> error err Left tcErr -> error $ render (ppTcError tcErr)
Right ty -> ty Right ty -> ty
Nothing -> error ("Can't parse '"++str++"' as a type") Nothing -> error ("Can't parse '"++str++"' as a type")
in maybeStrOpts "cat" (startCat pgf) readOpt opts
optViewFormat opts = valStrOpts "format" "png" opts optViewFormat opts = valStrOpts "format" "png" opts
optViewGraph opts = valStrOpts "view" "open" opts optViewGraph opts = valStrOpts "view" "open" opts
optNum opts = valIntOpts "number" 1 opts optNum opts = valIntOpts "number" 1 opts
optNumInf opts = valIntOpts "number" 1000000000 opts ---- 10^9 optNumInf opts = valIntOpts "number" 1000000000 opts ---- 10^9
takeOptNum opts = take (optNumInf opts) takeOptNum opts = take (optNumInf opts)
returnFromExprs show_p es = returnFromExprs es = return $ case es of
return $
case es of
[] -> pipeMessage "no trees found" [] -> pipeMessage "no trees found"
_ -> fromExprs show_p es _ -> fromExprs es
prGrammar pgf opts prGrammar (Env pgf mos) opts
| isOpt "pgf" opts = do | isOpt "pgf" opts = do
let outfile = valStrOpts "file" (abstractName pgf ++ ".pgf") opts let pgf1 = if isOpt "opt" opts then optimizePGF pgf else pgf
restricted $ writePGF outfile pgf let outfile = valStrOpts "file" (showCId (abstractName pgf) ++ ".pgf") opts
restricted $ encodeFile outfile pgf1
putStrLn $ "wrote file " ++ outfile putStrLn $ "wrote file " ++ outfile
return void return void
| isOpt "cats" opts = return $ fromString $ unwords $ categories pgf | isOpt "cats" opts = return $ fromString $ unwords $ map showCId $ categories pgf
| isOpt "funs" opts = return $ fromString $ unlines [showFun pgf f ty | f <- functions pgf, Just ty <- [functionType pgf f]] | isOpt "funs" opts = return $ fromString $ unlines $ map showFun $ funsigs pgf
| isOpt "fullform" opts = return $ fromString $ concatMap prFullFormLexicon $ optLangs pgf opts | isOpt "fullform" opts = return $ fromString $ concatMap (morpho mos "" prFullFormLexicon) $ optLangs pgf opts
| isOpt "langs" opts = return $ fromString $ unwords $ Map.keys $ languages pgf | isOpt "langs" opts = return $ fromString $ unwords $ map showCId $ languages pgf
| isOpt "lexc" opts = return $ fromString $ concatMap prLexcLexicon $ optLangs pgf opts | isOpt "lexc" opts = return $ fromString $ concatMap (morpho mos "" prLexcLexicon) $ optLangs pgf opts
| isOpt "missing" opts = return $ fromString $ unlines $ [unwords (concreteName concr:":":[f | f <- functions pgf, not (hasLinearization concr f)]) | | isOpt "missing" opts = return $ fromString $ unlines $ [unwords (showCId la:":": map showCId cs) |
concr <- optLangs pgf opts] la <- optLangs pgf opts, let cs = missingLins pgf la]
| isOpt "words" opts = return $ fromString $ concatMap prAllWords $ optLangs pgf opts | isOpt "words" opts = return $ fromString $ concatMap (morpho mos "" prAllWords) $ optLangs pgf opts
| otherwise = do fmt <- readOutputFormat (valStrOpts "printer" "pgf_pretty" opts) | otherwise = do fmt <- readOutputFormat (valStrOpts "printer" "pgf_pretty" opts)
return $ fromString $ concatMap snd $ exportPGF noOptions fmt pgf return $ fromString $ concatMap snd $ exportPGF noOptions fmt pgf
showFun pgf id ty = kwd++" "++ id ++ " : " ++ showType [] ty funsigs pgf = [(f,ty) | (f,(ty,_,_,_)) <- Map.assocs (funs (abstract pgf))]
where showFun (f,ty) = showCId f ++ " : " ++ showType [] ty ++ " ;"
kwd | functionIsDataCon pgf id = "data"
| otherwise = "fun"
morphos pgf opts s = morphos (Env pgf mos) opts s =
[(s,lookupMorpho concr s) | concr <- optLangs pgf opts] [(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 optClitics opts = case valStrOpts "clitics" "" opts of
"" -> [] "" -> []
@@ -817,28 +931,18 @@ pgfCommands = Map.fromList [
-- ps -f -g s returns g (f s) -- ps -f -g s returns g (f s)
treeOps pgf opts s = foldr app s (reverse opts) where treeOps pgf opts s = foldr app s (reverse opts) where
app (OOpt op) | Just (Left f) <- treeOp pgf op = f 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 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] treeOpOptions pgf = [(op,expl) | (op,(expl,Left _)) <- allTreeOps pgf]
treeOpFlags pgf = [(op,expl) | (op,(expl,Right _)) <- 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 translationQuiz mex pgf ig og typ = do
tts <- translationList mex pgf ig og typ infinity tts <- translationList mex pgf ig og typ infinity
mkQuiz "Welcome to GF Translation Quiz." tts 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 morphologyQuiz mex pgf ig typ = do
tts <- morphologyList mex pgf ig typ infinity tts <- morphologyList mex pgf ig typ infinity
mkQuiz "Welcome to GF Morphology Quiz." tts mkQuiz "Welcome to GF Morphology Quiz." tts
@@ -847,28 +951,30 @@ morphologyQuiz mex pgf ig typ = do
infinity :: Int infinity :: Int
infinity = 256 infinity = 256
prLexcLexicon :: Concr -> String prLexcLexicon :: Morpho -> String
prLexcLexicon concr = prLexcLexicon mo =
unlines $ "Multichar_Symbols":multichars:"":"LEXICON Root" : [prLexc l p ++ ":" ++ w ++ " # ;" | (w,lps) <- morpho, (l,p,_) <- lps] ++ ["END"] unlines $ "Multichar_Symbols":multichars:"":"LEXICON Root" : [prLexc l p ++ ":" ++ w ++ " # ;" | (w,lps) <- morpho, (l,p) <- lps] ++ ["END"]
where where
morpho = fullFormLexicon concr morpho = fullFormLexicon mo
prLexc l p = l ++ concat (mkTags (words p)) prLexc l p = showCId l ++ concat (mkTags (words p))
mkTags p = case p of mkTags p = case p of
"s":ws -> mkTags ws --- remove record field "s":ws -> mkTags ws --- remove record field
ws -> map ('+':) ws 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 :: Morpho -> String
prFullFormLexicon concr = prFullFormLexicon mo =
unlines (map prMorphoAnalysis (fullFormLexicon concr)) unlines (map prMorphoAnalysis (fullFormLexicon mo))
prAllWords :: Concr -> String prAllWords :: Morpho -> String
prAllWords concr = prAllWords mo =
unwords [w | (w,_) <- fullFormLexicon concr] unwords [w | (w,_) <- fullFormLexicon mo]
prMorphoAnalysis :: (String,[(Lemma,Analysis)]) -> String
prMorphoAnalysis (w,lps) = 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 :: String -> String -> String -> [String] -> SIO CommandOutput
viewGraphviz view format name grphs = do viewGraphviz view format name grphs = do

View File

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

View File

@@ -3,6 +3,7 @@
-- elsewhere -- elsewhere
module GF.Command.CommonCommands where module GF.Command.CommonCommands where
import Data.List(sort) import Data.List(sort)
import Data.Char (isSpace)
import GF.Command.CommandInfo import GF.Command.CommandInfo
import qualified Data.Map as Map import qualified Data.Map as Map
import GF.Infra.SIO import GF.Infra.SIO
@@ -16,7 +17,7 @@ import GF.Text.Transliterations
import GF.Text.Lexing(stringOp,opInEnv) import GF.Text.Lexing(stringOp,opInEnv)
import Data.Char (isSpace) import Data.Char (isSpace)
import PGF2(showExpr) import qualified PGF as H(showCId,showExpr,toATree,toTrie,Trie(..))
extend old new = Map.union (Map.fromList new) old -- Map.union is left-biased extend old new = Map.union (Map.fromList new) old -- Map.union is left-biased
@@ -102,7 +103,9 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
"To see transliteration tables, use command ut." "To see transliteration tables, use command ut."
], ],
examples = [ examples = [
-- mkEx "l (EAdd 3 4) | ps -code -- linearize code-like output",
mkEx "l (EAdd 3 4) | ps -unlexcode -- 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 "ps -lexcode | p -cat=Exp -- parse code-like input",
mkEx "gr -cat=QCl | l | ps -bind -- linearization output from LangFin", mkEx "gr -cat=QCl | l | ps -bind -- linearization output from LangFin",
mkEx "ps -to_devanagari \"A-p\" -- show Devanagari in UTF8 terminal", mkEx "ps -to_devanagari \"A-p\" -- show Devanagari in UTF8 terminal",
@@ -115,11 +118,13 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
let (os,fs) = optsAndFlags opts let (os,fs) = optsAndFlags opts
trans <- optTranslit opts trans <- optTranslit opts
if isOpt "lines" opts case opts of
then return $ fromStrings $ map (trans . stringOps (envFlag fs) (map prOpt os)) $ toStrings x _ | isOpt "lines" opts -> return $ fromStrings $ map (trans . stringOps (envFlag fs) (map prOpt os)) $ toStrings x
else return ((fromString . trans . stringOps (envFlag fs) (map prOpt os) . toString) 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 = [ 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, stringOpOptions,
flags = [ flags = [
@@ -175,6 +180,12 @@ commonCommands = fmap (mapCommandExec liftSIO) $ Map.fromList [
mkEx "gt | l | ? wc -- generate trees, linearize, and count words" 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 { ("ut", emptyCommandInfo {
longname = "unicode_table", longname = "unicode_table",
synopsis = "show a transliteration table for a unicode character set", synopsis = "show a transliteration table for a unicode character set",
@@ -222,6 +233,7 @@ envFlag fs =
_ -> Nothing _ -> Nothing
stringOpOptions = sort $ [ stringOpOptions = sort $ [
("bind","bind tokens separated by Prelude.BIND, i.e. &+"),
("chars","lexer that makes every non-space character a token"), ("chars","lexer that makes every non-space character a token"),
("from_cp1251","decode from cp1251 (Cyrillic used in Bulgarian resource)"), ("from_cp1251","decode from cp1251 (Cyrillic used in Bulgarian resource)"),
("from_utf8","decode from utf8 (default)"), ("from_utf8","decode from utf8 (default)"),
@@ -246,6 +258,27 @@ stringOpOptions = sort $ [
("to_" ++ p, "from GF " ++ n ++ " transliteration to unicode")] | ("to_" ++ p, "from GF " ++ n ++ " transliteration to unicode")] |
(p,n) <- transliterationPrintNames] (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 -- ** Converting command input
toString = unwords . toStrings toString = unwords . toStrings
toLines = unlines . toStrings toLines = unlines . toStrings
toParagraphs = map (unwords . words) . toParas
where
toParas ls = case break (all isSpace) ls of
([],[]) -> []
([],_:ll) -> toParas ll
(l, []) -> [unwords l]
(l, _:ll) -> unwords l : toParas ll

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
module GF.Command.Parse(readCommandLine, pCommand) where module GF.Command.Parse(readCommandLine, pCommand) where
import PGF2(pExpr,pIdent) import PGF(pExpr,pIdent)
import GF.Grammar.Parser(runPartial,pTerm) import GF.Grammar.Parser(runPartial,pTerm)
import GF.Command.Abstract import GF.Command.Abstract
@@ -22,7 +22,7 @@ pCommandLine =
pPipe = sepBy1 (skipSpaces >> pCommand) (skipSpaces >> char '|') pPipe = sepBy1 (skipSpaces >> pCommand) (skipSpaces >> char '|')
pCommand = (do pCommand = (do
cmd <- readS_to_P pIdent <++ (char '%' >> fmap ('%':) (readS_to_P pIdent)) cmd <- pIdent <++ (char '%' >> fmap ('%':) pIdent)
skipSpaces skipSpaces
opts <- sepBy pOption skipSpaces opts <- sepBy pOption skipSpaces
arg <- if getCommandOp cmd == "cc" then pArgTerm else pArgument arg <- if getCommandOp cmd == "cc" then pArgTerm else pArgument
@@ -37,7 +37,7 @@ pCommand = (do
pOption = do pOption = do
char '-' char '-'
flg <- readS_to_P pIdent flg <- pIdent
option (OOpt flg) (fmap (OFlag flg) (char '=' >> pValue)) option (OOpt flg) (fmap (OFlag flg) (char '=' >> pValue))
pValue = do pValue = do
@@ -52,9 +52,9 @@ pFilename = liftM2 (:) (satisfy isFileFirst) (munch (not . isSpace)) where
pArgument = pArgument =
option ANoArg option ANoArg
(fmap AExpr (readS_to_P pExpr) (fmap AExpr pExpr
<++ <++
(skipSpaces >> char '%' >> fmap AMacro (readS_to_P pIdent))) (skipSpaces >> char '%' >> fmap AMacro pIdent))
pArgTerm = ATerm `fmap` readS_to_P sTerm pArgTerm = ATerm `fmap` readS_to_P sTerm
where where

View File

@@ -1,17 +1,18 @@
module GF.Command.TreeOperations ( module GF.Command.TreeOperations (
treeOp, treeOp,
allTreeOps, allTreeOps,
treeChunks
) where ) where
import PGF2(Expr,PGF,Fun,compute,mkApp,unApp,unMeta,exprSize,exprFunctions) import PGF(Expr,PGF,CId,compute,mkApp,unApp,unapply,unMeta,exprSize,exprFunctions)
import Data.List import Data.List
type TreeOp = [Expr] -> [Expr] 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 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 = [ allTreeOps pgf = [
("compute",("compute by using semantic definitions (def)", ("compute",("compute by using semantic definitions (def)",
Left $ map (compute pgf))), Left $ map (compute pgf))),
@@ -33,6 +34,16 @@ largest = reverse . smallest
smallest :: [Expr] -> [Expr] smallest :: [Expr] -> [Expr]
smallest = sortBy (\t u -> compare (exprSize t) (exprSize u)) 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 :: Expr -> [Expr]
subtrees t = t : case unApp t of subtrees t = t : case unApp t of
Just (f,ts) -> concatMap subtrees ts Just (f,ts) -> concatMap subtrees ts

View File

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

View File

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

View File

@@ -21,8 +21,8 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module GF.Compile.CheckGrammar(checkModule) where 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.Ident
import GF.Infra.Option import GF.Infra.Option

View File

@@ -1,6 +1,6 @@
module GF.Compile.Compute.Value where module GF.Compile.Compute.Value where
import GF.Grammar.Grammar(Label,Type,MetaId,Patt,QIdent) import GF.Grammar.Grammar(Label,Type,MetaId,Patt,QIdent)
import PGF2(BindType) import PGF.Internal(BindType)
import GF.Infra.Ident(Ident) import GF.Infra.Ident(Ident)
import Text.Show.Functions() import Text.Show.Functions()
import Data.Ix(Ix) import Data.Ix(Ix)

View File

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

View File

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

View File

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

View File

@@ -13,9 +13,8 @@ module GF.Compile.GeneratePMCFG
(generatePMCFG, pgfCncCat, addPMCFG, resourceValues (generatePMCFG, pgfCncCat, addPMCFG, resourceValues
) where ) where
import qualified PGF2 as PGF2 --import PGF.CId
import qualified PGF2.Internal as PGF2 import PGF.Internal as PGF(CncCat(..),Symbol(..),fidVar)
import PGF2.Internal(Symbol(..),fidVar)
import GF.Infra.Option import GF.Infra.Option
import GF.Grammar hiding (Env, mkRecord, mkTable) import GF.Grammar hiding (Env, mkRecord, mkTable)
@@ -70,7 +69,7 @@ mapAccumWithKeyM f a m = do let xs = Map.toAscList m
--addPMCFG :: Options -> SourceGrammar -> GlobalEnv -> Maybe FilePath -> Ident -> Ident -> SeqSet -> Ident -> Info -> IOE (SeqSet, Info) --addPMCFG :: Options -> SourceGrammar -> GlobalEnv -> Maybe FilePath -> Ident -> Ident -> SeqSet -> Ident -> Info -> IOE (SeqSet, Info)
addPMCFG opts gr cenv opath am cm seqs id (CncFun mty@(Just (cat,cont,val)) mlin@(Just (L loc term)) mprn Nothing) = do 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++" ...") --when (verbAtLeast opts Verbose) $ ePutStr ("\n+ "++showIdent id++" ...")
let pres = protoFCat gr res val let pres = protoFCat gr res val
pargs = [protoFCat gr (snd $ catSkeleton ty) lincat | ((_,_,ty),(_,_,lincat)) <- zip ctxt cont] pargs = [protoFCat gr (snd $ catSkeleton ty) lincat | ((_,_,ty),(_,_,lincat)) <- zip ctxt cont]
@@ -94,7 +93,7 @@ addPMCFG opts gr cenv opath am cm seqs id (CncFun mty@(Just (cat,cont,val)) mlin
ePutStr ("\n+ "++showIdent id++" "++show (product (map catFactor pargs))) ePutStr ("\n+ "++showIdent id++" "++show (product (map catFactor pargs)))
seqs1 `seq` stats `seq` return () seqs1 `seq` stats `seq` return ()
when (verbAtLeast opts Verbose) $ ePutStr (" "++show stats) when (verbAtLeast opts Verbose) $ ePutStr (" "++show stats)
return (seqs1,CncFun mty mlin mprn (Just pmcfg)) return (seqs1,GF.Grammar.CncFun mty mlin mprn (Just pmcfg))
where where
(ctxt,res,_) = err bug typeForm (lookupFunType gr am id) (ctxt,res,_) = err bug typeForm (lookupFunType gr am id)
@@ -104,7 +103,7 @@ addPMCFG opts gr cenv opath am cm seqs id (CncFun mty@(Just (cat,cont,val)) mlin
newArgs = map getFIds newArgs' newArgs = map getFIds newArgs'
in addFunction env0 newCat fun newArgs in addFunction env0 newCat fun newArgs
addPMCFG opts gr cenv opath am cm seqs id (CncCat mty@(Just (L _ lincat)) addPMCFG opts gr cenv opath am cm seqs id (GF.Grammar.CncCat mty@(Just (L _ lincat))
mdef@(Just (L loc1 def)) mdef@(Just (L loc1 def))
mref@(Just (L loc2 ref)) mref@(Just (L loc2 ref))
mprn mprn
@@ -133,7 +132,7 @@ addPMCFG opts gr cenv opath am cm seqs id (CncCat mty@(Just (L _ lincat))
let pmcfg = getPMCFG pmcfgEnv2 let pmcfg = getPMCFG pmcfgEnv2
when (verbAtLeast opts Verbose) $ ePutStr ("\n+ "++showIdent id++" "++show (catFactor pcat)) when (verbAtLeast opts Verbose) $ ePutStr ("\n+ "++showIdent id++" "++show (catFactor pcat))
seqs2 `seq` pmcfg `seq` return (seqs2,CncCat mty mdef mref mprn (Just pmcfg)) seqs2 `seq` pmcfg `seq` return (seqs2,GF.Grammar.CncCat mty mdef mref mprn (Just pmcfg))
where where
addLindef lins (newCat', newArgs') env0 = addLindef lins (newCat', newArgs') env0 =
let [newCat] = getFIds newCat' let [newCat] = getFIds newCat'
@@ -159,15 +158,12 @@ convert opts gr cenv loc term ty@(_,val) pargs =
args = map Vr vars args = map Vr vars
vars = map (\(bt,x,t) -> x) context vars = map (\(bt,x,t) -> x) context
pgfCncCat :: SourceGrammar -> PGF2.Cat -> Type -> Int -> (PGF2.Cat,Int,Int,[String]) pgfCncCat :: SourceGrammar -> Type -> Int -> CncCat
pgfCncCat gr id lincat index = pgfCncCat gr lincat index =
let ((_,size),schema) = computeCatRange gr lincat let ((_,size),schema) = computeCatRange gr lincat
in ( id in PGF.CncCat index (index+size-1)
, index (mkArray (map (renderStyle style{mode=OneLineMode} . ppPath)
, index+size-1 (getStrPaths schema)))
, map (renderStyle style{mode=OneLineMode} . ppPath)
(getStrPaths schema)
)
where where
getStrPaths :: Schema Identity s c -> [Path] getStrPaths :: Schema Identity s c -> [Path]
getStrPaths = collect CNil [] getStrPaths = collect CNil []
@@ -479,7 +475,7 @@ goV (CPar t) rpath ss = restrictHead (reversePath rpath) t >> return ss
---------------------------------------------------------------------- ----------------------------------------------------------------------
-- SeqSet -- SeqSet
type SeqSet = Map.Map [Symbol] SeqId type SeqSet = Map.Map Sequence SeqId
addSequencesB :: SeqSet -> Branch (Value [Symbol]) -> (SeqSet, Branch (Value 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 addSequencesB seqs (Case nr path bs) = let !(seqs1,bs1) = mapAccumL' (\seqs (trm,b) -> let !(seqs',b') = addSequencesB seqs b
@@ -508,11 +504,13 @@ mapAccumL' f s (x:xs) = (s'',y:ys)
!(s'',ys) = mapAccumL' f s' xs !(s'',ys) = mapAccumL' f s' xs
addSequence :: SeqSet -> [Symbol] -> (SeqSet,SeqId) addSequence :: SeqSet -> [Symbol] -> (SeqSet,SeqId)
addSequence seqs seq = addSequence seqs lst =
case Map.lookup seq seqs of case Map.lookup seq seqs of
Just id -> (seqs,id) Just id -> (seqs,id)
Nothing -> let !last_seq = Map.size seqs Nothing -> let !last_seq = Map.size seqs
in (Map.insert seq last_seq seqs, last_seq) in (Map.insert seq last_seq seqs, last_seq)
where
seq = mkArray lst
------------------------------------------------------------ ------------------------------------------------------------

View File

@@ -50,13 +50,20 @@ getSourceModule opts file0 =
Right (i,mi0) -> Right (i,mi0) ->
do liftIO $ removeTemp tmp do liftIO $ removeTemp tmp
let mi =mi0 {mflags=mflags mi0 `addOptions` opts, msrc=file0} let mi =mi0 {mflags=mflags mi0 `addOptions` opts, msrc=file0}
case renameEncoding `fmap` flag optEncoding (mflags mi0) of optCoding' = renameEncoding `fmap` flag optEncoding (mflags mi0)
Just coding' -> 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') $ when (coding/=coding') $
raise $ "Encoding mismatch: "++coding++" /= "++coding' raise $ "Encoding mismatch: "++coding++" /= "++coding'
where coding = maybe defaultEncoding renameEncoding optCoding where coding = maybe defaultEncoding renameEncoding optCoding
_ -> return () _ -> return ()
return (i,mi) --liftIO $ transcodeModule' (i,mi) -- old lexer
return (i,mi) -- new lexer
getBNFCRules :: Options -> FilePath -> IOE [BNFCRule] getBNFCRules :: Options -> FilePath -> IOE [BNFCRule]
getBNFCRules opts fpath = do getBNFCRules opts fpath = do

View File

@@ -19,7 +19,7 @@ import GF.Compile.Compute.Predef(predef)
import GF.Compile.Compute.Value(Predefined(..)) import GF.Compile.Compute.Value(Predefined(..))
import GF.Infra.Ident(ModuleName(..),Ident,ident2raw,rawIdentS,showIdent,isWildIdent) import GF.Infra.Ident(ModuleName(..),Ident,ident2raw,rawIdentS,showIdent,isWildIdent)
import GF.Infra.Option(Options,optionsPGF) import GF.Infra.Option(Options,optionsPGF)
import PGF2.Internal(Literal(..)) import PGF.Internal(Literal(..))
import GF.Compile.Compute.Concrete(GlobalEnv,normalForm,resourceValues) import GF.Compile.Compute.Concrete(GlobalEnv,normalForm,resourceValues)
import GF.Grammar.Canonical as C import GF.Grammar.Canonical as C
import System.FilePath ((</>), (<.>)) import System.FilePath ((</>), (<.>))
@@ -92,7 +92,7 @@ concrete2canonical gr cenv absname cnc modinfo =
else let ((got,need),def) = paramType gr q else let ((got,need),def) = paramType gr q
in def++neededParamTypes (S.union got have) (S.toList need++qs) in def++neededParamTypes (S.union got have) (S.toList need++qs)
-- toCanonical :: G.Grammar -> ModuleName -> GlobalEnv -> (Ident, Info) -> [(S.Set QIdent, Either LincatDef LinDef)] toCanonical :: G.Grammar -> ModuleName -> GlobalEnv -> (Ident, Info) -> [(S.Set QIdent, Either LincatDef LinDef)]
toCanonical gr absname cenv (name,jment) = toCanonical gr absname cenv (name,jment) =
case jment of case jment of
CncCat (Just (L loc typ)) _ _ pprn _ -> CncCat (Just (L loc typ)) _ _ pprn _ ->

View File

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

View File

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

View File

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

View File

@@ -16,14 +16,13 @@
module GF.Compile.PGFtoHaskell (grammar2haskell) where module GF.Compile.PGFtoHaskell (grammar2haskell) where
import PGF2 import PGF(showCId)
import PGF2.Internal import PGF.Internal
import GF.Data.Operations import GF.Data.Operations
import GF.Infra.Option import GF.Infra.Option
import Data.List(isPrefixOf,find,intercalate,intersperse,groupBy,sortBy) import Data.List(isPrefixOf,find,intercalate,intersperse,groupBy,sortBy)
import Data.Maybe(mapMaybe)
import qualified Data.Map as Map import qualified Data.Map as Map
type Prefix = String -> String type Prefix = String -> String
@@ -259,7 +258,7 @@ fInstance gId lexical m (cat,rules) =
then " " ++ gId cat ++ " (fgs t) where\n fgs t = case unApp t of" then " " ++ gId cat ++ " (fgs t) where\n fgs t = case unApp t of"
else " case unApp t of") ++++ else " case unApp t of") ++++
unlines [mkInst f xx | (f,xx) <- nonLexicalRules (lexical cat) rules] ++++ 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)" " _ -> error (\"no" +++ cat ++ " \" ++ show t)"
where where
isList = isListCat (cat,rules) isList = isListCat (cat,rules)
@@ -281,21 +280,18 @@ fInstance gId lexical m (cat,rules) =
--type HSkeleton = [(OIdent, [(OIdent, [OIdent])])] --type HSkeleton = [(OIdent, [(OIdent, [OIdent])])]
hSkeleton :: PGF -> (String,HSkeleton) hSkeleton :: PGF -> (String,HSkeleton)
hSkeleton gr = hSkeleton gr =
(abstractName gr, (showCId (absname gr),
let fs = let fs =
[(c, [(f, cs) | (f, cs,_) <- fs]) | [(showCId c, [(showCId f, map showCId cs) | (f, (cs,_)) <- fs]) |
fs@((_, _,c):_) <- fns] fs@((_, (_,c)):_) <- fns]
in fs ++ [(c, []) | c <- cts, notElem c (["Int", "Float", "String"] ++ map fst fs)] in fs ++ [(sc, []) | c <- cts, let sc = showCId c, sc `notElem` (["Int", "Float", "String"] ++ map fst fs)]
) )
where where
cts = categories gr cts = Map.keys (cats (abstract gr))
fns = groupBy valtypg (sortBy valtyps (mapMaybe jty (functions gr))) fns = groupBy valtypg (sortBy valtyps (map jty (Map.assocs (funs (abstract gr)))))
valtyps (_,_,x) (_,_,y) = compare x y valtyps (_, (_,x)) (_, (_,y)) = compare x y
valtypg (_,_,x) (_,_,y) = x == y valtypg (_, (_,x)) (_, (_,y)) = x == y
jty f = case functionType gr f of jty (f,(ty,_,_,_)) = (f,catSkeleton ty)
Just ty -> let (hypos,valcat,_) = unType ty
in Just (f,[argcat | (_,_,ty) <- hypos, let (_,argcat,_) = unType ty],valcat)
Nothing -> Nothing
{- {-
updateSkeleton :: OIdent -> HSkeleton -> (OIdent, [OIdent]) -> HSkeleton updateSkeleton :: OIdent -> HSkeleton -> (OIdent, [OIdent]) -> HSkeleton
updateSkeleton cat skel rule = updateSkeleton cat skel rule =

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
module GF.Compile.PGFtoJava (grammar2java) where module GF.Compile.PGFtoJava (grammar2java) where
import PGF2 import PGF
import Data.Maybe(maybe) import Data.Maybe(maybe)
import Data.List(intercalate) import Data.List(intercalate)
import GF.Infra.Option import GF.Infra.Option
@@ -24,8 +24,9 @@ javaPreamble name =
] ]
javaMethod gr fun = 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 where
name = showCId fun
arity = maybe 0 getArrity (functionType gr fun) arity = maybe 0 getArrity (functionType gr fun)
vars = ['e':show i | i <- [1..arity]] vars = ['e':show i | i <- [1..arity]]

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
-- | Parallel grammar compilation -- | Parallel grammar compilation
module GF.CompileInParallel(parallelBatchCompile) where 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.Monad(join,ap,when,unless)
import Control.Applicative import Control.Applicative
import GF.Infra.Concurrency import GF.Infra.Concurrency
@@ -36,8 +36,11 @@ import qualified Control.Monad.Fail as Fail
parallelBatchCompile jobs opts rootfiles0 = parallelBatchCompile jobs opts rootfiles0 =
do setJobs jobs do setJobs jobs
rootfiles <- mapM canonical rootfiles0 rootfiles <- mapM canonical rootfiles0
lib_dir <- canonical =<< getLibraryDirectory opts lib_dirs1 <- getLibraryDirectory opts
filepaths <- mapM (getPathFromFile lib_dir opts) rootfiles 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 let groups = groupFiles lib_dir filepaths
n = length groups n = length groups
when (n>1) $ ePutStrLn "Grammar mixes present and alltenses, dividing modules into two groups" when (n>1) $ ePutStrLn "Grammar mixes present and alltenses, dividing modules into two groups"

View File

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

View File

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

View File

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

View File

@@ -10,9 +10,9 @@
module GF.Grammar.Binary(VersionTagged(..),decodeModuleHeader,decodeModule,encodeModule) where module GF.Grammar.Binary(VersionTagged(..),decodeModuleHeader,decodeModule,encodeModule) where
import Prelude hiding (catch) import Prelude hiding (catch)
import Control.Monad
import Control.Exception(catch,ErrorCall(..),throwIO) 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.Map as Map(empty)
import qualified Data.ByteString.Char8 as BS import qualified Data.ByteString.Char8 as BS
@@ -22,7 +22,8 @@ import GF.Infra.Option
import GF.Infra.UseIO(MonadIO(..)) import GF.Infra.UseIO(MonadIO(..))
import GF.Grammar.Grammar import GF.Grammar.Grammar
import PGF2.Internal(Literal(..),Symbol(..)) import PGF() -- Binary instances
import PGF.Internal(Literal(..))
-- Please change this every time when the GFO format is changed -- Please change this every time when the GFO format is changed
gfoVersion = "GF04" gfoVersion = "GF04"
@@ -297,53 +298,6 @@ instance Binary Label where
1 -> fmap LVar get 1 -> fmap LVar get
_ -> decodingError _ -> 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 n l) = putWord8 0 >> put (n,l)
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 -> liftM2 SymCat 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 --putGFOVersion = mapM_ (putWord8 . fromIntegral . ord) gfoVersion
--getGFOVersion = replicateM (length gfoVersion) (fmap (chr . fromIntegral) getWord8) --getGFOVersion = replicateM (length gfoVersion) (fmap (chr . fromIntegral) getWord8)
--putGFOVersion = put gfoVersion --putGFOVersion = put gfoVersion

View File

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

View File

@@ -31,7 +31,7 @@ data TypeApp = TypeApp CatId [Type] deriving Show
data TypeBinding = TypeBinding VarId Type deriving Show data TypeBinding = TypeBinding VarId Type deriving Show
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- ** Concreate syntax -- ** Concrete syntax
-- | Concrete Syntax -- | Concrete Syntax
data Concrete = Concrete ModId ModId Flags [ParamDef] [LincatDef] [LinDef] data Concrete = Concrete ModId ModId Flags [ParamDef] [LincatDef] [LinDef]
@@ -105,7 +105,7 @@ data TableRow rhs = TableRow LinPattern rhs
newtype PredefId = PredefId Id deriving (Eq,Ord,Show) newtype PredefId = PredefId Id deriving (Eq,Ord,Show)
newtype LabelId = LabelId Id deriving (Eq,Ord,Show) newtype LabelId = LabelId Id deriving (Eq,Ord,Show)
data VarValueId = VarValueId QualId deriving (Eq,Ord,Show) newtype VarValueId = VarValueId QualId deriving (Eq,Ord,Show)
-- | Name of param type or param value -- | Name of param type or param value
newtype ParamId = ParamId QualId deriving (Eq,Ord,Show) newtype ParamId = ParamId QualId deriving (Eq,Ord,Show)
@@ -116,9 +116,9 @@ newtype ParamId = ParamId QualId deriving (Eq,Ord,Show)
newtype ModId = ModId Id deriving (Eq,Ord,Show) newtype ModId = ModId Id deriving (Eq,Ord,Show)
newtype CatId = CatId Id deriving (Eq,Ord,Show) newtype CatId = CatId Id deriving (Eq,Ord,Show)
newtype FunId = FunId Id deriving (Eq,Show) newtype FunId = FunId Id deriving (Eq,Ord,Show)
data VarId = Anonymous | VarId Id deriving Show data VarId = Anonymous | VarId Id deriving (Eq,Show)
newtype Flags = Flags [(FlagName,FlagValue)] deriving Show newtype Flags = Flags [(FlagName,FlagValue)] deriving Show
type FlagName = Id type FlagName = Id

View File

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

View File

@@ -64,7 +64,7 @@ module GF.Grammar.Grammar (
Location(..), L(..), unLoc, noLoc, ppLocation, ppL, Location(..), L(..), unLoc, noLoc, ppLocation, ppL,
-- ** PMCFG -- ** PMCFG
PMCFG(..), Production(..), FId, FunId, SeqId, LIndex PMCFG(..), Production(..), FId, FunId, SeqId, LIndex, Sequence
) where ) where
import GF.Infra.Ident import GF.Infra.Ident
@@ -73,8 +73,7 @@ import GF.Infra.Location
import GF.Data.Operations import GF.Data.Operations
import PGF2(BindType(..)) import PGF.Internal (FId, FunId, SeqId, LIndex, Sequence, BindType(..))
import PGF2.Internal(FId, FunId, SeqId, LIndex, Symbol)
import Data.Array.IArray(Array) import Data.Array.IArray(Array)
import Data.Array.Unboxed(UArray) import Data.Array.Unboxed(UArray)
@@ -100,7 +99,7 @@ data ModuleInfo = ModInfo {
mopens :: [OpenSpec], mopens :: [OpenSpec],
mexdeps :: [ModuleName], mexdeps :: [ModuleName],
msrc :: FilePath, msrc :: FilePath,
mseqs :: Maybe (Array SeqId [Symbol]), mseqs :: Maybe (Array SeqId Sequence),
jments :: Map.Map Ident Info jments :: Map.Map Ident Info
} }

View File

@@ -25,6 +25,7 @@ import GF.Compile.Update (buildAnyTree)
import Data.List(intersperse) import Data.List(intersperse)
import Data.Char(isAlphaNum) import Data.Char(isAlphaNum)
import qualified Data.Map as Map import qualified Data.Map as Map
import PGF(mkCId)
} }
@@ -624,7 +625,7 @@ ListCFRule
CFRule :: { [BNFCRule] } CFRule :: { [BNFCRule] }
CFRule 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; | Ident '::=' ListCFRHS ';' { let { cat = showIdent $1;
mkFun cat its = mkFun cat its =
@@ -637,7 +638,7 @@ CFRule
Terminal c -> filter isAlphaNum c; Terminal c -> filter isAlphaNum c;
NonTerminal (t,_) -> t 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]} | 'coercions' Ident Integer ';' { [BNFCCoercions (showIdent $2) $3]}
| 'terminator' NonEmpty Ident String ';' { [BNFCTerminator $2 (showIdent $3) $4] } | 'terminator' NonEmpty Ident String ';' { [BNFCTerminator $2 (showIdent $3) $4] }

View File

@@ -24,13 +24,13 @@ module GF.Grammar.Printer
) where ) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import PGF2 as PGF2
import PGF2.Internal as PGF2
import GF.Infra.Ident import GF.Infra.Ident
import GF.Infra.Option import GF.Infra.Option
import GF.Grammar.Values import GF.Grammar.Values
import GF.Grammar.Grammar import GF.Grammar.Grammar
import PGF.Internal (ppMeta, ppLit, ppFId, ppFunId, ppSeqId, ppSeq)
import GF.Text.Pretty import GF.Text.Pretty
import Data.Maybe (isNothing) import Data.Maybe (isNothing)
import Data.List (intersperse) import Data.List (intersperse)
@@ -362,40 +362,3 @@ getLet :: Term -> ([LocalDef], Term)
getLet (Let l e) = let (ls,e') = getLet e getLet (Let l e) = let (ls,e') = getLet e
in (l:ls,e') in (l:ls,e')
getLet e = ([],e) getLet e = ([],e)
ppFunId funid = pp 'F' <> pp funid
ppSeqId seqid = pp 'S' <> pp seqid
ppFId fid
| fid == PGF2.fidString = pp "CString"
| fid == PGF2.fidInt = pp "CInt"
| fid == PGF2.fidFloat = pp "CFloat"
| fid == PGF2.fidVar = pp "CVar"
| fid == PGF2.fidStart = pp "CStart"
| otherwise = pp 'C' <> pp fid
ppMeta :: Int -> Doc
ppMeta n
| n == 0 = pp '?'
| otherwise = pp '?' <> pp n
ppLit (PGF2.LStr s) = pp (show s)
ppLit (PGF2.LInt n) = pp n
ppLit (PGF2.LFlt d) = pp d
ppSeq (seqid,seq) =
ppSeqId seqid <+> pp ":=" <+> hsep (map ppSymbol seq)
ppSymbol (PGF2.SymCat d r) = pp '<' <> pp d <> pp ',' <> pp r <> pp '>'
ppSymbol (PGF2.SymLit d r) = pp '{' <> pp d <> pp ',' <> pp r <> pp '}'
ppSymbol (PGF2.SymVar d r) = pp '<' <> pp d <> pp ',' <> pp '$' <> pp r <> pp '>'
ppSymbol (PGF2.SymKS t) = doubleQuotes (pp t)
ppSymbol PGF2.SymNE = pp "nonExist"
ppSymbol PGF2.SymBIND = pp "BIND"
ppSymbol PGF2.SymSOFT_BIND = pp "SOFT_BIND"
ppSymbol PGF2.SymSOFT_SPACE= pp "SOFT_SPACE"
ppSymbol PGF2.SymCAPIT = pp "CAPIT"
ppSymbol PGF2.SymALL_CAPIT = pp "ALL_CAPIT"
ppSymbol (PGF2.SymKP syms alts) = pp "pre" <+> braces (hsep (punctuate (pp ';') (hsep (map ppSymbol syms) : map ppAlt alts)))
ppAlt (syms,ps) = hsep (map ppSymbol syms) <+> pp '/' <+> hsep (map (doubleQuotes . pp) ps)

View File

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

View File

@@ -18,8 +18,8 @@ module GF.Infra.CheckM
checkIn, checkInModule, checkMap, checkMapRecover, checkIn, checkInModule, checkMap, checkMapRecover,
parallelCheck, accumulateError, commitCheck, parallelCheck, accumulateError, commitCheck,
) where ) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Data.Operations import GF.Data.Operations
--import GF.Infra.Ident --import GF.Infra.Ident
--import GF.Grammar.Grammar(msrc) -- ,Context --import GF.Grammar.Grammar(msrc) -- ,Context

View File

@@ -31,7 +31,7 @@ import qualified Data.ByteString.Char8 as BS(append,isPrefixOf)
-- Limit use of BS functions to the ones that work correctly on -- Limit use of BS functions to the ones that work correctly on
-- UTF-8-encoded bytestrings! -- UTF-8-encoded bytestrings!
import Data.Char(isDigit) import Data.Char(isDigit)
import Data.Binary(Binary(..)) import PGF.Internal(Binary(..))
import GF.Text.Pretty import GF.Text.Pretty

View File

@@ -1,6 +1,6 @@
-- | Source locations -- | Source locations
module GF.Infra.Location where module GF.Infra.Location where
import Prelude hiding ((<>)) import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import GF.Text.Pretty import GF.Text.Pretty
-- ** Source locations -- ** Source locations

View File

@@ -34,14 +34,16 @@ import Data.Maybe
import GF.Infra.Ident import GF.Infra.Ident
import GF.Infra.GetOpt import GF.Infra.GetOpt
import GF.Grammar.Predef import GF.Grammar.Predef
--import System.Console.GetOpt
import System.FilePath import System.FilePath
import PGF2.Internal(Literal(..)) --import System.IO
import GF.Data.Operations(Err,ErrorMonad(..),liftErr) import GF.Data.Operations(Err,ErrorMonad(..),liftErr)
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import PGF.Internal(Literal(..))
import qualified Control.Monad.Fail as Fail import qualified Control.Monad.Fail as Fail
usageHeader :: String usageHeader :: String
@@ -74,6 +76,7 @@ errors = raise . unlines
data Mode = ModeVersion | ModeHelp data Mode = ModeVersion | ModeHelp
| ModeInteractive | ModeRun | ModeInteractive | ModeRun
| ModeInteractive2 | ModeRun2
| ModeCompiler | ModeCompiler
| ModeServer {-port::-}Int | ModeServer {-port::-}Int
deriving (Show,Eq,Ord) deriving (Show,Eq,Ord)
@@ -84,7 +87,8 @@ data Verbosity = Quiet | Normal | Verbose | Debug
data Phase = Preproc | Convert | Compile | Link data Phase = Preproc | Convert | Compile | Link
deriving (Show,Eq,Ord) deriving (Show,Eq,Ord)
data OutputFormat = FmtPGFPretty data OutputFormat = FmtLPGF
| FmtPGFPretty
| FmtCanonicalGF | FmtCanonicalGF
| FmtCanonicalJson | FmtCanonicalJson
| FmtJavaScript | FmtJavaScript
@@ -92,6 +96,7 @@ data OutputFormat = FmtPGFPretty
| FmtPython | FmtPython
| FmtHaskell | FmtHaskell
| FmtJava | FmtJava
| FmtProlog
| FmtBNF | FmtBNF
| FmtEBNF | FmtEBNF
| FmtRegular | FmtRegular
@@ -158,7 +163,7 @@ data Flags = Flags {
optLiteralCats :: Set Ident, optLiteralCats :: Set Ident,
optGFODir :: Maybe FilePath, optGFODir :: Maybe FilePath,
optOutputDir :: Maybe FilePath, optOutputDir :: Maybe FilePath,
optGFLibPath :: Maybe FilePath, optGFLibPath :: Maybe [FilePath],
optDocumentRoot :: Maybe FilePath, -- For --server mode optDocumentRoot :: Maybe FilePath, -- For --server mode
optRecomp :: Recomp, optRecomp :: Recomp,
optProbsFile :: Maybe FilePath, optProbsFile :: Maybe FilePath,
@@ -213,9 +218,10 @@ parseModuleOptions args = do
then return opts then return opts
else errors $ map ("Non-option among module options: " ++) nonopts else errors $ map ("Non-option among module options: " ++) nonopts
fixRelativeLibPaths curr_dir lib_dir (Options o) = Options (fixPathFlags . o) fixRelativeLibPaths curr_dir lib_dirs (Options o) = Options (fixPathFlags . o)
where where
fixPathFlags f@(Flags{optLibraryPath=path}) = f{optLibraryPath=concatMap (\dir -> [curr_dir </> dir, lib_dir </> dir]) path} fixPathFlags f@(Flags{optLibraryPath=path}) = f{optLibraryPath=concatMap (\dir -> [parent </> dir
| parent <- curr_dir : lib_dirs]) path}
-- Showing options -- Showing options
@@ -311,6 +317,8 @@ optDescr =
Option ['j'] ["jobs"] (OptArg jobs "N") "Compile N modules in parallel with -batch (default 1).", Option ['j'] ["jobs"] (OptArg jobs "N") "Compile N modules in parallel with -batch (default 1).",
Option [] ["interactive"] (NoArg (mode ModeInteractive)) "Run in interactive mode (default).", Option [] ["interactive"] (NoArg (mode ModeInteractive)) "Run in interactive mode (default).",
Option [] ["run"] (NoArg (mode ModeRun)) "Run in interactive mode, showing output only (no other messages).", Option [] ["run"] (NoArg (mode ModeRun)) "Run in interactive mode, showing output only (no other messages).",
Option [] ["cshell"] (NoArg (mode ModeInteractive2)) "Start the C run-time shell.",
Option [] ["crun"] (NoArg (mode ModeRun2)) "Start the C run-time shell, showing output only (no other messages).",
Option [] ["server"] (OptArg modeServer "port") $ Option [] ["server"] (OptArg modeServer "port") $
"Run in HTTP server mode on given port (default "++show defaultPort++").", "Run in HTTP server mode on given port (default "++show defaultPort++").",
Option [] ["document-root"] (ReqArg gfDocuRoot "DIR") Option [] ["document-root"] (ReqArg gfDocuRoot "DIR")
@@ -328,7 +336,7 @@ optDescr =
Option ['f'] ["output-format"] (ReqArg outFmt "FMT") Option ['f'] ["output-format"] (ReqArg outFmt "FMT")
(unlines ["Output format. FMT can be one of:", (unlines ["Output format. FMT can be one of:",
"Canonical GF grammar: canonical_gf, canonical_json, (and haskell with option --haskell=concrete)", "Canonical GF grammar: canonical_gf, canonical_json, (and haskell with option --haskell=concrete)",
"Multiple concrete: pgf (default), json, js, pgf_pretty, prolog, python, ...", -- gar, "Multiple concrete: pgf (default), lpgf, json, js, pgf_pretty, prolog, python, ...", -- gar,
"Single concrete only: bnf, ebnf, fa, gsl, jsgf, regexp, slf, srgs_xml, srgs_abnf, vxml, ....", -- cf, lbnf, "Single concrete only: bnf, ebnf, fa, gsl, jsgf, regexp, slf, srgs_xml, srgs_abnf, vxml, ....", -- cf, lbnf,
"Abstract only: haskell, ..."]), -- prolog_abs, "Abstract only: haskell, ..."]), -- prolog_abs,
Option [] ["sisr"] (ReqArg sisrFmt "FMT") Option [] ["sisr"] (ReqArg sisrFmt "FMT")
@@ -425,7 +433,7 @@ optDescr =
literalCat x = set $ \o -> o { optLiteralCats = foldr Set.insert (optLiteralCats o) ((map identS . splitBy (==',')) x) } literalCat x = set $ \o -> o { optLiteralCats = foldr Set.insert (optLiteralCats o) ((map identS . splitBy (==',')) x) }
lexicalCat x = set $ \o -> o { optLexicalCats = foldr Set.insert (optLexicalCats o) (splitBy (==',') x) } lexicalCat x = set $ \o -> o { optLexicalCats = foldr Set.insert (optLexicalCats o) (splitBy (==',') x) }
outDir x = set $ \o -> o { optOutputDir = Just x } outDir x = set $ \o -> o { optOutputDir = Just x }
gfLibPath x = set $ \o -> o { optGFLibPath = Just x } gfLibPath x = set $ \o -> o { optGFLibPath = Just $ splitInModuleSearchPath x }
gfDocuRoot x = set $ \o -> o { optDocumentRoot = Just x } gfDocuRoot x = set $ \o -> o { optDocumentRoot = Just x }
recomp x = set $ \o -> o { optRecomp = x } recomp x = set $ \o -> o { optRecomp = x }
probsFile x = set $ \o -> o { optProbsFile = Just x } probsFile x = set $ \o -> o { optProbsFile = Just x }
@@ -470,12 +478,16 @@ outputFormats = map fst outputFormatsExpl
outputFormatsExpl :: [((String,OutputFormat),String)] outputFormatsExpl :: [((String,OutputFormat),String)]
outputFormatsExpl = outputFormatsExpl =
[(("pgf_pretty", FmtPGFPretty),"human-readable pgf"), [(("lpgf", FmtLPGF),"Linearisation-only PGF"),
(("pgf_pretty", FmtPGFPretty),"Human-readable PGF"),
(("canonical_gf", FmtCanonicalGF),"Canonical GF source files"), (("canonical_gf", FmtCanonicalGF),"Canonical GF source files"),
(("canonical_json", FmtCanonicalJson),"Canonical JSON source files"), (("canonical_json", FmtCanonicalJson),"Canonical JSON source files"),
(("js", FmtJavaScript),"JavaScript (whole grammar)"),
(("json", FmtJSON),"JSON (whole grammar)"), (("json", FmtJSON),"JSON (whole grammar)"),
(("python", FmtPython),"Python (whole grammar)"),
(("haskell", FmtHaskell),"Haskell (abstract syntax)"), (("haskell", FmtHaskell),"Haskell (abstract syntax)"),
(("java", FmtJava),"Java (abstract syntax)"), (("java", FmtJava),"Java (abstract syntax)"),
(("prolog", FmtProlog),"Prolog (whole grammar)"),
(("bnf", FmtBNF),"BNF (context-free grammar)"), (("bnf", FmtBNF),"BNF (context-free grammar)"),
(("ebnf", FmtEBNF),"Extended BNF"), (("ebnf", FmtEBNF),"Extended BNF"),
(("regular", FmtRegular),"* regular grammar"), (("regular", FmtRegular),"* regular grammar"),

View File

@@ -12,6 +12,9 @@ module GF.Infra.SIO(
newStdGen,print,putStr,putStrLn, newStdGen,print,putStr,putStrLn,
-- ** Specific to GF -- ** Specific to GF
importGrammar,importSource, importGrammar,importSource,
#ifdef C_RUNTIME
readPGF2,
#endif
putStrLnFlush,runInterruptibly,lazySIO, putStrLnFlush,runInterruptibly,lazySIO,
-- * Restricted accesss to arbitrary (potentially unsafe) IO operations -- * Restricted accesss to arbitrary (potentially unsafe) IO operations
-- | If the environment variable GF_RESTRICTED is defined, these -- | If the environment variable GF_RESTRICTED is defined, these
@@ -36,6 +39,9 @@ import qualified System.Random as IO(newStdGen)
import qualified GF.Infra.UseIO as IO(getLibraryDirectory) import qualified GF.Infra.UseIO as IO(getLibraryDirectory)
import qualified GF.System.Signal as IO(runInterruptibly) import qualified GF.System.Signal as IO(runInterruptibly)
import qualified GF.Command.Importing as GF(importGrammar, importSource) import qualified GF.Command.Importing as GF(importGrammar, importSource)
#ifdef C_RUNTIME
import qualified PGF2
#endif
import qualified Control.Monad.Fail as Fail import qualified Control.Monad.Fail as Fail
-- * The SIO monad -- * The SIO monad
@@ -121,3 +127,7 @@ lazySIO = lift1 lazyIO
importGrammar pgf opts files = lift0 $ GF.importGrammar pgf opts files importGrammar pgf opts files = lift0 $ GF.importGrammar pgf opts files
importSource opts files = lift0 $ GF.importSource opts files importSource opts files = lift0 $ GF.importSource opts files
#ifdef C_RUNTIME
readPGF2 = lift0 . PGF2.readPGF
#endif

View File

@@ -38,6 +38,7 @@ import Control.Monad(when,liftM,foldM)
import Control.Monad.Trans(MonadIO(..)) import Control.Monad.Trans(MonadIO(..))
import Control.Monad.State(StateT,lift) import Control.Monad.State(StateT,lift)
import Control.Exception(evaluate) import Control.Exception(evaluate)
import Data.List (nub)
--putIfVerb :: MonadIO io => Options -> String -> io () --putIfVerb :: MonadIO io => Options -> String -> io ()
putIfVerb opts msg = when (verbAtLeast opts Verbose) $ putStrLnE msg putIfVerb opts msg = when (verbAtLeast opts Verbose) $ putStrLnE msg
@@ -51,28 +52,32 @@ type FullPath = String
gfLibraryPath = "GF_LIB_PATH" gfLibraryPath = "GF_LIB_PATH"
gfGrammarPathVar = "GF_GRAMMAR_PATH" gfGrammarPathVar = "GF_GRAMMAR_PATH"
getLibraryDirectory :: MonadIO io => Options -> io FilePath getLibraryDirectory :: MonadIO io => Options -> io [FilePath]
getLibraryDirectory opts = getLibraryDirectory opts =
case flag optGFLibPath opts of case flag optGFLibPath opts of
Just path -> return path Just path -> return path
Nothing -> liftIO $ catch (getEnv gfLibraryPath) Nothing -> liftM splitSearchPath $ liftIO (catch (getEnv gfLibraryPath)
(\ex -> fmap (</> "lib") getDataDir) (\ex -> fmap (</> "lib") getDataDir))
getGrammarPath :: MonadIO io => FilePath -> io [FilePath] getGrammarPath :: MonadIO io => [FilePath] -> io [FilePath]
getGrammarPath lib_dir = liftIO $ do getGrammarPath lib_dirs = liftIO $ do
catch (fmap splitSearchPath $ getEnv gfGrammarPathVar) catch (fmap splitSearchPath $ getEnv gfGrammarPathVar)
(\_ -> return [lib_dir </> "alltenses",lib_dir </> "prelude"]) -- e.g. GF_GRAMMAR_PATH (\_ -> return $ concat [[lib_dir </> "alltenses", lib_dir </> "prelude"]
| lib_dir <- lib_dirs ]) -- e.g. GF_GRAMMAR_PATH
-- | extends the search path with the -- | extends the search path with the
-- 'gfLibraryPath' and 'gfGrammarPathVar' -- 'gfLibraryPath' and 'gfGrammarPathVar'
-- environment variables. Returns only existing paths. -- environment variables. Returns only existing paths.
extendPathEnv :: MonadIO io => Options -> io [FilePath] extendPathEnv :: MonadIO io => Options -> io [FilePath]
extendPathEnv opts = liftIO $ do extendPathEnv opts = liftIO $ do
let opt_path = flag optLibraryPath opts -- e.g. paths given as options let opt_path = nub $ flag optLibraryPath opts -- e.g. paths given as options
lib_dir <- getLibraryDirectory opts -- e.g. GF_LIB_PATH lib_dirs <- getLibraryDirectory opts -- e.g. GF_LIB_PATH
grm_path <- getGrammarPath lib_dir -- e.g. GF_GRAMMAR_PATH grm_path <- getGrammarPath lib_dirs -- e.g. GF_GRAMMAR_PATH
let paths = opt_path ++ [lib_dir] ++ grm_path let paths = opt_path ++ lib_dirs ++ grm_path
ps <- liftM concat $ mapM allSubdirs paths when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: opt_path is "++ show opt_path)
when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: lib_dirs is "++ show lib_dirs)
when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: grm_path is "++ show grm_path)
ps <- liftM (nub . concat) $ mapM allSubdirs (nub paths)
mapM canonicalizePath ps mapM canonicalizePath ps
where where
allSubdirs :: FilePath -> IO [FilePath] allSubdirs :: FilePath -> IO [FilePath]
@@ -80,11 +85,15 @@ extendPathEnv opts = liftIO $ do
allSubdirs p = case last p of allSubdirs p = case last p of
'*' -> do let path = init p '*' -> do let path = init p
fs <- getSubdirs path fs <- getSubdirs path
return [path </> f | f <- fs] let starpaths = [path </> f | f <- fs]
when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: allSubdirs: * found "++show starpaths)
return starpaths
_ -> do exists <- doesDirectoryExist p _ -> do exists <- doesDirectoryExist p
if exists if exists
then return [p] then do
else do when (verbAtLeast opts Verbose) $ putStrLn ("ignore path "++p) when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: allSubdirs: found path "++show p)
return [p]
else do when (verbAtLeast opts Verbose) $ putStrLn ("extendPathEnv: allSubdirs: ignore path "++ show p)
return [] return []
getSubdirs :: FilePath -> IO [FilePath] getSubdirs :: FilePath -> IO [FilePath]

View File

@@ -5,7 +5,7 @@ module GF.Interactive (mainGFI,mainRunGFI,mainServerGFI) where
import Prelude hiding (putStrLn,print) import Prelude hiding (putStrLn,print)
import qualified Prelude as P(putStrLn) import qualified Prelude as P(putStrLn)
import GF.Command.Interpreter(CommandEnv(..),mkCommandEnv,interpretCommandLine) import GF.Command.Interpreter(CommandEnv(..),mkCommandEnv,interpretCommandLine)
import GF.Command.Commands(HasPGF(..),pgfCommands) import GF.Command.Commands(PGFEnv,HasPGFEnv(..),pgf,pgfEnv,pgfCommands)
import GF.Command.CommonCommands(commonCommands,extend) import GF.Command.CommonCommands(commonCommands,extend)
import GF.Command.SourceCommands import GF.Command.SourceCommands
import GF.Command.CommandInfo import GF.Command.CommandInfo
@@ -20,12 +20,15 @@ import GF.Infra.SIO
import GF.Infra.Option import GF.Infra.Option
import qualified System.Console.Haskeline as Haskeline import qualified System.Console.Haskeline as Haskeline
import PGF2 import PGF
import PGF.Internal(abstract,funs,lookStartCat,emptyPGF)
import Data.Char import Data.Char
import Data.List(isPrefixOf) import Data.List(isPrefixOf)
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Text.ParserCombinators.ReadP as RP import qualified Text.ParserCombinators.ReadP as RP
--import System.IO(utf8)
--import System.CPUTime(getCPUTime)
import System.Directory({-getCurrentDirectory,-}getAppUserDataDirectory) import System.Directory({-getCurrentDirectory,-}getAppUserDataDirectory)
import Control.Exception(SomeException,fromException,evaluate,try) import Control.Exception(SomeException,fromException,evaluate,try)
import Control.Monad.State hiding (void) import Control.Monad.State hiding (void)
@@ -35,6 +38,8 @@ import GF.Server(server)
#endif #endif
import GF.Command.Messages(welcome) import GF.Command.Messages(welcome)
-- Provides an orphan instance of MonadFail for StateT in ghc versions < 8
import Control.Monad.Trans.Instances ()
-- | Run the GF Shell in quiet mode (@gf -run@). -- | Run the GF Shell in quiet mode (@gf -run@).
mainRunGFI :: Options -> [FilePath] -> IO () mainRunGFI :: Options -> [FilePath] -> IO ()
@@ -275,18 +280,17 @@ importInEnv opts files =
if flag optRetainResource opts if flag optRetainResource opts
then do src <- lift $ importSource opts files then do src <- lift $ importSource opts files
pgf <- lift . lazySIO $ importPGF pgf0 -- duplicates some work, better to link src pgf <- lift . lazySIO $ importPGF pgf0 -- duplicates some work, better to link src
modify $ \ gfenv -> gfenv {retain=True, pgfenv = (src,pgf)} modify $ \ gfenv -> gfenv {retain=True, pgfenv = (src,pgfEnv pgf)}
else do pgf1 <- lift $ importPGF pgf0 else do pgf1 <- lift $ importPGF pgf0
modify $ \ gfenv->gfenv { retain=False, modify $ \ gfenv->gfenv { retain=False,
pgfenv = (emptyGrammar,pgf1) } pgfenv = (emptyGrammar,pgfEnv pgf1) }
where where
importPGF pgf0 = importPGF pgf0 =
do let opts' = addOptions (setOptimization OptCSE False) opts do let opts' = addOptions (setOptimization OptCSE False) opts
pgf1 <- importGrammar pgf0 opts' files pgf1 <- importGrammar pgf0 opts' files
if (verbAtLeast opts Normal) if (verbAtLeast opts Normal)
then case pgf1 of then putStrLnFlush $
Just pgf -> putStrLnFlush $ unwords $ "\nLanguages:" : Map.keys (languages pgf) unwords $ "\nLanguages:" : map showCId (languages pgf1)
Nothing -> return ()
else return () else return ()
return pgf1 return pgf1
@@ -297,12 +301,12 @@ tryGetLine = do
Right l -> return l Right l -> return l
prompt env prompt env
| retain env = "> " | retain env || abs == wildCId = "> "
| otherwise = case multigrammar env of | otherwise = showCId abs ++ "> "
Just pgf -> abstractName pgf ++ "> " where
Nothing -> "> " abs = abstractName (multigrammar env)
type CmdEnv = (Grammar,Maybe PGF) type CmdEnv = (Grammar,PGFEnv)
data GFEnv = GFEnv { data GFEnv = GFEnv {
startOpts :: Options, startOpts :: Options,
@@ -314,10 +318,10 @@ data GFEnv = GFEnv {
emptyGFEnv opts = GFEnv opts False emptyCmdEnv emptyCommandEnv [] emptyGFEnv opts = GFEnv opts False emptyCmdEnv emptyCommandEnv []
emptyCmdEnv = (emptyGrammar,Nothing) emptyCmdEnv = (emptyGrammar,pgfEnv emptyPGF)
emptyCommandEnv = mkCommandEnv allCommands emptyCommandEnv = mkCommandEnv allCommands
multigrammar = snd . pgfenv multigrammar = pgf . snd . pgfenv
allCommands = allCommands =
extend pgfCommands (helpCommand allCommands:moreCommands) extend pgfCommands (helpCommand allCommands:moreCommands)
@@ -325,35 +329,24 @@ allCommands =
`Map.union` commonCommands `Map.union` commonCommands
instance HasGrammar ShellM where getGrammar = gets (fst . pgfenv) instance HasGrammar ShellM where getGrammar = gets (fst . pgfenv)
instance HasPGF ShellM where getPGF = gets (snd . pgfenv) instance HasPGFEnv ShellM where getPGFEnv = gets (snd . pgfenv)
wordCompletion gfenv (left,right) = do wordCompletion gfenv (left,right) = do
case wc_type (reverse left) of case wc_type (reverse left) of
CmplCmd pref CmplCmd pref
-> ret (length pref) [Haskeline.simpleCompletion name | name <- Map.keys (commands cmdEnv), isPrefixOf pref name] -> ret (length pref) [Haskeline.simpleCompletion name | name <- Map.keys (commands cmdEnv), isPrefixOf pref name]
CmplStr (Just (Command _ opts _)) s0 CmplStr (Just (Command _ opts _)) s0
-> case multigrammar gfenv of -> do mb_state0 <- try (evaluate (initState pgf (optLang opts) (optType opts)))
Just pgf -> let langs = languages pgf case mb_state0 of
optLang opts = case valStrOpts "lang" "" opts of Right state0 -> let (rprefix,rs) = break isSpace (reverse s0)
"" -> case Map.minView langs of
Nothing -> Nothing
Just (concr,_) -> Just concr
lang -> mplus (Map.lookup lang langs)
(Map.lookup (abstractName pgf ++ lang) langs)
optType opts = let readOpt str = case readType str of
Just ty -> case checkType pgf ty of
Left _ -> Nothing
Right ty -> Just ty
Nothing -> Nothing
in maybeStrOpts "cat" (Just (startCat pgf)) readOpt opts
(rprefix,rs) = break isSpace (reverse s0)
s = reverse rs s = reverse rs
prefix = reverse rprefix prefix = reverse rprefix
in case (optLang opts, optType opts) of ws = words s
(Just lang,Just cat) -> let compls = [t | ParseOk res <- [complete lang cat s prefix], (t,_,_,_) <- res] in case loop state0 ws of
in ret (length prefix) (map Haskeline.simpleCompletion compls)
_ -> ret 0 []
Nothing -> ret 0 [] Nothing -> ret 0 []
Just state -> let compls = getCompletions state prefix
in ret (length prefix) (map (\x -> Haskeline.simpleCompletion x) (Map.keys compls))
Left (_ :: SomeException) -> ret 0 []
CmplOpt (Just (Command n _ _)) pref CmplOpt (Just (Command n _ _)) pref
-> case Map.lookup n (commands cmdEnv) of -> case Map.lookup n (commands cmdEnv) of
Just inf -> do let flg_compls = [Haskeline.Completion ('-':flg++"=") ('-':flg) False | (flg,_) <- flags inf, isPrefixOf pref flg] Just inf -> do let flg_compls = [Haskeline.Completion ('-':flg++"=") ('-':flg) False | (flg,_) <- flags inf, isPrefixOf pref flg]
@@ -364,15 +357,23 @@ wordCompletion gfenv (left,right) = do
CmplIdent (Just (Command "i" _ _)) _ -- HACK: file name completion for command i CmplIdent (Just (Command "i" _ _)) _ -- HACK: file name completion for command i
-> Haskeline.completeFilename (left,right) -> Haskeline.completeFilename (left,right)
CmplIdent _ pref CmplIdent _ pref
-> case multigrammar gfenv of -> do mb_abs <- try (evaluate (abstract pgf))
Just pgf -> ret (length pref) [Haskeline.simpleCompletion name | name <- functions pgf, isPrefixOf pref name] case mb_abs of
Nothing -> ret (length pref) [] Right abs -> ret (length pref) [Haskeline.simpleCompletion name | cid <- Map.keys (funs abs), let name = showCId cid, isPrefixOf pref name]
Left (_ :: SomeException) -> ret (length pref) []
_ -> ret 0 [] _ -> ret 0 []
where where
pgf = multigrammar gfenv
cmdEnv = commandenv gfenv cmdEnv = commandenv gfenv
optLang opts = valCIdOpts "lang" (head (languages pgf)) opts
optType opts =
let str = valStrOpts "cat" (showCId $ lookStartCat pgf) opts
in case readType str of
Just ty -> ty
Nothing -> error ("Can't parse '"++str++"' as type")
loop ps [] = Just ps loop ps [] = Just ps
loop ps (t:ts) = case error "nextState ps (simpleParseInput t)" of loop ps (t:ts) = case nextState ps (simpleParseInput t) of
Left es -> Nothing Left es -> Nothing
Right ps -> loop ps ts Right ps -> loop ps ts

View File

@@ -0,0 +1,443 @@
{-# LANGUAGE CPP, ScopedTypeVariables, TypeSynonymInstances, FlexibleInstances, FlexibleContexts #-}
-- | GF interactive mode (with the C run-time system)
module GF.Interactive2 (mainGFI,mainRunGFI{-,mainServerGFI-}) where
import Prelude hiding (putStrLn,print)
import qualified Prelude as P(putStrLn)
import GF.Command.Interpreter(CommandEnv(..),commands,mkCommandEnv,interpretCommandLine)
import GF.Command.Commands2(PGFEnv,HasPGFEnv(..),pgf,concs,pgfEnv,emptyPGFEnv,pgfCommands)
import GF.Command.CommonCommands
import GF.Command.CommandInfo
import GF.Command.Help(helpCommand)
import GF.Command.Abstract
import GF.Command.Parse(readCommandLine,pCommand)
import GF.Data.Operations (Err(..))
import GF.Data.Utilities(whenM,repeatM)
import GF.Infra.UseIO(ioErrorText,putStrLnE)
import GF.Infra.SIO
import GF.Infra.Option
import qualified System.Console.Haskeline as Haskeline
import qualified PGF2 as C
import qualified PGF as H
import Data.Char
import Data.List(isPrefixOf)
import qualified Data.Map as Map
import qualified Text.ParserCombinators.ReadP as RP
--import System.IO(utf8)
--import System.CPUTime(getCPUTime)
import System.Directory({-getCurrentDirectory,-}getAppUserDataDirectory)
import System.FilePath(takeExtensions)
import Control.Exception(SomeException,fromException,try)
--import Control.Monad
import Control.Monad.State hiding (void)
import qualified GF.System.Signal as IO(runInterruptibly)
{-
#ifdef SERVER_MODE
import GF.Server(server)
#endif
-}
import GF.Command.Messages(welcome)
-- | Run the GF Shell in quiet mode (@gf -run@).
mainRunGFI :: Options -> [FilePath] -> IO ()
mainRunGFI opts files = shell (beQuiet opts) files
beQuiet = addOptions (modifyFlags (\f -> f{optVerbosity=Quiet}))
-- | Run the interactive GF Shell
mainGFI :: Options -> [FilePath] -> IO ()
mainGFI opts files = do
P.putStrLn welcome
P.putStrLn "This shell uses the C run-time system. See help for available commands."
shell opts files
shell opts files = flip evalStateT (emptyGFEnv opts) $
do mapStateT runSIO $ importInEnv opts files
modify $ \ gfenv0 -> gfenv0 {history = [unwords ("i":files)]}
loop
{-
#ifdef SERVER_MODE
-- | Run the GF Server (@gf -server@).
-- The 'Int' argument is the port number for the HTTP service.
mainServerGFI opts0 port files =
server jobs port root (execute1 opts)
=<< runSIO (importInEnv (emptyGFEnv opts) opts files)
where
root = flag optDocumentRoot opts
opts = beQuiet opts0
jobs = join (flag optJobs opts)
#else
mainServerGFI opts port files =
error "GF has not been compiled with server mode support"
#endif
-}
-- | Read end execute commands until it is time to quit
loop :: StateT GFEnv IO ()
loop = repeatM readAndExecute1
-- | Read and execute one command, returning 'True' to continue execution,
-- | 'False' when it is time to quit
readAndExecute1 :: StateT GFEnv IO Bool
readAndExecute1 = mapStateT runSIO . execute1 =<< readCommand
-- | Read a command
readCommand :: StateT GFEnv IO String
readCommand =
do opts <- gets startOpts
case flag optMode opts of
ModeRun -> lift tryGetLine
_ -> lift . fetchCommand =<< get
timeIt act =
do t1 <- liftSIO $ getCPUTime
a <- act
t2 <- liftSIO $ getCPUTime
return (t2-t1,a)
-- | Optionally show how much CPU time was used to run an IO action
optionallyShowCPUTime :: (Monad m,MonadSIO m) => Options -> m a -> m a
optionallyShowCPUTime opts act
| not (verbAtLeast opts Normal) = act
| otherwise = do (dt,r) <- timeIt act
liftSIO $ putStrLnFlush $ show (dt `div` 1000000000) ++ " msec"
return r
type ShellM = StateT GFEnv SIO
-- | Execute a given command line, returning 'True' to continue execution,
-- | 'False' when it is time to quit
execute1 :: String -> ShellM Bool
execute1 s0 =
do modify $ \ gfenv0 -> gfenv0 {history = s0 : history gfenv0}
execute1' s0
-- | Execute a given command line, without adding it to the history
execute1' s0 =
do opts <- gets startOpts
interruptible $ optionallyShowCPUTime opts $
case pwords s0 of
-- cc, sd, so, ss and dg are now in GF.Commands.SourceCommands
-- special commands
"q" :_ -> quit
"!" :ws -> system_command ws
"eh":ws -> execute_history ws
"i" :ws -> do import_ ws; continue
-- other special commands, working on GFEnv
"dc":ws -> define_command ws
"dt":ws -> define_tree ws
-- ordinary commands
_ -> do env <- gets commandenv
interpretCommandLine env s0
continue
where
continue,stop :: ShellM Bool
continue = return True
stop = return False
interruptible :: ShellM Bool -> ShellM Bool
interruptible act =
do gfenv <- get
mapStateT (
either (\e -> printException e >> return (True,gfenv)) return
<=< runInterruptibly) act
-- Special commands:
quit = do opts <- gets startOpts
when (verbAtLeast opts Normal) $ putStrLnE "See you."
stop
system_command ws = do lift $ restrictedSystem $ unwords ws ; continue
{-"eh":w:_ -> do
cs <- readFile w >>= return . map words . lines
gfenv' <- foldM (flip (process False benv)) gfenv cs
loopNewCPU gfenv' -}
execute_history [w] =
do execute . lines =<< lift (restricted (readFile w))
continue
where
execute :: [String] -> ShellM ()
execute [] = return ()
execute (line:lines) = whenM (execute1' line) (execute lines)
execute_history _ =
do putStrLnE "eh command not parsed"
continue
define_command (f:ws) =
case readCommandLine (unwords ws) of
Just comm ->
do modify $
\ gfenv ->
let env = commandenv gfenv
in gfenv {
commandenv = env {
commandmacros = Map.insert f comm (commandmacros env)
}
}
continue
_ -> dc_not_parsed
define_command _ = dc_not_parsed
dc_not_parsed = putStrLnE "command definition not parsed" >> continue
define_tree (f:ws) =
case H.readExpr (unwords ws) of
Just exp ->
do modify $
\ gfenv ->
let env = commandenv gfenv
in gfenv { commandenv = env {
expmacros = Map.insert f exp (expmacros env) } }
continue
_ -> dt_not_parsed
define_tree _ = dt_not_parsed
dt_not_parsed = putStrLnE "value definition not parsed" >> continue
pwords s = case words s of
w:ws -> getCommandOp w :ws
ws -> ws
import_ args =
do case parseOptions args of
Ok (opts',files) -> do
opts <- gets startOpts
curr_dir <- lift getCurrentDirectory
lib_dir <- lift $ getLibraryDirectory (addOptions opts opts')
importInEnv (addOptions opts (fixRelativeLibPaths curr_dir lib_dir opts')) files
Bad err ->
do putStrLnE $ "Command parse error: " ++ err
-- | Commands that work on 'GFEnv'
moreCommands = [
("e", emptyCommandInfo {
longname = "empty",
synopsis = "empty the environment (except the command history)",
exec = \ _ _ ->
do modify $ \ gfenv -> (emptyGFEnv (startOpts gfenv))
{ history=history gfenv }
return void
}),
("ph", emptyCommandInfo {
longname = "print_history",
synopsis = "print command history",
explanation = unlines [
"Prints the commands issued during the GF session.",
"The result is readable by the eh command.",
"The result can be used as a script when starting GF."
],
examples = [
mkEx "ph | wf -file=foo.gfs -- save the history into a file"
],
exec = \ _ _ ->
fmap (fromString . unlines . reverse . drop 1 . history) get
}),
("r", emptyCommandInfo {
longname = "reload",
synopsis = "repeat the latest import command",
exec = \ _ _ ->
do gfenv0 <- get
let imports = [(s,ws) | s <- history gfenv0, ("i":ws) <- [pwords s]]
case imports of
(s,ws):_ -> do
putStrLnE $ "repeating latest import: " ++ s
import_ ws
_ -> do
putStrLnE $ "no import in history"
return void
})
]
printException e = maybe (print e) (putStrLn . ioErrorText) (fromException e)
fetchCommand :: GFEnv -> IO String
fetchCommand gfenv = do
path <- getAppUserDataDirectory "gf_history"
let settings =
Haskeline.Settings {
Haskeline.complete = wordCompletion gfenv,
Haskeline.historyFile = Just path,
Haskeline.autoAddHistory = True
}
res <- IO.runInterruptibly $ Haskeline.runInputT settings (Haskeline.getInputLine (prompt gfenv))
case res of
Left _ -> return ""
Right Nothing -> return "q"
Right (Just s) -> return s
importInEnv :: Options -> [FilePath] -> ShellM ()
importInEnv opts files =
case files of
_ | flag optRetainResource opts ->
putStrLnE "Flag -retain is not supported in this shell"
[file] | takeExtensions file == ".pgf" -> importPGF file
[] -> return ()
_ -> do putStrLnE "Can only import one .pgf file"
where
importPGF file =
do gfenv <- get
case multigrammar gfenv of
Just _ -> putStrLnE "Discarding previous grammar"
_ -> return ()
pgf1 <- lift $ readPGF2 file
let gfenv' = gfenv { pgfenv = pgfEnv pgf1 }
when (verbAtLeast opts Normal) $
let langs = Map.keys . concretes $ gfenv'
in putStrLnE . unwords $ "\nLanguages:":langs
put gfenv'
tryGetLine = do
res <- try getLine
case res of
Left (e :: SomeException) -> return "q"
Right l -> return l
prompt env = abs ++ "> "
where
abs = maybe "" C.abstractName (multigrammar env)
data GFEnv = GFEnv {
startOpts :: Options,
--grammar :: (), -- gfo grammar -retain
--retain :: (), -- grammar was imported with -retain flag
pgfenv :: PGFEnv,
commandenv :: CommandEnv ShellM,
history :: [String]
}
emptyGFEnv opts = GFEnv opts {-() ()-} emptyPGFEnv emptyCommandEnv []
emptyCommandEnv = mkCommandEnv allCommands
multigrammar = pgf . pgfenv
concretes = concs . pgfenv
allCommands =
extend pgfCommands (helpCommand allCommands:moreCommands)
`Map.union` commonCommands
instance HasPGFEnv ShellM where getPGFEnv = gets pgfenv
-- ** Completion
wordCompletion gfenv (left,right) = do
case wc_type (reverse left) of
CmplCmd pref
-> ret (length pref) [Haskeline.simpleCompletion name | name <- Map.keys (commands cmdEnv), isPrefixOf pref name]
{-
CmplStr (Just (Command _ opts _)) s0
-> do mb_state0 <- try (evaluate (H.initState pgf (optLang opts) (optType opts)))
case mb_state0 of
Right state0 -> let (rprefix,rs) = break isSpace (reverse s0)
s = reverse rs
prefix = reverse rprefix
ws = words s
in case loop state0 ws of
Nothing -> ret 0 []
Just state -> let compls = H.getCompletions state prefix
in ret (length prefix) (map (\x -> Haskeline.simpleCompletion x) (Map.keys compls))
Left (_ :: SomeException) -> ret 0 []
-}
CmplOpt (Just (Command n _ _)) pref
-> case Map.lookup n (commands cmdEnv) of
Just inf -> do let flg_compls = [Haskeline.Completion ('-':flg++"=") ('-':flg) False | (flg,_) <- flags inf, isPrefixOf pref flg]
opt_compls = [Haskeline.Completion ('-':opt) ('-':opt) True | (opt,_) <- options inf, isPrefixOf pref opt]
ret (length pref+1)
(flg_compls++opt_compls)
Nothing -> ret (length pref) []
CmplIdent (Just (Command "i" _ _)) _ -- HACK: file name completion for command i
-> Haskeline.completeFilename (left,right)
CmplIdent _ pref
-> case mb_pgf of
Just pgf -> ret (length pref)
[Haskeline.simpleCompletion name
| name <- C.functions pgf,
isPrefixOf pref name]
_ -> ret (length pref) []
_ -> ret 0 []
where
mb_pgf = multigrammar gfenv
cmdEnv = commandenv gfenv
{-
optLang opts = valStrOpts "lang" (head $ Map.keys (concretes cmdEnv)) opts
optType opts =
let str = valStrOpts "cat" (H.showCId $ H.lookStartCat pgf) opts
in case H.readType str of
Just ty -> ty
Nothing -> error ("Can't parse '"++str++"' as type")
loop ps [] = Just ps
loop ps (t:ts) = case H.nextState ps (H.simpleParseInput t) of
Left es -> Nothing
Right ps -> loop ps ts
-}
ret len xs = return (drop len left,xs)
data CompletionType
= CmplCmd Ident
| CmplStr (Maybe Command) String
| CmplOpt (Maybe Command) Ident
| CmplIdent (Maybe Command) Ident
deriving Show
wc_type :: String -> CompletionType
wc_type = cmd_name
where
cmd_name cs =
let cs1 = dropWhile isSpace cs
in go cs1 cs1
where
go x [] = CmplCmd x
go x (c:cs)
| isIdent c = go x cs
| otherwise = cmd x cs
cmd x [] = ret CmplIdent x "" 0
cmd _ ('|':cs) = cmd_name cs
cmd _ (';':cs) = cmd_name cs
cmd x ('"':cs) = str x cs cs
cmd x ('-':cs) = option x cs cs
cmd x (c :cs)
| isIdent c = ident x (c:cs) cs
| otherwise = cmd x cs
option x y [] = ret CmplOpt x y 1
option x y ('=':cs) = optValue x y cs
option x y (c :cs)
| isIdent c = option x y cs
| otherwise = cmd x cs
optValue x y ('"':cs) = str x y cs
optValue x y cs = cmd x cs
ident x y [] = ret CmplIdent x y 0
ident x y (c:cs)
| isIdent c = ident x y cs
| otherwise = cmd x cs
str x y [] = ret CmplStr x y 1
str x y ('\"':cs) = cmd x cs
str x y ('\\':c:cs) = str x y cs
str x y (c:cs) = str x y cs
ret f x y d = f cmd y
where
x1 = take (length x - length y - d) x
x2 = takeWhile (\c -> isIdent c || isSpace c || c == '-' || c == '=' || c == '"') x1
cmd = case [x | (x,cs) <- RP.readP_to_S pCommand x2, all isSpace cs] of
[x] -> Just x
_ -> Nothing
isIdent c = c == '_' || c == '\'' || isAlphaNum c

View File

@@ -2,7 +2,10 @@
{-# LANGUAGE CPP #-} {-# LANGUAGE CPP #-}
module GF.Main where module GF.Main where
import GF.Compiler import GF.Compiler
import GF.Interactive import qualified GF.Interactive as GFI1
#ifdef C_RUNTIME
import qualified GF.Interactive2 as GFI2
#endif
import GF.Data.ErrM import GF.Data.ErrM
import GF.Infra.Option import GF.Infra.Option
import GF.Infra.UseIO import GF.Infra.UseIO
@@ -45,7 +48,17 @@ mainOpts opts files =
case flag optMode opts of case flag optMode opts of
ModeVersion -> putStrLn $ "Grammatical Framework (GF) version " ++ showVersion version ++ "\n" ++ buildInfo ModeVersion -> putStrLn $ "Grammatical Framework (GF) version " ++ showVersion version ++ "\n" ++ buildInfo
ModeHelp -> putStrLn helpMessage ModeHelp -> putStrLn helpMessage
ModeServer port -> mainServerGFI opts port files ModeServer port -> GFI1.mainServerGFI opts port files
ModeCompiler -> mainGFC opts files ModeCompiler -> mainGFC opts files
ModeInteractive -> mainGFI opts files ModeInteractive -> GFI1.mainGFI opts files
ModeRun -> mainRunGFI opts files ModeRun -> GFI1.mainRunGFI opts files
#ifdef C_RUNTIME
ModeInteractive2 -> GFI2.mainGFI opts files
ModeRun2 -> GFI2.mainRunGFI opts files
#else
ModeInteractive2 -> noCruntime
ModeRun2 -> noCruntime
where
noCruntime = do ePutStrLn "GF configured without C run-time support"
exitFailure
#endif

View File

@@ -18,8 +18,13 @@ module GF.Quiz (
morphologyList morphologyList
) where ) where
import PGF2 import PGF
--import PGF.Linearize
import GF.Data.Operations import GF.Data.Operations
--import GF.Infra.UseIO
--import GF.Infra.Option
--import PGF.Probabilistic
import System.Random import System.Random
import Data.List (nub) import Data.List (nub)
@@ -33,7 +38,7 @@ mkQuiz msg tts = do
teachDialogue qas msg teachDialogue qas msg
translationList :: translationList ::
Maybe Expr -> PGF -> Concr -> Concr -> Type -> Int -> IO [(String,[String])] Maybe Expr -> PGF -> Language -> Language -> Type -> Int -> IO [(String,[String])]
translationList mex pgf ig og typ number = do translationList mex pgf ig og typ number = do
gen <- newStdGen gen <- newStdGen
let ts = take number $ case mex of let ts = take number $ case mex of
@@ -41,22 +46,19 @@ translationList mex pgf ig og typ number = do
Nothing -> generateRandom gen pgf typ Nothing -> generateRandom gen pgf typ
return $ map mkOne $ ts return $ map mkOne $ ts
where where
mkOne t = (norml (linearize ig t), mkOne t = (norml (linearize pgf ig t),
map norml (concatMap lins (homonyms t))) map norml (concatMap lins (homonyms t)))
homonyms t = homonyms = parse pgf ig typ . linearize pgf ig
case (parse ig typ . linearize ig) t of lins = nub . concatMap (map snd) . tabularLinearizes pgf og
ParseOk res -> map fst res
_ -> []
lins = nub . concatMap (map snd) . tabularLinearizeAll og
morphologyList :: morphologyList ::
Maybe Expr -> PGF -> Concr -> Type -> Int -> IO [(String,[String])] Maybe Expr -> PGF -> Language -> Type -> Int -> IO [(String,[String])]
morphologyList mex pgf ig typ number = do morphologyList mex pgf ig typ number = do
gen <- newStdGen gen <- newStdGen
let ts = take (max 1 number) $ case mex of let ts = take (max 1 number) $ case mex of
Just ex -> generateRandomFrom gen pgf ex Just ex -> generateRandomFrom gen pgf ex
Nothing -> generateRandom gen pgf typ Nothing -> generateRandom gen pgf typ
let ss = map (tabularLinearizeAll ig) ts let ss = map (tabularLinearizes pgf ig) ts
let size = length (head (head ss)) let size = length (head (head ss))
let forms = take number $ randomRs (0,size-1) gen let forms = take number $ randomRs (0,size-1) gen
return [(snd (head pws0) +++ fst (pws0 !! i), ws) | return [(snd (head pws0) +++ fst (pws0 !! i), ws) |

View File

@@ -3,6 +3,7 @@
module GF.Server(server) where module GF.Server(server) where
import Data.List(partition,stripPrefix,isInfixOf) import Data.List(partition,stripPrefix,isInfixOf)
import qualified Data.Map as M import qualified Data.Map as M
import Control.Applicative -- for GHC<7.10
import Control.Monad(when) import Control.Monad(when)
import Control.Monad.State(StateT(..),get,gets,put) import Control.Monad.State(StateT(..),get,gets,put)
import Control.Monad.Except(ExceptT(..),runExceptT) import Control.Monad.Except(ExceptT(..),runExceptT)
@@ -33,7 +34,7 @@ import Network.Shed.Httpd(initServer,Request(..),Response(..),noCache)
--import qualified Network.FastCGI as FCGI -- from hackage direct-fastcgi --import qualified Network.FastCGI as FCGI -- from hackage direct-fastcgi
import Network.CGI(handleErrors,liftIO) import Network.CGI(handleErrors,liftIO)
import CGIUtils(handleCGIErrors)--,outputJSONP,stderrToFile import CGIUtils(handleCGIErrors)--,outputJSONP,stderrToFile
import Text.JSON(encode,showJSON,makeObj) import Text.JSON(JSValue(..),Result(..),valFromObj,encode,decode,showJSON,makeObj)
--import System.IO.Silently(hCapture) --import System.IO.Silently(hCapture)
import System.Process(readProcessWithExitCode) import System.Process(readProcessWithExitCode)
import System.Exit(ExitCode(..)) import System.Exit(ExitCode(..))
@@ -42,6 +43,7 @@ import GF.Infra.UseIO(readBinaryFile,writeBinaryFile,ePutStrLn)
import GF.Infra.SIO(captureSIO) import GF.Infra.SIO(captureSIO)
import GF.Data.Utilities(apSnd,mapSnd) import GF.Data.Utilities(apSnd,mapSnd)
import qualified PGFService as PS import qualified PGFService as PS
import qualified ExampleService as ES
import Data.Version(showVersion) import Data.Version(showVersion)
import Paths_gf(getDataDir,version) import Paths_gf(getDataDir,version)
import GF.Infra.BuildInfo (buildInfo) import GF.Infra.BuildInfo (buildInfo)
@@ -169,6 +171,7 @@ handle logLn documentroot state0 cache execute1 stateVar
(_ ,_ ,".pgf") -> do --debug $ "PGF service: "++path (_ ,_ ,".pgf") -> do --debug $ "PGF service: "++path
wrapCGI $ PS.cgiMain' cache path wrapCGI $ PS.cgiMain' cache path
(dir,"grammars.cgi",_ ) -> grammarList dir (decoded qs) (dir,"grammars.cgi",_ ) -> grammarList dir (decoded qs)
(dir ,"exb.fcgi" ,_ ) -> wrapCGI $ ES.cgiMain' root dir (PS.pgfCache cache)
_ -> serveStaticFile rpath path _ -> serveStaticFile rpath path
where path = translatePath rpath where path = translatePath rpath
_ -> return $ resp400 upath _ -> return $ resp400 upath
@@ -177,7 +180,7 @@ handle logLn documentroot state0 cache execute1 stateVar
translatePath rpath = root</>rpath -- hmm, check for ".." translatePath rpath = root</>rpath -- hmm, check for ".."
versionInfo c = versionInfo (c1,c2) =
html200 . unlines $ html200 . unlines $
"<!DOCTYPE html>": "<!DOCTYPE html>":
"<meta name = \"viewport\" content = \"width = device-width\">": "<meta name = \"viewport\" content = \"width = device-width\">":
@@ -185,7 +188,8 @@ handle logLn documentroot state0 cache execute1 stateVar
"": "":
("<h2>"++hdr++"</h2>"): ("<h2>"++hdr++"</h2>"):
(zipWith (++) ("<p>":repeat "<br>") buildinfo)++ (zipWith (++) ("<p>":repeat "<br>") buildinfo)++
sh "Run-time system" c sh "Haskell run-time system" c1++
sh "C run-time system" c2
where where
hdr:buildinfo = lines gf_version hdr:buildinfo = lines gf_version
rel = makeRelative documentroot rel = makeRelative documentroot
@@ -280,13 +284,17 @@ handle logLn documentroot state0 cache execute1 stateVar
skip_empty = filter (not.null.snd) skip_empty = filter (not.null.snd)
jsonList = jsonList' return jsonList = jsonList' return
jsonListLong = jsonList' (mapM addTime) jsonListLong ext = jsonList' (mapM (addTime ext)) ext
jsonList' details ext = fmap (json200) (details =<< ls_ext "." ext) jsonList' details ext = fmap (json200) (details =<< ls_ext "." ext)
addTime path = addTime ext path =
do t <- getModificationTime path do t <- getModificationTime path
return $ makeObj ["path".=path,"time".=format t] if ext==".json"
then addComment (time t) <$> liftIO (try $ getComment path)
else return . makeObj $ time t
where where
addComment t = makeObj . either (const t) (\c->t++["comment".=c])
time t = ["path".=path,"time".=format t]
format = formatTime defaultTimeLocale rfc822DateFormat format = formatTime defaultTimeLocale rfc822DateFormat
rm path | takeExtension path `elem` ok_to_delete = rm path | takeExtension path `elem` ok_to_delete =
@@ -328,6 +336,11 @@ handle logLn documentroot state0 cache execute1 stateVar
do paths <- getDirectoryContents dir do paths <- getDirectoryContents dir
return [path | path<-paths, takeExtension path==ext] return [path | path<-paths, takeExtension path==ext]
getComment path =
do Ok (JSObject obj) <- decode <$> readFile path
Ok cmnt <- return (valFromObj "comment" obj)
return (cmnt::String)
-- * Dynamic content -- * Dynamic content
jsonresult cwd dir cmd (ecode,stdout,stderr) files = jsonresult cwd dir cmd (ecode,stdout,stderr) files =

View File

@@ -14,6 +14,7 @@ import qualified Data.Map as Map
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
import PGF.Internal
import GF.Data.Utilities import GF.Data.Utilities
import GF.Grammar.CFG import GF.Grammar.CFG
--import GF.Speech.PGFToCFG --import GF.Speech.PGFToCFG

View File

@@ -7,13 +7,15 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module GF.Speech.GSL (gslPrinter) where module GF.Speech.GSL (gslPrinter) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Prelude hiding ((<>)) --import GF.Data.Utilities
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.SRG import GF.Speech.SRG
import GF.Speech.RegExp import GF.Speech.RegExp
import GF.Infra.Option import GF.Infra.Option
import PGF2 --import GF.Infra.Ident
import PGF
import Data.Char (toUpper,toLower) import Data.Char (toUpper,toLower)
import Data.List (partition) import Data.List (partition)
@@ -22,7 +24,7 @@ import GF.Text.Pretty
width :: Int width :: Int
width = 75 width = 75
gslPrinter :: Options -> PGF -> Concr -> String gslPrinter :: Options -> PGF -> CId -> String
gslPrinter opts pgf cnc = renderStyle st $ prGSL $ makeNonLeftRecursiveSRG opts pgf cnc gslPrinter opts pgf cnc = renderStyle st $ prGSL $ makeNonLeftRecursiveSRG opts pgf cnc
where st = style { lineLength = width } where st = style { lineLength = width }

View File

@@ -11,14 +11,15 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module GF.Speech.JSGF (jsgfPrinter) where module GF.Speech.JSGF (jsgfPrinter) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Prelude hiding ((<>)) --import GF.Data.Utilities
import GF.Infra.Option import GF.Infra.Option
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.RegExp import GF.Speech.RegExp
import GF.Speech.SISR import GF.Speech.SISR
import GF.Speech.SRG import GF.Speech.SRG
import PGF2 import PGF
import Data.Char import Data.Char
import Data.List import Data.List
@@ -31,7 +32,7 @@ width = 75
jsgfPrinter :: Options jsgfPrinter :: Options
-> PGF -> PGF
-> Concr -> String -> CId -> String
jsgfPrinter opts pgf cnc = renderStyle st $ prJSGF sisr $ makeNonLeftRecursiveSRG opts pgf cnc jsgfPrinter opts pgf cnc = renderStyle st $ prJSGF sisr $ makeNonLeftRecursiveSRG opts pgf cnc
where st = style { lineLength = width } where st = style { lineLength = width }
sisr = flag optSISR opts sisr = flag optSISR opts

View File

@@ -6,54 +6,60 @@
---------------------------------------------------------------------- ----------------------------------------------------------------------
module GF.Speech.PGFToCFG (bnfPrinter, pgfToCFG) where module GF.Speech.PGFToCFG (bnfPrinter, pgfToCFG) where
import PGF2 import PGF(showCId)
import PGF2.Internal import PGF.Internal as PGF
--import GF.Infra.Ident
import GF.Grammar.CFG hiding (Symbol) import GF.Grammar.CFG hiding (Symbol)
import Data.Array.IArray as Array
--import Data.List
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import qualified Data.IntMap as IntMap import qualified Data.IntMap as IntMap
--import Data.Maybe
import Data.Set (Set) import Data.Set (Set)
import qualified Data.Set as Set import qualified Data.Set as Set
bnfPrinter :: PGF -> Concr -> String bnfPrinter :: PGF -> CId -> String
bnfPrinter = toBNF id bnfPrinter = toBNF id
toBNF :: (CFG -> CFG) -> PGF -> Concr -> String toBNF :: (CFG -> CFG) -> PGF -> CId -> String
toBNF f pgf cnc = prCFG $ f $ pgfToCFG pgf cnc toBNF f pgf cnc = prCFG $ f $ pgfToCFG pgf cnc
type Profile = [Int] type Profile = [Int]
pgfToCFG :: PGF -> Concr -> CFG pgfToCFG :: PGF
pgfToCFG pgf cnc = mkCFG start_cat extCats (startRules ++ concatMap ruleToCFRule rules) -> CId -- ^ Concrete syntax name
-> CFG
pgfToCFG pgf lang = mkCFG (showCId (lookStartCat pgf)) extCats (startRules ++ concatMap ruleToCFRule rules)
where where
(_,start_cat,_) = unType (startCat pgf) cnc = lookConcr pgf lang
rules :: [(FId,Production)] rules :: [(FId,Production)]
rules = [(fcat,prod) | fcat <- [0..concrTotalCats cnc], rules = [(fcat,prod) | (fcat,set) <- IntMap.toList (PGF.productions cnc)
prod <- concrProductions cnc fcat] , prod <- Set.toList set]
fcatCats :: Map FId Cat fcatCats :: Map FId Cat
fcatCats = Map.fromList [(fc, c ++ "_" ++ show i) fcatCats = Map.fromList [(fc, showCId c ++ "_" ++ show i)
| (c,s,e,lbls) <- concrCategories cnc, | (c,CncCat s e lbls) <- Map.toList (cnccats cnc),
(fc,i) <- zip [s..e] [1..]] (fc,i) <- zip (range (s,e)) [1..]]
fcatCat :: FId -> Cat fcatCat :: FId -> Cat
fcatCat c = Map.findWithDefault ("Unknown_" ++ show c) c fcatCats fcatCat c = Map.findWithDefault ("Unknown_" ++ show c) c fcatCats
fcatToCat :: FId -> Int -> Cat fcatToCat :: FId -> LIndex -> Cat
fcatToCat c l = fcatCat c ++ row fcatToCat c l = fcatCat c ++ row
where row = if catLinArity c == 1 then "" else "_" ++ show l where row = if catLinArity c == 1 then "" else "_" ++ show l
-- gets the number of fields in the lincat for the given category -- gets the number of fields in the lincat for the given category
catLinArity :: FId -> Int catLinArity :: FId -> Int
catLinArity c = maximum (1:[length rhs | ((_,rhs), _) <- topdownRules c]) catLinArity c = maximum (1:[rangeSize (bounds rhs) | (CncFun _ rhs, _) <- topdownRules c])
topdownRules cat = f cat [] topdownRules cat = f cat []
where where
f cat rules = foldr g rules (concrProductions cnc cat) f cat rules = maybe rules (Set.foldr g rules) (IntMap.lookup cat (productions cnc))
g (PApply funid args) rules = (concrFunction cnc funid,args) : rules g (PApply funid args) rules = (cncfuns cnc ! funid,args) : rules
g (PCoerce cat) rules = f cat rules g (PCoerce cat) rules = f cat rules
@@ -61,26 +67,26 @@ pgfToCFG pgf cnc = mkCFG start_cat extCats (startRules ++ concatMap ruleToCFRule
extCats = Set.fromList $ map ruleLhs startRules extCats = Set.fromList $ map ruleLhs startRules
startRules :: [CFRule] startRules :: [CFRule]
startRules = [Rule c [NonTerminal (fcatToCat fc r)] (CFRes 0) startRules = [Rule (showCId c) [NonTerminal (fcatToCat fc r)] (CFRes 0)
| (c,s,e,lbls) <- concrCategories cnc, | (c,CncCat s e lbls) <- Map.toList (cnccats cnc),
fc <- [s..e], not (isPredefFId fc), fc <- range (s,e), not (isPredefFId fc),
r <- [0..catLinArity fc-1]] r <- [0..catLinArity fc-1]]
ruleToCFRule :: (FId,Production) -> [CFRule] ruleToCFRule :: (FId,Production) -> [CFRule]
ruleToCFRule (c,PApply funid args) = ruleToCFRule (c,PApply funid args) =
[Rule (fcatToCat c l) (mkRhs row) (profilesToTerm [fixProfile row n | n <- [0..length args-1]]) [Rule (fcatToCat c l) (mkRhs row) (profilesToTerm [fixProfile row n | n <- [0..length args-1]])
| (l,seqid) <- zip [0..] rhs | (l,seqid) <- Array.assocs rhs
, let row = concrSequence cnc seqid , let row = sequences cnc ! seqid
, not (containsLiterals row)] , not (containsLiterals row)]
where where
(f, rhs) = concrFunction cnc funid CncFun f rhs = cncfuns cnc ! funid
mkRhs :: [Symbol] -> [CFSymbol] mkRhs :: Array DotPos Symbol -> [CFSymbol]
mkRhs = concatMap symbolToCFSymbol mkRhs = concatMap symbolToCFSymbol . Array.elems
containsLiterals :: [Symbol] -> Bool containsLiterals :: Array DotPos Symbol -> Bool
containsLiterals row = not (null ([n | SymLit n _ <- row] ++ containsLiterals row = not (null ([n | SymLit n _ <- Array.elems row] ++
[n | SymVar n _ <- row])) [n | SymVar n _ <- Array.elems row]))
symbolToCFSymbol :: Symbol -> [CFSymbol] symbolToCFSymbol :: Symbol -> [CFSymbol]
symbolToCFSymbol (SymCat n l) = [let PArg _ fid = args!!n in NonTerminal (fcatToCat fid l)] symbolToCFSymbol (SymCat n l) = [let PArg _ fid = args!!n in NonTerminal (fcatToCat fid l)]
@@ -96,10 +102,10 @@ pgfToCFG pgf cnc = mkCFG start_cat extCats (startRules ++ concatMap ruleToCFRule
symbolToCFSymbol SymALL_CAPIT = [Terminal "&|"] symbolToCFSymbol SymALL_CAPIT = [Terminal "&|"]
symbolToCFSymbol SymNE = [] symbolToCFSymbol SymNE = []
fixProfile :: [Symbol] -> Int -> Profile fixProfile :: Array DotPos Symbol -> Int -> Profile
fixProfile row i = [k | (k,j) <- nts, j == i] fixProfile row i = [k | (k,j) <- nts, j == i]
where where
nts = zip [0..] [j | nt <- row, j <- getPos nt] nts = zip [0..] [j | nt <- Array.elems row, j <- getPos nt]
getPos (SymCat j _) = [j] getPos (SymCat j _) = [j]
getPos (SymLit j _) = [j] getPos (SymLit j _) = [j]
@@ -107,10 +113,9 @@ pgfToCFG pgf cnc = mkCFG start_cat extCats (startRules ++ concatMap ruleToCFRule
profilesToTerm :: [Profile] -> CFTerm profilesToTerm :: [Profile] -> CFTerm
profilesToTerm ps = CFObj f (zipWith profileToTerm argTypes ps) profilesToTerm ps = CFObj f (zipWith profileToTerm argTypes ps)
where Just (hypos,_,_) = fmap unType (functionType pgf f) where (argTypes,_) = catSkeleton $ lookType (abstract pgf) f
argTypes = [cat | (_,_,ty) <- hypos, let (_,cat,_) = unType ty]
profileToTerm :: Fun -> Profile -> CFTerm profileToTerm :: CId -> Profile -> CFTerm
profileToTerm t [] = CFMeta t profileToTerm t [] = CFMeta t
profileToTerm _ xs = CFRes (last xs) -- FIXME: unify profileToTerm _ xs = CFRes (last xs) -- FIXME: unify
ruleToCFRule (c,PCoerce c') = ruleToCFRule (c,PCoerce c') =

View File

@@ -11,12 +11,12 @@ import GF.Grammar.CFG
import GF.Speech.CFGToFA import GF.Speech.CFGToFA
import GF.Speech.PGFToCFG import GF.Speech.PGFToCFG
import GF.Speech.RegExp import GF.Speech.RegExp
import PGF2 import PGF
regexpPrinter :: PGF -> Concr -> String regexpPrinter :: PGF -> CId -> String
regexpPrinter pgf cnc = (++"\n") $ prRE id $ dfa2re $ cfgToFA $ pgfToCFG pgf cnc regexpPrinter pgf cnc = (++"\n") $ prRE id $ dfa2re $ cfgToFA $ pgfToCFG pgf cnc
multiRegexpPrinter :: PGF -> Concr -> String multiRegexpPrinter :: PGF -> CId -> String
multiRegexpPrinter pgf cnc = prREs $ mfa2res $ cfgToMFA $ pgfToCFG pgf cnc multiRegexpPrinter pgf cnc = prREs $ mfa2res $ cfgToMFA $ pgfToCFG pgf cnc
prREs :: [(String,RE CFSymbol)] -> String prREs :: [(String,RE CFSymbol)] -> String

View File

@@ -10,9 +10,13 @@ module GF.Speech.SISR (SISRFormat(..), SISRTag, prSISR,
import Data.List import Data.List
--import GF.Data.Utilities
--import GF.Infra.Ident
import GF.Infra.Option (SISRFormat(..)) import GF.Infra.Option (SISRFormat(..))
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.SRG (SRGNT) import GF.Speech.SRG (SRGNT)
import PGF(showCId)
import qualified GF.JavaScript.AbsJS as JS import qualified GF.JavaScript.AbsJS as JS
import qualified GF.JavaScript.PrintJS as JS import qualified GF.JavaScript.PrintJS as JS
@@ -46,12 +50,12 @@ catSISR t (c,i) fmt
profileFinalSISR :: CFTerm -> SISRFormat -> SISRTag profileFinalSISR :: CFTerm -> SISRFormat -> SISRTag
profileFinalSISR term fmt = [JS.DExpr $ fmtOut fmt `ass` f term] profileFinalSISR term fmt = [JS.DExpr $ fmtOut fmt `ass` f term]
where where
f (CFObj n ts) = tree n (map f ts) f (CFObj n ts) = tree (showCId n) (map f ts)
f (CFAbs v x) = JS.EFun [var v] [JS.SReturn (f x)] f (CFAbs v x) = JS.EFun [var v] [JS.SReturn (f x)]
f (CFApp x y) = JS.ECall (f x) [f y] f (CFApp x y) = JS.ECall (f x) [f y]
f (CFRes i) = JS.EIndex (JS.EVar args) (JS.EInt (fromIntegral i)) f (CFRes i) = JS.EIndex (JS.EVar args) (JS.EInt (fromIntegral i))
f (CFVar v) = JS.EVar (var v) f (CFVar v) = JS.EVar (var v)
f (CFMeta typ) = obj [("name",JS.EStr "?"), ("type",JS.EStr typ)] f (CFMeta typ) = obj [("name",JS.EStr "?"), ("type",JS.EStr (showCId typ))]
fmtOut SISR_WD20030401 = JS.EVar (JS.Ident "$") fmtOut SISR_WD20030401 = JS.EVar (JS.Ident "$")
fmtOut SISR_1_0 = JS.EVar (JS.Ident "out") fmtOut SISR_1_0 = JS.EVar (JS.Ident "out")

View File

@@ -16,14 +16,17 @@ module GF.Speech.SLF (slfPrinter,slfGraphvizPrinter,
import GF.Data.Utilities import GF.Data.Utilities
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.FiniteState import GF.Speech.FiniteState
--import GF.Speech.CFG
import GF.Speech.CFGToFA import GF.Speech.CFGToFA
import GF.Speech.PGFToCFG import GF.Speech.PGFToCFG
import qualified GF.Data.Graphviz as Dot import qualified GF.Data.Graphviz as Dot
import PGF2 import PGF
--import PGF.CId
import Control.Monad import Control.Monad
import qualified Control.Monad.State as STM import qualified Control.Monad.State as STM
import Data.Char (toUpper) import Data.Char (toUpper)
--import Data.List
import Data.Maybe import Data.Maybe
data SLFs = SLFs [(String,SLF)] SLF data SLFs = SLFs [(String,SLF)] SLF
@@ -40,7 +43,7 @@ data SLFEdge = SLFEdge { eId :: Int, eStart :: Int, eEnd :: Int }
type SLF_FA = FA State (Maybe CFSymbol) () type SLF_FA = FA State (Maybe CFSymbol) ()
mkFAs :: PGF -> Concr -> (SLF_FA, [(String,SLF_FA)]) mkFAs :: PGF -> CId -> (SLF_FA, [(String,SLF_FA)])
mkFAs pgf cnc = (slfStyleFA main, [(c,slfStyleFA n) | (c,n) <- subs]) mkFAs pgf cnc = (slfStyleFA main, [(c,slfStyleFA n) | (c,n) <- subs])
where MFA start subs = {- renameSubs $ -} cfgToMFA $ pgfToCFG pgf cnc where MFA start subs = {- renameSubs $ -} cfgToMFA $ pgfToCFG pgf cnc
main = let (fa,s,f) = newFA_ in newTransition s f (NonTerminal start) fa main = let (fa,s,f) = newFA_ in newTransition s f (NonTerminal start) fa
@@ -61,7 +64,7 @@ renameSubs (MFA start subs) = MFA (newName start) subs'
-- * SLF graphviz printing (without sub-networks) -- * SLF graphviz printing (without sub-networks)
-- --
slfGraphvizPrinter :: PGF -> Concr -> String slfGraphvizPrinter :: PGF -> CId -> String
slfGraphvizPrinter pgf cnc slfGraphvizPrinter pgf cnc
= prFAGraphviz $ gvFA $ slfStyleFA $ cfgToFA' $ pgfToCFG pgf cnc = prFAGraphviz $ gvFA $ slfStyleFA $ cfgToFA' $ pgfToCFG pgf cnc
where where
@@ -71,7 +74,7 @@ slfGraphvizPrinter pgf cnc
-- * SLF graphviz printing (with sub-networks) -- * SLF graphviz printing (with sub-networks)
-- --
slfSubGraphvizPrinter :: PGF -> Concr -> String slfSubGraphvizPrinter :: PGF -> CId -> String
slfSubGraphvizPrinter pgf cnc = Dot.prGraphviz g slfSubGraphvizPrinter pgf cnc = Dot.prGraphviz g
where (main, subs) = mkFAs pgf cnc where (main, subs) = mkFAs pgf cnc
g = STM.evalState (liftM2 Dot.addSubGraphs ss m) [0..] g = STM.evalState (liftM2 Dot.addSubGraphs ss m) [0..]
@@ -97,7 +100,7 @@ gvSLFFA n fa =
-- * SLF printing (without sub-networks) -- * SLF printing (without sub-networks)
-- --
slfPrinter :: PGF -> Concr -> String slfPrinter :: PGF -> CId -> String
slfPrinter pgf cnc slfPrinter pgf cnc
= prSLF $ automatonToSLF mkSLFNode $ slfStyleFA $ cfgToFA' $ pgfToCFG pgf cnc = prSLF $ automatonToSLF mkSLFNode $ slfStyleFA $ cfgToFA' $ pgfToCFG pgf cnc
@@ -106,7 +109,7 @@ slfPrinter pgf cnc
-- --
-- | Make a network with subnetworks in SLF -- | Make a network with subnetworks in SLF
slfSubPrinter :: PGF -> Concr -> String slfSubPrinter :: PGF -> CId -> String
slfSubPrinter pgf cnc = prSLFs slfs slfSubPrinter pgf cnc = prSLFs slfs
where where
(main,subs) = mkFAs pgf cnc (main,subs) = mkFAs pgf cnc

View File

@@ -17,15 +17,21 @@ module GF.Speech.SRG (SRG(..), SRGRule(..), SRGAlt(..), SRGItem, SRGSymbol
, lookupFM_ , lookupFM_
) where ) where
import PGF2 --import GF.Data.Operations
import GF.Data.Utilities import GF.Data.Utilities
--import GF.Infra.Ident
import GF.Infra.Option import GF.Infra.Option
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.PGFToCFG import GF.Speech.PGFToCFG
--import GF.Data.Relation
--import GF.Speech.FiniteState
import GF.Speech.RegExp import GF.Speech.RegExp
import GF.Speech.CFGToFA import GF.Speech.CFGToFA
--import GF.Infra.Option
import PGF
import Data.List import Data.List
--import Data.Maybe (fromMaybe, maybeToList)
import Data.Map (Map) import Data.Map (Map)
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Set (Set) import Data.Set (Set)
@@ -56,16 +62,16 @@ type SRGSymbol = Symbol SRGNT Token
-- | An SRG non-terminal. Category name and its number in the profile. -- | An SRG non-terminal. Category name and its number in the profile.
type SRGNT = (Cat, Int) type SRGNT = (Cat, Int)
ebnfPrinter :: Options -> PGF -> Concr -> String ebnfPrinter :: Options -> PGF -> CId -> String
ebnfPrinter opts pgf cnc = prSRG opts $ makeSRG opts pgf cnc ebnfPrinter opts pgf cnc = prSRG opts $ makeSRG opts pgf cnc
-- | Create a compact filtered non-left-recursive SRG. -- | Create a compact filtered non-left-recursive SRG.
makeNonLeftRecursiveSRG :: Options -> PGF -> Concr -> SRG makeNonLeftRecursiveSRG :: Options -> PGF -> CId -> SRG
makeNonLeftRecursiveSRG opts = makeSRG opts' makeNonLeftRecursiveSRG opts = makeSRG opts'
where where
opts' = setDefaultCFGTransform opts CFGNoLR True opts' = setDefaultCFGTransform opts CFGNoLR True
makeSRG :: Options -> PGF -> Concr -> SRG makeSRG :: Options -> PGF -> CId -> SRG
makeSRG opts = mkSRG cfgToSRG preprocess makeSRG opts = mkSRG cfgToSRG preprocess
where where
cfgToSRG cfg = [cfRulesToSRGRule rs | (_,rs) <- allRulesGrouped cfg] cfgToSRG cfg = [cfRulesToSRGRule rs | (_,rs) <- allRulesGrouped cfg]
@@ -91,7 +97,7 @@ stats g = "Categories: " ++ show (countCats g)
-} -}
makeNonRecursiveSRG :: Options makeNonRecursiveSRG :: Options
-> PGF -> PGF
-> Concr -> CId -- ^ Concrete syntax name.
-> SRG -> SRG
makeNonRecursiveSRG opts = mkSRG cfgToSRG id makeNonRecursiveSRG opts = mkSRG cfgToSRG id
where where
@@ -99,17 +105,17 @@ makeNonRecursiveSRG opts = mkSRG cfgToSRG id
where where
MFA _ dfas = cfgToMFA cfg MFA _ dfas = cfgToMFA cfg
dfaToSRGItem = mapRE dummySRGNT . minimizeRE . dfa2re dfaToSRGItem = mapRE dummySRGNT . minimizeRE . dfa2re
dummyCFTerm = CFMeta "dummy" dummyCFTerm = CFMeta (mkCId "dummy")
dummySRGNT = mapSymbol (\c -> (c,0)) id dummySRGNT = mapSymbol (\c -> (c,0)) id
mkSRG :: (CFG -> [SRGRule]) -> (CFG -> CFG) -> PGF -> Concr -> SRG mkSRG :: (CFG -> [SRGRule]) -> (CFG -> CFG) -> PGF -> CId -> SRG
mkSRG mkRules preprocess pgf cnc = mkSRG mkRules preprocess pgf cnc =
SRG { srgName = concreteName cnc, SRG { srgName = showCId cnc,
srgStartCat = cfgStartCat cfg, srgStartCat = cfgStartCat cfg,
srgExternalCats = cfgExternalCats cfg, srgExternalCats = cfgExternalCats cfg,
srgLanguage = languageCode cnc, srgLanguage = languageCode pgf cnc,
srgRules = mkRules cfg } srgRules = mkRules cfg }
where cfg = renameCats (concreteName cnc) $ preprocess $ pgfToCFG pgf cnc where cfg = renameCats (showCId cnc) $ preprocess $ pgfToCFG pgf cnc
-- | Renames all external cats C to C_cat, and all internal cats C_X (where X is any string), -- | Renames all external cats C to C_cat, and all internal cats C_X (where X is any string),
-- to C_N where N is an integer. -- to C_N where N is an integer.

View File

@@ -18,27 +18,31 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module GF.Speech.SRGS_ABNF (srgsAbnfPrinter, srgsAbnfNonRecursivePrinter) where module GF.Speech.SRGS_ABNF (srgsAbnfPrinter, srgsAbnfNonRecursivePrinter) where
import Prelude hiding ((<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Prelude hiding ((<>)) --import GF.Data.Utilities
import GF.Infra.Option import GF.Infra.Option
import GF.Grammar.CFG import GF.Grammar.CFG
import GF.Speech.SISR as SISR import GF.Speech.SISR as SISR
import GF.Speech.SRG import GF.Speech.SRG
import GF.Speech.RegExp import GF.Speech.RegExp
import PGF2 (PGF,Concr) import PGF (PGF, CId)
--import Data.Char
import Data.List import Data.List
import Data.Maybe import Data.Maybe
import GF.Text.Pretty import GF.Text.Pretty
--import Debug.Trace
width :: Int width :: Int
width = 75 width = 75
srgsAbnfPrinter :: Options -> PGF -> Concr -> String srgsAbnfPrinter :: Options
-> PGF -> CId -> String
srgsAbnfPrinter opts pgf cnc = showDoc $ prABNF sisr $ makeNonLeftRecursiveSRG opts pgf cnc srgsAbnfPrinter opts pgf cnc = showDoc $ prABNF sisr $ makeNonLeftRecursiveSRG opts pgf cnc
where sisr = flag optSISR opts where sisr = flag optSISR opts
srgsAbnfNonRecursivePrinter :: Options -> PGF -> Concr -> String srgsAbnfNonRecursivePrinter :: Options -> PGF -> CId -> String
srgsAbnfNonRecursivePrinter opts pgf cnc = showDoc $ prABNF Nothing $ makeNonRecursiveSRG opts pgf cnc srgsAbnfNonRecursivePrinter opts pgf cnc = showDoc $ prABNF Nothing $ makeNonRecursiveSRG opts pgf cnc
showDoc = renderStyle (style { lineLength = width }) showDoc = renderStyle (style { lineLength = width })

View File

@@ -13,7 +13,7 @@ import GF.Grammar.CFG
import GF.Speech.RegExp import GF.Speech.RegExp
import GF.Speech.SISR as SISR import GF.Speech.SISR as SISR
import GF.Speech.SRG import GF.Speech.SRG
import PGF2 (PGF, Concr) import PGF (PGF, CId, Token)
--import Control.Monad --import Control.Monad
--import Data.Char (toUpper,toLower) --import Data.Char (toUpper,toLower)
@@ -22,11 +22,11 @@ import Data.Maybe
--import qualified Data.Map as Map --import qualified Data.Map as Map
srgsXmlPrinter :: Options srgsXmlPrinter :: Options
-> PGF -> Concr -> String -> PGF -> CId -> String
srgsXmlPrinter opts pgf cnc = prSrgsXml sisr $ makeNonLeftRecursiveSRG opts pgf cnc srgsXmlPrinter opts pgf cnc = prSrgsXml sisr $ makeNonLeftRecursiveSRG opts pgf cnc
where sisr = flag optSISR opts where sisr = flag optSISR opts
srgsXmlNonRecursivePrinter :: Options -> PGF -> Concr -> String srgsXmlNonRecursivePrinter :: Options -> PGF -> CId -> String
srgsXmlNonRecursivePrinter opts pgf cnc = prSrgsXml Nothing $ makeNonRecursiveSRG opts pgf cnc srgsXmlNonRecursivePrinter opts pgf cnc = prSrgsXml Nothing $ makeNonRecursiveSRG opts pgf cnc

View File

@@ -6,8 +6,15 @@
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
module GF.Speech.VoiceXML (grammar2vxml) where module GF.Speech.VoiceXML (grammar2vxml) where
--import GF.Data.Operations
--import GF.Data.Str (sstrV)
--import GF.Data.Utilities
import GF.Data.XML import GF.Data.XML
import PGF2 --import GF.Infra.Ident
import PGF
import PGF.Internal
--import Control.Monad (liftM)
import Data.List (intersperse) -- isPrefixOf, find import Data.List (intersperse) -- isPrefixOf, find
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.Maybe (fromMaybe) import Data.Maybe (fromMaybe)
@@ -15,45 +22,59 @@ import Data.Maybe (fromMaybe)
--import Debug.Trace --import Debug.Trace
-- | the main function -- | the main function
grammar2vxml :: PGF -> Concr -> String grammar2vxml :: PGF -> CId -> String
grammar2vxml pgf cnc = showsXMLDoc (skel2vxml name mb_language start skel qs) "" grammar2vxml pgf cnc = showsXMLDoc (skel2vxml name language start skel qs) ""
where skel = pgfSkeleton pgf where skel = pgfSkeleton pgf
name = concreteName cnc name = showCId cnc
qs = catQuestions cnc (map fst skel) qs = catQuestions pgf cnc (map fst skel)
mb_language = languageCode cnc language = languageCode pgf cnc
(_,start,_) = unType (startCat pgf) start = lookStartCat pgf
-- --
-- * VSkeleton: a simple description of the abstract syntax. -- * VSkeleton: a simple description of the abstract syntax.
-- --
type Skeleton = [(Cat, [(Fun, [Cat])])] type Skeleton = [(CId, [(CId, [CId])])]
pgfSkeleton :: PGF -> Skeleton pgfSkeleton :: PGF -> Skeleton
pgfSkeleton pgf = [(c,[(f,[cat | (_,_,ty) <- hypos, let (_,cat,_) = unType ty]) | f <- functionsByCat pgf c, Just (hypos,_,_) <- [fmap unType (functionType pgf f)]]) pgfSkeleton pgf = [(c,[(f,fst (catSkeleton (lookType (abstract pgf) f))) | (_,f) <- fs])
| c <- categories pgf] | (c,(_,fs,_)) <- Map.toList (cats (abstract pgf))]
-- --
-- * Questions to ask -- * Questions to ask
-- --
type CatQuestions = [(Cat,String)] type CatQuestions = [(CId,String)]
catQuestions :: Concr -> [Cat] -> CatQuestions catQuestions :: PGF -> CId -> [CId] -> CatQuestions
catQuestions cnc cats = [(c,catQuestion cnc c) | c <- cats] catQuestions pgf cnc cats = [(c,catQuestion pgf cnc c) | c <- cats]
catQuestion :: Concr -> Cat -> String catQuestion :: PGF -> CId -> CId -> String
catQuestion cnc cat = fromMaybe cat (printName cnc cat) catQuestion pgf cnc cat = showPrintName pgf cnc cat
getCatQuestion :: Cat -> CatQuestions -> String
{-
lin :: StateGrammar -> String -> Err String
lin gr fun = do
tree <- string2treeErr gr fun
let ls = map unt $ linTree2strings noMark g c tree
case ls of
[] -> fail $ "No linearization of " ++ fun
l:_ -> return l
where c = cncId gr
g = stateGrammarST gr
unt = formatAsText
-}
getCatQuestion :: CId -> CatQuestions -> String
getCatQuestion c qs = getCatQuestion c qs =
fromMaybe (error "No question for category " ++ c) (lookup c qs) fromMaybe (error "No question for category " ++ showCId c) (lookup c qs)
-- --
-- * Generate VoiceXML -- * Generate VoiceXML
-- --
skel2vxml :: String -> Maybe String -> Cat -> Skeleton -> CatQuestions -> XML skel2vxml :: String -> Maybe String -> CId -> Skeleton -> CatQuestions -> XML
skel2vxml name language start skel qs = skel2vxml name language start skel qs =
vxml language ([startForm] ++ concatMap (uncurry (catForms gr qs)) skel) vxml language ([startForm] ++ concatMap (uncurry (catForms gr qs)) skel)
where where
@@ -65,12 +86,12 @@ grammarURI :: String -> String
grammarURI name = name ++ ".grxml" grammarURI name = name ++ ".grxml"
catForms :: String -> CatQuestions -> Cat -> [(Fun, [Cat])] -> [XML] catForms :: String -> CatQuestions -> CId -> [(CId, [CId])] -> [XML]
catForms gr qs cat fs = catForms gr qs cat fs =
comments [cat ++ " category."] comments [showCId cat ++ " category."]
++ [cat2form gr qs cat fs] ++ [cat2form gr qs cat fs]
cat2form :: String -> CatQuestions -> Cat -> [(Fun, [Cat])] -> XML cat2form :: String -> CatQuestions -> CId -> [(CId, [CId])] -> XML
cat2form gr qs cat fs = cat2form gr qs cat fs =
form (catFormId cat) $ form (catFormId cat) $
[var "old" Nothing, [var "old" Nothing,
@@ -83,22 +104,22 @@ cat2form gr qs cat fs =
++ concatMap (uncurry (fun2sub gr cat)) fs ++ concatMap (uncurry (fun2sub gr cat)) fs
++ [block [return_ ["term"]{-]-}]] ++ [block [return_ ["term"]{-]-}]]
fun2sub :: String -> Cat -> Fun -> [Cat] -> [XML] fun2sub :: String -> CId -> CId -> [CId] -> [XML]
fun2sub gr cat fun args = fun2sub gr cat fun args =
comments [fun ++ " : (" comments [showCId fun ++ " : ("
++ concat (intersperse ", " args) ++ concat (intersperse ", " (map showCId args))
++ ") " ++ cat] ++ ss ++ ") " ++ showCId cat] ++ ss
where where
ss = zipWith mkSub [0..] args ss = zipWith mkSub [0..] args
mkSub n t = subdialog s [("src","#"++catFormId t), mkSub n t = subdialog s [("src","#"++catFormId t),
("cond","term.name == "++string fun)] ("cond","term.name == "++string (showCId fun))]
[param "old" v, [param "old" v,
filled [] [assign v (s++".term")]] filled [] [assign v (s++".term")]]
where s = fun ++ "_" ++ show n where s = showCId fun ++ "_" ++ show n
v = "term.args["++show n++"]" v = "term.args["++show n++"]"
catFormId :: Cat -> String catFormId :: CId -> String
catFormId c = c ++ "_cat" catFormId c = showCId c ++ "_cat"
-- --

View File

@@ -14,11 +14,11 @@
module GF.System.NoSignal where module GF.System.NoSignal where
import Control.Exception (SomeException,catch) import Control.Exception (Exception,catch)
import Prelude hiding (catch) import Prelude hiding (catch)
{-# NOINLINE runInterruptibly #-} {-# NOINLINE runInterruptibly #-}
runInterruptibly :: IO a -> IO (Either SomeException a) runInterruptibly :: IO a -> IO (Either Exception a)
--runInterruptibly = fmap Right --runInterruptibly = fmap Right
runInterruptibly a = runInterruptibly a =
p `catch` h p `catch` h

View File

@@ -15,6 +15,7 @@ stringOp good name = case name of
"lexgreek" -> Just $ appLexer lexAGreek "lexgreek" -> Just $ appLexer lexAGreek
"lexgreek2" -> Just $ appLexer lexAGreek2 "lexgreek2" -> Just $ appLexer lexAGreek2
"words" -> Just $ appLexer words "words" -> Just $ appLexer words
"bind" -> Just $ appUnlexer (unwords . bindTok)
"unchars" -> Just $ appUnlexer concat "unchars" -> Just $ appUnlexer concat
"unlextext" -> Just $ appUnlexer (unlexText . unquote . bindTok) "unlextext" -> Just $ appUnlexer (unlexText . unquote . bindTok)
"unlexcode" -> Just $ appUnlexer unlexCode "unlexcode" -> Just $ appUnlexer unlexCode

View File

@@ -39,6 +39,7 @@ allTransliterations = Map.fromList [
("amharic",transAmharic), ("amharic",transAmharic),
("ancientgreek", transAncientGreek), ("ancientgreek", transAncientGreek),
("arabic", transArabic), ("arabic", transArabic),
("arabic_unvocalized", transArabicUnvoc),
("devanagari", transDevanagari), ("devanagari", transDevanagari),
("greek", transGreek), ("greek", transGreek),
("hebrew", transHebrew), ("hebrew", transHebrew),
@@ -178,6 +179,13 @@ transArabic = mkTransliteration "Arabic" allTrans allCodes where
allCodes = [0x0621..0x062f] ++ [0x0630..0x063a] ++ allCodes = [0x0621..0x062f] ++ [0x0630..0x063a] ++
[0x0641..0x064f] ++ [0x0650..0x0657] ++ [0x0671,0x061f] [0x0641..0x064f] ++ [0x0650..0x0657] ++ [0x0671,0x061f]
transArabicUnvoc :: Transliteration
transArabicUnvoc = transArabic{
invisible_chars = ["a","u","i","v2","o","V+","V-","a:"],
printname = "unvocalized Arabic"
}
transPersian :: Transliteration transPersian :: Transliteration
transPersian = (mkTransliteration "Persian/Farsi" allTrans allCodes) transPersian = (mkTransliteration "Persian/Farsi" allTrans allCodes)
{invisible_chars = ["a","u","i"]} where {invisible_chars = ["a","u","i"]} where

View File

@@ -17,7 +17,7 @@ import GF.Grammar.Printer(ppParams,ppTerm,getAbs,TermPrintQual(..))
import GF.Grammar.Parser(runP,pModDef) import GF.Grammar.Parser(runP,pModDef)
import GF.Grammar.Lexer(Posn(..)) import GF.Grammar.Lexer(Posn(..))
import GF.Data.ErrM import GF.Data.ErrM
import PGF2.Internal(Literal(LStr)) import PGF.Internal(Literal(LStr))
import SimpleEditor.Syntax as S import SimpleEditor.Syntax as S
import SimpleEditor.JSON import SimpleEditor.JSON

View File

@@ -0,0 +1,553 @@
module ExampleDemo (Environ,initial,getNext, provideExample, testThis,mkFuncWithArg,searchGoodTree,isMeta)
where
import PGF
--import System.IO
import Data.List
--import Control.Monad
import qualified Data.Map as Map
--import qualified Data.IntMap as IntMap
import qualified Data.Set as Set
import Data.Maybe
--import System.Environment (getArgs)
import System.Random (RandomGen) --newStdGen
type MyType = CId -- name of the categories from the program
type ConcType = CId -- categories from the resource grammar, that we parse on
type MyFunc = CId -- functions that we need to implement
--type FuncWithArg = ((MyFunc, MyType), Expr) -- function with arguments
type InterInstr = [String] -- lincats that were generated but not written to the file
data FuncWithArg = FuncWithArg
{getName :: MyFunc, -- name of the function to generate
getType :: MyType, -- return type of the function
getTypeArgs :: [MyType] -- types of arguments
}
deriving (Show,Eq,Ord)
-- we assume that it's for English for the moment
type TypeMap = Map.Map MyType ConcType -- mapping found from a file
type ConcMap = Map.Map MyFunc Expr -- concrete expression after parsing
data Environ = Env {getTypeMap :: TypeMap, -- mapping between a category in the grammar and a concrete type from RGL
getConcMap :: ConcMap, -- concrete expression after parsing
getSigs :: Map.Map MyType [FuncWithArg], -- functions for which we have the concrete syntax already with args
getAll :: [FuncWithArg] -- all the functions with arguments
}
getNext :: Environ -> Environ -> ([MyFunc],[MyFunc])
getNext env example_env =
let sgs = getSigs env
allfuncs = getAll env
names = Set.fromList $ map getName $ concat $ Map.elems sgs
exampleable = filter (\x -> (isJust $ getNameExpr x env)
&&
(not $ Set.member x names) -- maybe drop this if you want to also rewrite from examples...
) $ map getName allfuncs
testeable = filter (\x -> (isJust $ getNameExpr x env )
&&
(Set.member x names)
) $ map getName allfuncs
in (exampleable,testeable)
provideExample :: RandomGen gen => gen -> Environ -> MyFunc -> PGF -> PGF -> Language -> Maybe (Expr,String)
provideExample gen env myfunc parsePGF pgfFile lang =
fmap giveExample $ getNameExpr myfunc env
where
giveExample e_ =
let newexpr = head $ generateRandomFromDepth gen pgfFile e_ (Just 5) -- change here with the new random generator
ty = getType $ head $ filter (\x -> getName x == myfunc) $ getAll env
embeddedExpr = maybe "" (\x -> ", as in: " ++ q (linearize pgfFile lang x)) (embedInStart (getAll env) (Map.fromList [(ty,e_)]))
lexpr = linearize pgfFile lang newexpr
q s = sq++s++sq
sq = "\""
in (newexpr,q lexpr ++ embeddedExpr)
-- question, you need the IO monad for the random generator, how to do otherwise ??
-- question can you make the expression bold/italic - somehow distinguishable from the rest ?
testThis :: Environ -> MyFunc -> PGF -> Language -> Maybe String
testThis env myfunc parsePGF lang =
fmap (linearize parsePGF lang . mapToResource env . llin env) $
getNameExpr myfunc env
-- we assume that even the functions linearized by the user will still be in getSigs along with their linearization
-- fill in the blancs of an expression that we want to linearize for testing purposes
---------------------------------------------------------------------------
llin :: Environ -> Expr -> Expr
llin env expr =
let
(id,args) = fromJust $ unApp expr
--cexpr = fromJust $ Map.lookup id (getConcMap env)
in
if any isMeta args
then let
sigs = concat $ Map.elems $ getSigs env
tys = findExprWhich sigs id
in replaceConcArg 1 tys expr env
else mkApp id $ map (llin env) args
-- argument of the meta variable to replace, list of arguments left, expression to replace, environment, current replace expression
replaceConcArg :: Int -> [MyType] -> Expr -> Environ -> Expr
replaceConcArg i [] expr env = expr
replaceConcArg i (t:ts) expr env = -- TO DO : insert randomness here !!
let ss = fromJust $ Map.lookup t $ getSigs env
args = filter (null . getTypeArgs) ss
finArg = if null args then let l = last ss in llin env (mkApp (getName l) [mkMeta j | j <- [1..(length $ getTypeArgs l)]])
else mkApp (getName $ last args) []
in
let newe = replaceOne i finArg expr
in replaceConcArg (i+1) ts newe env
-- replace a certain metavariable with a certain expression in another expression - return updated expression
replaceOne :: Int -> Expr -> Expr -> Expr
replaceOne i erep expr =
if isMeta expr && ((fromJust $ unMeta expr) == i)
then erep
else if isMeta expr then expr
else let (id,args) = fromJust $ unApp expr
in
mkApp id $ map (replaceOne i erep) args
findExprWhich :: [FuncWithArg] -> MyFunc -> [MyType]
findExprWhich lst f = getTypeArgs $ head $ filter (\x -> getName x == f) lst
mapToResource :: Environ -> Expr -> Expr
mapToResource env expr =
let (id,args) = maybe (error $ "tried to unwrap " ++ showExpr [] expr) (\x -> x) (unApp expr)
cmap = getConcMap env
cexp = maybe (error $ "didn't find " ++ showCId id ++ " in "++ show cmap) (\x -> x) (Map.lookup id cmap)
in
if null args then cexp
else let newargs = map (mapToResource env) args
in replaceAllArgs cexp 1 newargs
where
replaceAllArgs expr i [] = expr
replaceAllArgs expr i (x:xs) = replaceAllArgs (replaceOne i x expr) (i+1) xs
-----------------------------------------------
-- embed expression in another one from the start category
embedInStart :: [FuncWithArg] -> Map.Map MyType Expr -> Maybe Expr
embedInStart fss cs =
let currset = Map.toList cs
nextset = Map.fromList $ concat [ if elem myt (getTypeArgs farg)
then connectWithArg (myt,exp) farg else []
| (myt,exp) <- currset, farg <- fss]
nextmap = Map.union cs nextset
maybeExpr = Map.lookup startCateg nextset
in if isNothing maybeExpr then
if Map.size nextmap == Map.size cs then Nothing --error $ "could't build " ++ show startCateg ++ "with " ++ show fss
else embedInStart fss nextmap
else return $ fromJust maybeExpr
where
connectWithArg (myt,exp) farg =
let ind = head $ elemIndices myt (getTypeArgs farg)
in [(getType farg, mkApp (getName farg) $ [mkMeta i | i <- [1..ind]] ++ [exp] ++ [mkMeta i | i <- [(ind + 1)..((length $ getTypeArgs farg) - 1)]])]
-----------------------------------------------
{-
updateConcMap :: Environ -> MyFunc -> Expr -> Environ
updateConcMap env myf expr =
Env (getTypeMap env) (Map.insert myf expr (getConcMap env)) (getSigs env) (getAll env)
updateInterInstr :: Environ -> MyType -> FuncWithArg -> Environ
updateInterInstr env myt myf =
let ii = getSigs env
newInterInstr =
maybe (Map.insert myt [myf] ii) (\x -> Map.insert myt (myf:x) ii) $ Map.lookup myt ii
in Env (getTypeMap env) (getConcMap env) newInterInstr (getAll env)
putSignatures :: Environ -> [FuncWithArg] -> Environ
putSignatures env fss =
Env (getTypeMap env) (getConcMap env) (mkSigs fss) (getAll env)
updateEnv :: Environ -> FuncWithArg -> MyType -> Expr -> Environ
updateEnv env myf myt expr =
let ii = getSigs env
nn = getName myf
newInterInstr =
maybe (Map.insert myt [myf] ii) (\x -> Map.insert myt (myf:x) ii) $ Map.lookup myt ii
in Env (getTypeMap env) (Map.insert nn expr (getConcMap env)) newInterInstr (getAll env)
-}
mkSigs :: [FuncWithArg] -> Map.Map MyType [FuncWithArg]
mkSigs fss = Map.fromListWith (++) $ zip (map getType fss) (map (\x -> [x]) fss)
{------------------------------------
lang :: String
lang = "Eng"
parseLang :: Language
parseLang = fromJust $ readLanguage "ParseEng"
parsePGFfile :: String
parsePGFfile = "ParseEngAbs.pgf"
------------------------------------}
searchGoodTree :: Environ -> Expr -> [Expr] -> IO (Maybe (Expr,Expr))
searchGoodTree env expr [] = return Nothing
searchGoodTree env expr (e:es) =
do val <- debugReplaceArgs expr e env
maybe (searchGoodTree env expr es) (\x -> return $ Just (x,e)) val
getNameExpr :: MyFunc -> Environ -> Maybe Expr
getNameExpr myfunc env =
let allfunc = filter (\x -> getName x == myfunc) $ getAll env
in
if null allfunc then Nothing
else getExpr (head allfunc) env
-- find an expression to generate where we have all the other elements available
getExpr :: FuncWithArg -> Environ -> Maybe Expr
getExpr farg env =
let tys = getTypeArgs farg
ctx = getSigs env
lst = getConcTypes ctx tys 1
in if (all isJust lst) then Just $ mkApp (getName farg) (map fromJust lst)
else Nothing
where getConcTypes context [] i = []
getConcTypes context (ty:types) i =
let pos = Map.lookup ty context
in
if isNothing pos || (null $ fromJust pos) then [Nothing]
else
let mm = last $ fromJust pos
mmargs = getTypeArgs mm
newi = i + length mmargs - 1
lst = getConcTypes (Map.insert ty (init $ (fromJust pos)) context) types (newi+1)
in
if (all isJust lst) then -- i..newi
(Just $ mkApp (getName mm) [mkMeta j | j <- [1..(length mmargs)]]) : lst
else [Nothing]
-- only covers simple expressions with meta variables, not the rest...
isGeneralizationOf :: Expr -> Expr -> Bool
isGeneralizationOf genExpr testExpr =
if isMeta genExpr then True
else if isMeta testExpr then False
else let genUnwrap = unApp genExpr
testUnwrap = unApp testExpr
in if isNothing genUnwrap || isNothing testUnwrap then False -- see if you can generalize here
else let (gencid, genargs) = fromJust genUnwrap
(testcid, testargs) = fromJust testUnwrap
in
(gencid == testcid) && (length genargs == length testargs)
&& (and [isGeneralizationOf g t | (g,t) <- (zip genargs testargs)])
{-do lst <- getConcTypes context types (i+1)
return $ mkMeta i : lst -}
debugReplaceArgs :: Expr -> Expr -> Environ -> IO (Maybe Expr)
debugReplaceArgs aexpr cexpr env =
if isNothing $ unApp aexpr then return Nothing
else if any isNothing $ map unApp $ snd $ fromJust $ unApp aexpr then return Nothing
else
let args = map (fst.fromJust.unApp) $ snd $ fromJust $ unApp aexpr
concExprs = map (\x -> fromJust $ Map.lookup x $ getConcMap env) args
in startReplace 1 cexpr concExprs
where
startReplace i cex [] = return $ Just cex
startReplace i cex (a:as) = do val <- debugReplaceConc cex i a
maybe ( --do putStrLn $ "didn't find "++ showExpr [] a ++ " in " ++showExpr [] cexpr
return Nothing)
(\x -> --do putStrLn $ "found it, the current expression is "++ showExpr [] x
startReplace (i+1) x as)
val
debugReplaceConc :: Expr -> Int -> Expr -> IO (Maybe Expr)
debugReplaceConc expr i e =
let (newe,isThere) = searchArg expr
in if isThere then return $ Just newe else return $ Nothing
where
searchArg e_ =
if isGeneralizationOf e e_ then (mkMeta i, True)
else maybe (e_,False) (\(cid,args) -> let repargs = map searchArg args
in (mkApp cid (map fst repargs), or $ map snd repargs)) $ unApp e_
{-
-- replaceArgs : Original expression to parse (from abstract syntax) -> Concrete expression (parsed)
replaceArgs :: Expr -> Expr -> Environ -> Maybe Expr
replaceArgs aexpr cexpr env =
if isNothing $ unApp aexpr then error $ "could't unwrap this "++ show aexpr
else if any isNothing $ map unApp $ snd $ fromJust $ unApp aexpr then error $ "couldn't unwrap more this : "++ show aexpr
else
let args = map (fst.fromJust.unApp) $ snd $ fromJust $ unApp aexpr
concExprs = map (\x -> fromJust $ Map.lookup x $ getConcMap env) args
in startReplace 1 cexpr concExprs
where
startReplace i cex [] = return cex
startReplace i cex (a:as) = maybe Nothing (\x -> startReplace (i+1) x as) $ replaceConc cex i a
replaceConc :: Expr -> Int -> Expr -> Maybe Expr
replaceConc expr i e =
let (newe,isThere) = searchArg expr
in if isThere then return newe else Nothing
where
searchArg e_ =
if isGeneralizationOf e e_ then (mkMeta i, True)
else maybe (e_,False) (\(cid,args) -> let repargs = map searchArg args
in (mkApp cid (map fst repargs), or $ map snd repargs)) $ unApp e_
writeResults :: Environ -> String -> IO ()
writeResults env fileName =
let cmap = getConcMap env
lincats = unlines $ map (\(x,y) -> "lincat " ++ showCId x ++ " = " ++ showCId y ++ " ; " ) $ Map.toList $ getTypeMap env
sigs = unlines $ map
(\x -> let n = getName x
no = length $ getTypeArgs x
oargs = unwords $ ("lin " ++ showCId n) : ["o"++show i | i <- [1..no]]
in (oargs ++ " = " ++ (simpleReplace $ showExpr [] $ fromJust $ Map.lookup n cmap) ++ " ; ")) $ concat $ Map.elems $ getSigs env
in
writeFile fileName ("\n" ++ lincats ++ "\n\n" ++ sigs)
simpleReplace :: String -> String
simpleReplace [] = []
simpleReplace ('?':xs) = 'o' : simpleReplace xs
simpleReplace (x:xs) = x : simpleReplace xs
-}
isMeta :: Expr -> Bool
isMeta = isJust.unMeta
-- works with utf-8 characters also, as it seems
mkFuncWithArg :: ((CId,CId),[CId]) -> FuncWithArg
mkFuncWithArg ((c1,c2),cids) = FuncWithArg c1 c2 cids
---------------------------------------------------------------------------------
initial :: TypeMap -> ConcMap -> [FuncWithArg] -> [FuncWithArg] -> Environ
initial tm cm fss allfs = Env tm cm (mkSigs fss) allfs
{-
testInit :: [FuncWithArg] -> Environ
testInit allfs = initial lTypes Map.empty [] allfs
lTypes = Map.fromList [(mkCId "Comment", mkCId "S"),(mkCId "Item", mkCId "NP"), (mkCId "Kind", mkCId "CN"), (mkCId "Quality", mkCId "AP")]
-}
startCateg = mkCId "Comment"
-- question about either to give the startcat or not ...
----------------------------------------------------------------------------------------------------------
{-
main =
do args <- getArgs
case args of
[pgfFile] ->
do pgf <- readPGF pgfFile
parsePGF <- readPGF parsePGFfile
fsWithArg <- forExample pgf
let funcsWithArg = map (map mkFuncWithArg) fsWithArg
let morpho = buildMorpho parsePGF parseLang
let fss = concat funcsWithArg
let fileName = takeWhile (/='.') pgfFile ++ lang ++ ".gf"
env <- start parsePGF pgf morpho (testInit fss) fss
putStrLn $ "Should I write the results to a file ? yes/no"
ans <-getLine
if ans == "yes" then do writeResults env fileName
putStrLn $ "Wrote file " ++ fileName
else return ()
_ -> fail "usage : Testing <path-to-pgf> "
start :: PGF -> PGF -> Morpho -> Environ -> [FuncWithArg] -> IO Environ
start parsePGF pgfFile morpho env lst =
do putStrLn "Do you want examples from another language ? (no/concrete syntax name otherwise)"
ans1 <- getLine
putStrLn "Do you want testing mode ? (yes/no)"
ans2 <- getLine
case (ans1,ans2) of
("no","no") -> do putStrLn "no extra language, just the abstract syntax tree"
interact env lst False Nothing
(_,"no") -> interact env lst False (readLanguage ans1)
("no","yes") -> do putStrLn "no extra language, just the abstract syntax tree"
interact env lst True Nothing
(_,"yes") -> interact env lst True (readLanguage ans1)
("no",_) -> do putStrLn "no extra language, just the abstract syntax tree"
putStrLn $ "I assume you don't want the testing mode ... "
interact env lst False Nothing
(_,_) -> do putStrLn $ "I assume you don't want the testing mode ... "
interact env lst False (readLanguage ans1)
where
interact environ [] func _ = return environ
interact environ (farg:fargs) boo otherLang =
do
maybeEnv <- basicInter farg otherLang environ boo
if isNothing maybeEnv then return environ
else interact (fromJust maybeEnv) fargs boo otherLang
basicInter farg js environ False =
let e_ = getExpr farg environ in
if isNothing e_ then return $ Just environ
else parseAndBuild farg js environ (getType farg) e_ Nothing
basicInter farg js environ True =
let (e_,e_test) = get2Expr farg environ in
if isNothing e_ then return $ Just environ
else if isNothing e_test then do putStrLn $ "not enough arguments "++ (showCId $ getName farg)
parseAndBuild farg js environ (getType farg) e_ Nothing
else parseAndBuild farg js environ (getType farg) e_ e_test
-- . head . generateRandomFrom gen2 pgfFile
parseAndBuild farg js environ ty e_ e_test =
do let expr = fromJust e_
gen1 <- newStdGen
gen2 <- newStdGen
let newexpr = head $ generateRandomFrom gen1 pgfFile expr
let embeddedExpr = maybe "***" (showExpr [] ) (embedInStart (getAll environ) (Map.fromList [(ty,expr)]))
let lexpr = if isNothing js then "" else "\n-- " ++ linearize pgfFile (fromJust js) newexpr ++ " --"
putStrLn $ "Give an example for " ++ (showExpr [] expr)
++ lexpr ++ "and now"
++ "\n\nas in " ++ embeddedExpr ++ "\n\n"
--
ex <- getLine
if (ex == ":q") then return Nothing
else
let ctype = fromJust $ Map.lookup (getType farg) (getTypeMap environ) in
do env' <- decypher farg ex expr environ (fromJust $ readType $ showCId ctype) e_test
return (Just env')
decypher farg ex expr environ ty e_test =
--do putStrLn $ "We need to parse " ++ ex ++ " as " ++ show ctype
let pTrees = parse parsePGF (fromJust $ readLanguage "ParseEng") ty ex in
pickTree farg expr environ ex e_test pTrees
-- putStrLn $ "And now for testing, \n is this also correct yes/no \n ## " ++ (linearize parsePGF parseLang $ mapToResource newenv $ llin newenv e_test) ++ " ##"
-- select the right tree among the options given by the parser
pickTree farg expr environ ex e_test [] =
let miswords = morphoMissing morpho (words ex)
in
if null miswords then do putStrLn $ "all words known, but some syntactic construction is not covered by the grammar..."
return environ
else do putStrLn $ "the following words are unknown, please add them to the lexicon: " ++ show miswords
return environ
pickTree farg expr environ ex e_test [tree] =
do val <- searchGoodTree environ expr [tree] -- maybe order here after the probabilities for better precision
maybe (do putStrLn $ "none of the trees is consistent with the rest of the grammar, please check arguments "
return environ)
(\(x,newtree) -> let newenv = updateEnv environ farg (getType farg) x in
do putStrLn $ "the result is "++showExpr [] x
newtestenv <- testTest newenv e_test -- question ? should it belong there - there is just one possibility of a tree...
return newenv) val
pickTree farg expr environ ex e_test parseTrees =
do putStrLn $ "There is more than one possibility, do you want to choose the right tree yourself ? yes/no "
putStr " >"
ans <- getLine
if ans == "yes" then do pTree <- chooseRightTree parseTrees
processTree farg environ expr pTree e_test
else processTree farg environ expr parseTrees e_test
-- introduce testing function, if it doesn't work, then reparse, take that tree
testTree envv e_test = return envv -- TO DO - add testing here
testTest envv Nothing = return envv
testTest envv (Just exxpr) = testTree envv exxpr
-- allows the user to pick his own tree
chooseRightTree trees = return trees -- TO DO - add something clever here
-- selects the tree from where one can abstract over the original arguments
processTree farg environ expr lsTrees e_test =
let trmes = if length lsTrees == 1 then "the tree is not consistent " else "none of the trees is consistent " in
do val <- searchGoodTree environ expr lsTrees
maybe (do putStrLn $ trmes ++ "with the rest of the grammar, please check arguments! "
return environ)
(\(x,newtree) -> let newenv = updateEnv environ farg (getType farg) x in
do putStrLn $ "the result is "++showExpr [] x
newtestenv <- testTest newenv e_test
return newenv) val
-------------------------------
get2Expr :: FuncWithArg -> Environ -> (Maybe Expr, Maybe Expr)
get2Expr farg env =
let tys = getTypeArgs farg
ctx = getSigs env
(lst1,lst2) = getConcTypes2 ctx tys 1
arg1 = if (all isJust lst1) then Just $ mkApp (getName farg) (map fromJust lst1) else Nothing
arg2 = if (all isJust lst2) then Just $ mkApp (getName farg) (map fromJust lst2) else Nothing
in if arg1 == arg2 then (arg1, Nothing)
else (arg1,arg2)
where
getConcTypes2 context [] i = ([],[])
getConcTypes2 context (ty:types) i =
let pos = Map.lookup ty context
in
if isNothing pos || (null $ fromJust pos) then ([Nothing],[Nothing])
else
let (mm,tt) = (last $ fromJust pos, head $ fromJust pos)
mmargs = getTypeArgs mm
newi = i + length mmargs - 1
(lst1,lst2) = getConcTypes2 (Map.insert ty (init (fromJust pos)) context) types (newi+1)
ttargs = getTypeArgs tt
newtti = i + length ttargs - 1
fstArg = if (all isJust lst1) then -- i..newi
(Just $ mkApp (getName mm) [mkMeta j | j <- [1..(length mmargs)]]) : lst1
else [Nothing]
sndArg = if (all isJust lst2) then
(Just $ mkApp (getName tt) [mkMeta j | j <- [1..(length ttargs)]]) : lst2
else [Nothing]
in
(fstArg,sndArg)
-}

View File

@@ -0,0 +1,128 @@
module ExampleService(cgiMain,cgiMain',newPGFCache) where
import System.Random(newStdGen)
import System.FilePath((</>),makeRelative)
import Data.Map(fromList)
import Data.Char(isDigit)
import Data.Maybe(fromJust)
import qualified Codec.Binary.UTF8.String as UTF8 (decodeString)
import PGF
import GF.Compile.ToAPI
import Network.CGI
import Text.JSON
import CGIUtils
import Cache
import qualified ExampleDemo as E
newPGFCache = newCache readPGF
cgiMain :: Cache PGF -> CGI CGIResult
cgiMain = handleErrors . handleCGIErrors . cgiMain' "." "."
cgiMain' root cwd cache =
do command <- getInp "command"
environ <- parseEnviron =<< getInp "state"
case command of
"possibilities" -> doPossibilities environ
"provide_example" -> doProvideExample root cwd cache environ
"abstract_example" -> doAbstractExample cwd cache environ
"test_function" -> doTestFunction cwd cache environ
_ -> throwCGIError 400 ("Unknown command: "++command) []
doPossibilities environ =
do example_environ <- parseEnviron =<< getInp "example_state"
outputJSONP (E.getNext environ example_environ)
doProvideExample root cwd cache environ =
do Just lang <- readInput "lang"
fun <- getCId "fun"
parsePGF <- readParsePGF cwd cache
let adjpath path = root</>makeRelative "/" (makeRelative root cwd</>path)
pgf <- liftIO . readCache cache . adjpath =<< getInp "grammar"
gen <- liftIO newStdGen
let Just (e,s) = E.provideExample gen environ fun parsePGF pgf lang
res = (showExpr [] e,s)
liftIO $ logError $ "proveExample ... = "++show res
outputJSONP res
doAbstractExample cwd cache environ =
do example <- getInp "input"
Just params <- readInput "params"
absstr <- getInp "abstract"
Just abs <- return $ readExpr absstr
liftIO $ logError $ "abstract = "++showExpr [] abs
Just cat <- readInput "cat"
let t = mkType [] cat []
parsePGF <- readParsePGF cwd cache
let lang:_ = languages parsePGF
ae <- liftIO $ abstractExample parsePGF environ lang t abs example
outputJSONP (fmap (\(e,_)->(exprToAPI (instExpMeta params e),e)) ae)
abstractExample parsePGF env lang cat abs example =
E.searchGoodTree env abs (parse parsePGF lang cat example)
doTestFunction cwd cache environ =
do fun <- getCId "fun"
parsePGF <- readParsePGF cwd cache
let lang:_ = languages parsePGF
Just txt <- return (E.testThis environ fun parsePGF lang)
outputJSONP txt
getCId :: String -> CGI CId
getCId name = maybe err return =<< fmap readCId (getInp name)
where err = throwCGIError 400 ("Bad "++name) []
{-
getLimit :: CGI Int
getLimit = maybe err return =<< readInput "limit"
where err = throwCGIError 400 "Missing/bad limit" []
-}
readParsePGF cwd cache =
do parsepgf <- getInp "parser"
liftIO $ readCache cache (cwd</>parsepgf)
parseEnviron s = do state <- liftIO $ readIO s
return $ environ state
getInp name = maybe err (return . UTF8.decodeString) =<< getInput name
where err = throwCGIError 400 ("Missing parameter: "++name) []
instance JSON CId where
showJSON = showJSON . show
readJSON = (readResult =<<) . readJSON
instance JSON Expr where
showJSON = showJSON . showExpr []
readJSON = (m2r . readExpr =<<) . readJSON
m2r = maybe (Error "read failed") Ok
readResult s = case reads s of
(x,r):_ | lex r==[("","")] -> Ok x
_ -> Error "read failed"
--------------------------------------------------------------------------------
-- cat lincat fun lin fun cat cat
environ :: ([(CId, CId)],[(CId, Expr)],[((CId, CId), [CId])]) -> E.Environ
environ (lincats,lins0,funs) =
E.initial (fromList lincats) concmap fs allfs
where
concmap = fromList lins
allfs = map E.mkFuncWithArg funs
fs = [E.mkFuncWithArg f | f@((fn,_),_)<-funs, fn `elem` cns]
cns = map fst lins
lins = filter (not . E.isMeta .snd) lins0
instExpMeta :: [CId] -> Expr -> Expr
instExpMeta ps = fromJust . readExpr . instMeta ps . showExpr []
instMeta :: [CId] -> String -> String
instMeta ps s =
case break (=='?') s of
(s1,'?':s2) ->
case span isDigit s2 of
(s21@(_:_),s22) -> s1++show (ps!!(read s21-1))++instMeta ps s22
("",s22) -> s1++'?':instMeta ps s22
(_,_) -> s

View File

@@ -0,0 +1,15 @@
{-# LANGUAGE CPP #-}
import Control.Concurrent(forkIO)
import Network.FastCGI(runFastCGI,runFastCGIConcurrent')
import ExampleService(cgiMain,newPGFCache)
main = do --stderrToFile logFile
fcgiMain =<< newPGFCache
fcgiMain cache =
#ifndef mingw32_HOST_OS
runFastCGIConcurrent' forkIO 100 (cgiMain cache)
#else
runFastCGI (cgiMain cache)
#endif

View File

@@ -0,0 +1,25 @@
Name: gf-exb
Version: 1.0
Cabal-version: >= 1.8
Build-type: Simple
License: GPL
Synopsis: Example-based grammar writing for the Grammatical Framework
executable exb.fcgi
main-is: exb-fcgi.hs
Hs-source-dirs: . ../server ../compiler ../runtime/haskell
other-modules: ExampleService ExampleDemo
CGIUtils Cache GF.Compile.ToAPI
-- and a lot more...
ghc-options: -threaded
if impl(ghc>=7.0)
ghc-options: -rtsopts
build-depends: base >=4.2 && <5, json, cgi, fastcgi, random,
containers, old-time, directory, bytestring, utf8-string,
pretty, array, mtl, time, filepath
if os(windows)
ghc-options: -optl-mwindows
else
build-depends: unix

View File

@@ -0,0 +1,20 @@
Editor improvements for example-based grammar writing:
+ Remove the same language from the example language menu
+ Send the other language environment to getNext
- Compile a new .pgf automatically when needed
- Update buttons automatically when functions are added or removed
- Switch over to using AbsParadigmsEng.pgf instead of the old exprToAPI function
Editor support for guided construction of linearization functions
- enter api expressions by parsing them with AbsParadigmsEng.pgf in minibar
- replace simpleParseInput with one that accepts quoted string literals
- use lexcode/unlexcode in minibar
- better support for literals in minibar (completion info from the PGF
library should indicate if literals are acceptable)
Server support for example-based grammar writing:
- Change getNext to use info from the example language
- Random generator restricted to defined functions
- More testing

View File

@@ -84,8 +84,8 @@ libpgf_la_SOURCES = \
pgf/graphviz.c \ pgf/graphviz.c \
pgf/aligner.c \ pgf/aligner.c \
pgf/pgf.c \ pgf/pgf.c \
pgf/pgf.h \ pgf/pgf.h
libpgf_la_LDFLAGS = "-no-undefined" libpgf_la_LDFLAGS = -no-undefined
libpgf_la_LIBADD = libgu.la libpgf_la_LIBADD = libgu.la
bin_PROGRAMS = bin_PROGRAMS =

View File

@@ -23,6 +23,14 @@
#define restrict __restrict #define restrict __restrict
#elif defined(__MINGW32__)
#define GU_API_DECL
#define GU_API
#define GU_INTERNAL_DECL
#define GU_INTERNAL
#else #else
#define GU_API_DECL #define GU_API_DECL
@@ -30,7 +38,9 @@
#define GU_INTERNAL_DECL __attribute__ ((visibility ("hidden"))) #define GU_INTERNAL_DECL __attribute__ ((visibility ("hidden")))
#define GU_INTERNAL __attribute__ ((visibility ("hidden"))) #define GU_INTERNAL __attribute__ ((visibility ("hidden")))
#endif #endif
// end MSVC workaround // end MSVC workaround
#include <stddef.h> #include <stddef.h>

10
src/runtime/c/libsg.pc.in Normal file
View File

@@ -0,0 +1,10 @@
prefix=@prefix@
exec_prefix=@exec_prefix@
libdir=@libdir@
includedir=@includedir@
Name: libsg
Description: Semantic Graph library
Version: @VERSION@
Libs: -L${libdir} -lsg -lpgf
Cflags: -I${includedir}

View File

@@ -30,8 +30,8 @@ pgf_expr_unwrap(PgfExpr expr)
} }
} }
static PgfExprTag PGF_API int
pgf_expr_arity(PgfExpr expr, int *arity) pgf_expr_arity(PgfExpr expr)
{ {
int n = 0; int n = 0;
while (true) { while (true) {
@@ -44,9 +44,10 @@ pgf_expr_arity(PgfExpr expr, int *arity)
n = n + 1; n = n + 1;
break; break;
} }
case PGF_EXPR_FUN:
return n;
default: default:
*arity = n; return -1;
return i.tag;
} }
} }
} }
@@ -54,8 +55,8 @@ pgf_expr_arity(PgfExpr expr, int *arity)
PGF_API PgfApplication* PGF_API PgfApplication*
pgf_expr_unapply(PgfExpr expr, GuPool* pool) pgf_expr_unapply(PgfExpr expr, GuPool* pool)
{ {
int arity; int arity = pgf_expr_arity(expr);
if (pgf_expr_arity(expr, &arity) != PGF_EXPR_FUN) { if (arity < 0) {
return NULL; return NULL;
} }
PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity); PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity);
@@ -67,35 +68,10 @@ pgf_expr_unapply(PgfExpr expr, GuPool* pool)
appl->args[n] = app->arg; appl->args[n] = app->arg;
expr = app->fun; expr = app->fun;
} }
appl->efun = pgf_expr_unwrap(expr);
gu_assert(gu_variant_tag(appl->efun) == PGF_EXPR_FUN);
PgfExprFun* fun = gu_variant_data(appl->efun);
appl->fun = fun->fun;
return appl;
}
PGF_API PgfApplication*
pgf_expr_unapply_ex(PgfExpr expr, GuPool* pool)
{
int arity;
pgf_expr_arity(expr, &arity);
PgfApplication* appl = gu_new_flex(pool, PgfApplication, args, arity);
appl->n_args = arity;
for (int n = arity - 1; n >= 0; n--) {
PgfExpr e = pgf_expr_unwrap(expr); PgfExpr e = pgf_expr_unwrap(expr);
gu_assert(gu_variant_tag(e) == PGF_EXPR_APP); gu_assert(gu_variant_tag(e) == PGF_EXPR_FUN);
PgfExprApp* app = gu_variant_data(e); PgfExprFun* fun = gu_variant_data(e);
appl->args[n] = app->arg;
expr = app->fun;
}
appl->efun = pgf_expr_unwrap(expr);
if (gu_variant_tag(appl->efun) == PGF_EXPR_FUN) {
PgfExprFun* fun = gu_variant_data(appl->efun);
appl->fun = fun->fun; appl->fun = fun->fun;
} else {
appl->fun = NULL;
}
return appl; return appl;
} }
@@ -699,17 +675,6 @@ pgf_expr_parser_binds(PgfExprParser* parser)
return binds; return binds;
} }
PGF_API GuString
pgf_expr_parser_ident(PgfExprParser* parser)
{
GuString ident = NULL;
if (parser->token_tag == PGF_TOKEN_IDENT) {
ident = gu_string_copy(gu_string_buf_data(parser->token_value), parser->expr_pool);
pgf_expr_parser_token(parser, true);
}
return ident;
}
PGF_API PgfExpr PGF_API PgfExpr
pgf_expr_parser_expr(PgfExprParser* parser, bool mark) pgf_expr_parser_expr(PgfExprParser* parser, bool mark)
{ {

View File

@@ -126,10 +126,12 @@ typedef struct {
PgfExpr expr; PgfExpr expr;
} PgfExprProb; } PgfExprProb;
PGF_API_DECL int
pgf_expr_arity(PgfExpr expr);
typedef struct PgfApplication PgfApplication; typedef struct PgfApplication PgfApplication;
struct PgfApplication { struct PgfApplication {
PgfExpr efun;
PgfCId fun; PgfCId fun;
int n_args; int n_args;
PgfExpr args[]; PgfExpr args[];
@@ -138,9 +140,6 @@ struct PgfApplication {
PGF_API_DECL PgfApplication* PGF_API_DECL PgfApplication*
pgf_expr_unapply(PgfExpr expr, GuPool* pool); pgf_expr_unapply(PgfExpr expr, GuPool* pool);
PGF_API_DECL PgfApplication*
pgf_expr_unapply_ex(PgfExpr expr, GuPool* pool);
PGF_API_DECL PgfExpr PGF_API_DECL PgfExpr
pgf_expr_apply(PgfApplication*, GuPool* pool); pgf_expr_apply(PgfApplication*, GuPool* pool);

View File

@@ -175,6 +175,7 @@ redo:;
gu_buf_get(buf, PgfProductionApply*, index); gu_buf_get(buf, PgfProductionApply*, index);
gu_assert(n_args == gu_seq_length(papply->args)); gu_assert(n_args == gu_seq_length(papply->args));
capp->abs_id = papply->fun->absfun->name;
capp->fun = papply->fun; capp->fun = papply->fun;
capp->fid = 0; capp->fid = 0;
capp->n_args = n_args; capp->n_args = n_args;
@@ -222,10 +223,10 @@ redo:;
static PgfCncTree static PgfCncTree
pgf_cnc_resolve_def(PgfCnc* cnc, pgf_cnc_resolve_def(PgfCnc* cnc,
size_t n_vars, PgfPrintContext* context, size_t n_vars, PgfPrintContext* context,
PgfCCat* ccat, GuString s, GuPool* pool) PgfCId abs_id, PgfCCat* ccat, GuString s, GuPool* pool)
{ {
PgfCncTree lit = gu_null_variant;
PgfCncTree ret = gu_null_variant; PgfCncTree ret = gu_null_variant;
PgfCncTree lit = gu_null_variant;
PgfCncTreeLit* clit = PgfCncTreeLit* clit =
gu_new_variant(PGF_CNC_TREE_LIT, gu_new_variant(PGF_CNC_TREE_LIT,
@@ -233,7 +234,7 @@ pgf_cnc_resolve_def(PgfCnc* cnc,
&lit, pool); &lit, pool);
clit->n_vars = 0; clit->n_vars = 0;
clit->context = context; clit->context = context;
clit->fid = cnc->fid++; clit->fid = -1; // don't report the literal in the bracket
PgfLiteralStr* lit_str = PgfLiteralStr* lit_str =
gu_new_flex_variant(PGF_LITERAL_STR, gu_new_flex_variant(PGF_LITERAL_STR,
PgfLiteralStr, PgfLiteralStr,
@@ -241,7 +242,7 @@ pgf_cnc_resolve_def(PgfCnc* cnc,
&clit->lit, pool); &clit->lit, pool);
strcpy((char*) lit_str->val, (char*) s); strcpy((char*) lit_str->val, (char*) s);
if (ccat->lindefs == NULL) if (ccat == NULL || ccat->lindefs == NULL)
return lit; return lit;
int index = int index =
@@ -254,6 +255,7 @@ pgf_cnc_resolve_def(PgfCnc* cnc,
PgfCncTreeApp, PgfCncTreeApp,
args, 1, &ret, pool); args, 1, &ret, pool);
capp->ccat = ccat; capp->ccat = ccat;
capp->abs_id= abs_id;
capp->fun = gu_seq_get(ccat->lindefs, PgfCncFun*, index); capp->fun = gu_seq_get(ccat->lindefs, PgfCncFun*, index);
capp->fid = cnc->fid++; capp->fid = cnc->fid++;
capp->n_vars = n_vars; capp->n_vars = n_vars;
@@ -303,6 +305,7 @@ pgf_lzr_wrap_linref(PgfCncTree ctree, GuPool* pool)
PgfCncTreeApp, PgfCncTreeApp,
args, 1, &new_ctree, pool); args, 1, &new_ctree, pool);
new_capp->ccat = NULL; new_capp->ccat = NULL;
new_capp->abs_id = NULL;
new_capp->fun = gu_seq_get(capp->ccat->linrefs, PgfCncFun*, 0); new_capp->fun = gu_seq_get(capp->ccat->linrefs, PgfCncFun*, 0);
new_capp->fid = -1; new_capp->fid = -1;
new_capp->n_vars = 0; new_capp->n_vars = 0;
@@ -396,6 +399,17 @@ pgf_cnc_resolve(PgfCnc* cnc,
goto done; goto done;
} }
PgfCId abs_id = "?";
if (emeta->id > 0) {
GuPool* tmp_pool = gu_local_pool();
GuExn* err = gu_new_exn(tmp_pool);
GuStringBuf* sbuf = gu_new_string_buf(tmp_pool);
GuOut* out = gu_string_buf_out(sbuf);
gu_printf(out, err, "?%d", emeta->id);
abs_id = gu_string_buf_freeze(sbuf, pool);
}
int index = int index =
gu_choice_next(cnc->ch, gu_seq_length(ccat->lindefs)); gu_choice_next(cnc->ch, gu_seq_length(ccat->lindefs));
if (index < 0) { if (index < 0) {
@@ -406,6 +420,7 @@ pgf_cnc_resolve(PgfCnc* cnc,
PgfCncTreeApp, PgfCncTreeApp,
args, 1, &ret, pool); args, 1, &ret, pool);
capp->ccat = ccat; capp->ccat = ccat;
capp->abs_id = abs_id;
capp->fun = gu_seq_get(ccat->lindefs, PgfCncFun*, index); capp->fun = gu_seq_get(ccat->lindefs, PgfCncFun*, index);
capp->fid = cnc->fid++; capp->fid = cnc->fid++;
capp->n_vars = 0; capp->n_vars = 0;
@@ -435,23 +450,7 @@ pgf_cnc_resolve(PgfCnc* cnc,
gu_putc(']', out, err); gu_putc(']', out, err);
GuString s = gu_string_buf_freeze(sbuf, tmp_pool); GuString s = gu_string_buf_freeze(sbuf, tmp_pool);
if (ccat != NULL) { ret = pgf_cnc_resolve_def(cnc, n_vars, context, efun->fun, ccat, s, pool);
ret = pgf_cnc_resolve_def(cnc, n_vars, context, ccat, s, pool);
} else {
PgfCncTreeLit* clit =
gu_new_variant(PGF_CNC_TREE_LIT,
PgfCncTreeLit,
&ret, pool);
clit->n_vars = 0;
clit->context = context;
clit->fid = cnc->fid++;
PgfLiteralStr* lit =
gu_new_flex_variant(PGF_LITERAL_STR,
PgfLiteralStr,
val, strlen(s)+1,
&clit->lit, pool);
strcpy(lit->val, s);
}
gu_pool_free(tmp_pool); gu_pool_free(tmp_pool);
goto done; goto done;
@@ -499,28 +498,7 @@ redo:;
index--; index--;
} }
if (ccat != NULL && ccat->lindefs == NULL) { ret = pgf_cnc_resolve_def(cnc, n_vars, context, ctxt->name, ccat, ctxt->name, pool);
goto done;
}
if (ccat != NULL) {
ret = pgf_cnc_resolve_def(cnc, n_vars, context, ccat, ctxt->name, pool);
} else {
PgfCncTreeLit* clit =
gu_new_variant(PGF_CNC_TREE_LIT,
PgfCncTreeLit,
&ret, pool);
clit->n_vars = 0;
clit->context = context;
clit->fid = cnc->fid++;
PgfLiteralStr* lit =
gu_new_flex_variant(PGF_LITERAL_STR,
PgfLiteralStr,
val, strlen(ctxt->name)+1,
&clit->lit, pool);
strcpy(lit->val, ctxt->name);
}
goto done; goto done;
} }
case PGF_EXPR_TYPED: { case PGF_EXPR_TYPED: {
@@ -941,7 +919,7 @@ pgf_lzr_linearize_tree(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx)
(*lzr->funcs)->begin_phrase(lzr->funcs, (*lzr->funcs)->begin_phrase(lzr->funcs,
fapp->ccat->cnccat->abscat->name, fapp->ccat->cnccat->abscat->name,
fapp->fid, fapp->ccat->cnccat->labels[lin_idx], fapp->fid, fapp->ccat->cnccat->labels[lin_idx],
fun->absfun->name); fapp->abs_id);
} }
gu_require(lin_idx < fun->n_lins); gu_require(lin_idx < fun->n_lins);
@@ -951,7 +929,7 @@ pgf_lzr_linearize_tree(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx)
(*lzr->funcs)->end_phrase(lzr->funcs, (*lzr->funcs)->end_phrase(lzr->funcs,
fapp->ccat->cnccat->abscat->name, fapp->ccat->cnccat->abscat->name,
fapp->fid, fapp->ccat->cnccat->labels[lin_idx], fapp->fid, fapp->ccat->cnccat->labels[lin_idx],
fun->absfun->name); fapp->abs_id);
} }
break; break;
} }
@@ -977,7 +955,7 @@ pgf_lzr_linearize_tree(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx)
PgfCId cat = PgfCId cat =
pgf_literal_cat(lzr->concr, flit->lit)->cnccat->abscat->name; pgf_literal_cat(lzr->concr, flit->lit)->cnccat->abscat->name;
if ((*lzr->funcs)->begin_phrase) { if ((*lzr->funcs)->begin_phrase && flit->fid >= 0) {
(*lzr->funcs)->begin_phrase(lzr->funcs, (*lzr->funcs)->begin_phrase(lzr->funcs,
cat, flit->fid, "s", cat, flit->fid, "s",
""); "");
@@ -1009,7 +987,7 @@ pgf_lzr_linearize_tree(PgfLzr* lzr, PgfCncTree ctree, size_t lin_idx)
(*lzr->funcs)->symbol_token(lzr->funcs, tok); (*lzr->funcs)->symbol_token(lzr->funcs, tok);
} }
if ((*lzr->funcs)->end_phrase) { if ((*lzr->funcs)->end_phrase && flit->fid >= 0) {
(*lzr->funcs)->end_phrase(lzr->funcs, (*lzr->funcs)->end_phrase(lzr->funcs,
cat, flit->fid, "s", cat, flit->fid, "s",
""); "");

View File

@@ -22,6 +22,7 @@ typedef enum {
typedef struct { typedef struct {
PgfCCat* ccat; PgfCCat* ccat;
PgfCId abs_id;
PgfCncFun* fun; PgfCncFun* fun;
int fid; int fid;

View File

@@ -9,6 +9,9 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <math.h> #include <math.h>
#if defined(__MINGW32__) || defined(_MSC_VER)
#include <malloc.h>
#endif
//#define PGF_LOOKUP_DEBUG //#define PGF_LOOKUP_DEBUG
//#define PGF_LINEARIZER_DEBUG //#define PGF_LINEARIZER_DEBUG
@@ -116,7 +119,7 @@ typedef struct {
static PgfAbsProduction* static PgfAbsProduction*
pgf_lookup_new_production(PgfAbsFun* fun, GuPool *pool) pgf_lookup_new_production(PgfAbsFun* fun, GuPool *pool)
{ {
size_t n_hypos = gu_seq_length(fun->type->hypos); size_t n_hypos = fun->type->hypos ? gu_seq_length(fun->type->hypos) : 0;
PgfAbsProduction* prod = gu_new_flex(pool, PgfAbsProduction, args, n_hypos); PgfAbsProduction* prod = gu_new_flex(pool, PgfAbsProduction, args, n_hypos);
prod->fun = fun; prod->fun = fun;
prod->count = 0; prod->count = 0;
@@ -696,8 +699,12 @@ pgf_lookup_tokenize(GuMap* lexicon_idx, GuString sentence, GuPool* pool)
break; break;
const uint8_t* start = p-1; const uint8_t* start = p-1;
while (c != 0 && !gu_ucs_is_space(c)) { if (strchr(".!?,:",c) != NULL)
c = gu_utf8_decode(&p); c = gu_utf8_decode(&p);
else {
while (c != 0 && strchr(".!?,:",c) == NULL && !gu_ucs_is_space(c)) {
c = gu_utf8_decode(&p);
}
} }
const uint8_t* end = p-1; const uint8_t* end = p-1;

View File

@@ -1682,8 +1682,6 @@ pgf_parsing_init(PgfConcr* concr, PgfCId cat,
start_ccat->prods = NULL; start_ccat->prods = NULL;
start_ccat->n_synprods = 0; start_ccat->n_synprods = 0;
gu_assert(start_ccat->cnccat != NULL);
#ifdef PGF_COUNTS_DEBUG #ifdef PGF_COUNTS_DEBUG
ps->ccat_full_count++; ps->ccat_full_count++;
#endif #endif

View File

@@ -46,7 +46,7 @@ pgf_read_in(GuIn* in,
} }
PGF_API_DECL void PGF_API_DECL void
pgf_write(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, const char* fpath, GuExn* err) pgf_write(PgfPGF* pgf, const char* fpath, GuExn* err)
{ {
FILE* outfile = fopen(fpath, "wb"); FILE* outfile = fopen(fpath, "wb");
if (outfile == NULL) { if (outfile == NULL) {
@@ -60,70 +60,13 @@ pgf_write(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, const char* fpath, Gu
GuOut* out = gu_file_out(outfile, tmp_pool); GuOut* out = gu_file_out(outfile, tmp_pool);
PgfWriter* wtr = pgf_new_writer(out, tmp_pool, err); PgfWriter* wtr = pgf_new_writer(out, tmp_pool, err);
pgf_write_pgf(pgf, n_concrs, concrs, wtr); pgf_write_pgf(pgf, wtr);
gu_pool_free(tmp_pool); gu_pool_free(tmp_pool);
fclose(outfile); fclose(outfile);
} }
PGF_API void
pgf_concrete_save(PgfConcr* concr, const char* fpath, GuExn* err)
{
FILE* outfile = fopen(fpath, "wb");
if (outfile == NULL) {
gu_raise_errno(err);
return;
}
GuPool* tmp_pool = gu_local_pool();
// Create an input stream from the input file
GuOut* out = gu_file_out(outfile, tmp_pool);
PgfWriter* wtr = pgf_new_writer(out, tmp_pool, err);
pgf_write_concrete(concr, wtr, true);
gu_pool_free(tmp_pool);
fclose(outfile);
}
PGF_API bool
pgf_have_same_abstract(PgfPGF *one, PgfPGF *two)
{
if (strcmp(one->abstract.name, two->abstract.name) != 0)
return false;
size_t n_cats = gu_seq_length(one->abstract.cats);
if (n_cats != gu_seq_length(two->abstract.cats))
return false;
size_t n_funs = gu_seq_length(one->abstract.funs);
if (n_funs != gu_seq_length(two->abstract.funs))
return false;
for (size_t i = 0; i < n_cats; i++) {
PgfAbsCat* cat1 = gu_seq_index(one->abstract.cats, PgfAbsCat, i);
PgfAbsCat* cat2 = gu_seq_index(two->abstract.cats, PgfAbsCat, i);
if (strcmp(cat1->name, cat2->name) != 0)
return false;
}
for (size_t i = 0; i < n_funs; i++) {
PgfAbsFun* fun1 = gu_seq_index(one->abstract.funs, PgfAbsFun, i);
PgfAbsFun* fun2 = gu_seq_index(two->abstract.funs, PgfAbsFun, i);
if (strcmp(fun1->name, fun2->name) != 0)
return false;
if (!pgf_type_eq(fun1->type, fun2->type))
return false;
}
return true;
}
PGF_API GuString PGF_API GuString
pgf_abstract_name(PgfPGF* pgf) pgf_abstract_name(PgfPGF* pgf)
{ {
@@ -241,7 +184,7 @@ pgf_language_code(PgfConcr* concr)
gu_seq_binsearch(concr->cflags, pgf_flag_order, PgfFlag, "language"); gu_seq_binsearch(concr->cflags, pgf_flag_order, PgfFlag, "language");
if (flag == NULL) if (flag == NULL)
return NULL; return "";
GuVariantInfo i = gu_variant_open(flag->value); GuVariantInfo i = gu_variant_open(flag->value);
switch (i.tag) { switch (i.tag) {
@@ -251,7 +194,7 @@ pgf_language_code(PgfConcr* concr)
} }
} }
return NULL; return "";
} }
PGF_API void PGF_API void

View File

@@ -19,6 +19,14 @@
#define PGF_INTERNAL_DECL #define PGF_INTERNAL_DECL
#define PGF_INTERNAL #define PGF_INTERNAL
#elif defined(__MINGW32__)
#define PGF_API_DECL
#define PGF_API
#define PGF_INTERNAL_DECL
#define PGF_INTERNAL
#else #else
#define PGF_API_DECL #define PGF_API_DECL
@@ -58,10 +66,7 @@ PGF_API_DECL void
pgf_concrete_unload(PgfConcr* concr); pgf_concrete_unload(PgfConcr* concr);
PGF_API_DECL void PGF_API_DECL void
pgf_write(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, const char* fpath, GuExn* err); pgf_write(PgfPGF* pgf, const char* fpath, GuExn* err);
PGF_API_DECL bool
pgf_have_same_abstract(PgfPGF *one, PgfPGF *two);
PGF_API_DECL GuString PGF_API_DECL GuString
pgf_abstract_name(PgfPGF*); pgf_abstract_name(PgfPGF*);
@@ -269,8 +274,7 @@ pgf_callbacks_map_add_literal(PgfConcr* concr, PgfCallbacksMap* callbacks,
PgfCId cat, PgfLiteralCallback* callback); PgfCId cat, PgfLiteralCallback* callback);
PGF_API_DECL void PGF_API_DECL void
pgf_print(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, pgf_print(PgfPGF* pgf, GuOut* out, GuExn* err);
GuOut* out, GuExn* err);
PGF_API_DECL void PGF_API_DECL void
pgf_check_expr(PgfPGF* gr, PgfExpr* pe, PgfType* ty, pgf_check_expr(PgfPGF* gr, PgfExpr* pe, PgfType* ty,

View File

@@ -7,17 +7,13 @@ typedef struct {
} PgfPrintFn; } PgfPrintFn;
static void static void
pgf_print_flags(PgfFlags* flags, int indent, GuOut *out, GuExn* err) pgf_print_flags(PgfFlags* flags, GuOut *out, GuExn* err)
{ {
size_t n_flags = gu_seq_length(flags); size_t n_flags = gu_seq_length(flags);
for (size_t i = 0; i < n_flags; i++) { for (size_t i = 0; i < n_flags; i++) {
PgfFlag* flag = gu_seq_index(flags, PgfFlag, i); PgfFlag* flag = gu_seq_index(flags, PgfFlag, i);
for (int k = 0; k < indent; k++) { gu_puts(" flag ", out, err);
gu_putc(' ', out, err);
}
gu_puts("flag ", out, err);
pgf_print_cid(flag->name, out, err); pgf_print_cid(flag->name, out, err);
gu_puts(" = ", out, err); gu_puts(" = ", out, err);
pgf_print_literal(flag->value, out, err); pgf_print_literal(flag->value, out, err);
@@ -74,7 +70,7 @@ pgf_print_abstract(PgfAbstr* abstr, GuOut* out, GuExn* err)
pgf_print_cid(abstr->name, out, err); pgf_print_cid(abstr->name, out, err);
gu_puts(" {\n", out, err); gu_puts(" {\n", out, err);
pgf_print_flags(abstr->aflags, 2, out, err); pgf_print_flags(abstr->aflags, out, err);
pgf_print_abscats(abstr->cats, out, err); pgf_print_abscats(abstr->cats, out, err);
pgf_print_absfuns(abstr->funs, out, err); pgf_print_absfuns(abstr->funs, out, err);
@@ -389,7 +385,7 @@ pgf_print_concrete(PgfConcr* concr, GuOut* out, GuExn* err)
pgf_print_cid(concr->name, out, err); pgf_print_cid(concr->name, out, err);
gu_puts(" {\n", out, err); gu_puts(" {\n", out, err);
pgf_print_flags(concr->cflags, 2, out, err); pgf_print_flags(concr->cflags, out, err);
gu_puts(" productions\n", out, err); gu_puts(" productions\n", out, err);
PgfPrintFn clo2 = { { pgf_print_productions }, out }; PgfPrintFn clo2 = { { pgf_print_productions }, out };
@@ -427,12 +423,13 @@ pgf_print_concrete(PgfConcr* concr, GuOut* out, GuExn* err)
} }
PGF_API void PGF_API void
pgf_print(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, GuOut* out, GuExn* err) pgf_print(PgfPGF* pgf, GuOut* out, GuExn* err)
{ {
pgf_print_flags(pgf->gflags, 0, out, err);
pgf_print_abstract(&pgf->abstract, out, err); pgf_print_abstract(&pgf->abstract, out, err);
size_t n_concrs = gu_seq_length(pgf->concretes);
for (size_t i = 0; i < n_concrs; i++) { for (size_t i = 0; i < n_concrs; i++) {
pgf_print_concrete(concrs[i], out, err); PgfConcr* concr = gu_seq_index(pgf->concretes, PgfConcr, i);
pgf_print_concrete(concr, out, err);
} }
} }

View File

@@ -1174,14 +1174,6 @@ pgf_read_ccat_cb(GuMapItor* fn, const void* key, void* value, GuExn* err)
// pgf_ccat_set_viterbi_prob(ccat); // pgf_ccat_set_viterbi_prob(ccat);
} }
// The GF compiler needs to call this function when building in memory grammars.
PGF_API void
pgf_concrete_fix_internals(PgfConcr* concr)
{
GuMapItor clo1 = { pgf_read_ccat_cb };
gu_map_iter(concr->ccats, &clo1, NULL);
}
static void static void
pgf_read_concrete_content(PgfReader* rdr, PgfConcr* concr) pgf_read_concrete_content(PgfReader* rdr, PgfConcr* concr)
{ {
@@ -1207,7 +1199,8 @@ pgf_read_concrete_content(PgfReader* rdr, PgfConcr* concr)
concr->cnccats = pgf_read_cnccats(rdr, concr->abstr, concr); concr->cnccats = pgf_read_cnccats(rdr, concr->abstr, concr);
concr->total_cats = pgf_read_int(rdr); concr->total_cats = pgf_read_int(rdr);
pgf_concrete_fix_internals(concr); GuMapItor clo1 = { pgf_read_ccat_cb };
gu_map_iter(concr->ccats, &clo1, NULL);
} }
static void static void

View File

@@ -72,15 +72,10 @@ pgf_write_cid(PgfCId id, PgfWriter* wtr)
PGF_INTERNAL void PGF_INTERNAL void
pgf_write_string(GuString val, PgfWriter* wtr) pgf_write_string(GuString val, PgfWriter* wtr)
{ {
size_t len = 0; size_t len = strlen(val);
const uint8_t* buf = (const uint8_t*) val;
const uint8_t* p = buf;
while (gu_utf8_decode(&p) != 0)
len++;
pgf_write_len(len, wtr); pgf_write_len(len, wtr);
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
gu_out_bytes(wtr->out, (uint8_t*) val, (p-buf)-1, wtr->err); gu_out_bytes(wtr->out, (uint8_t*) val, len, wtr->err);
} }
PGF_INTERNAL void PGF_INTERNAL void
@@ -848,7 +843,7 @@ pgf_write_concrete_content(PgfConcr* concr, PgfWriter* wtr)
pgf_write_int(concr->total_cats, wtr); pgf_write_int(concr->total_cats, wtr);
} }
PGF_INTERNAL void static void
pgf_write_concrete(PgfConcr* concr, PgfWriter* wtr, bool with_content) pgf_write_concrete(PgfConcr* concr, PgfWriter* wtr, bool with_content)
{ {
if (with_content && if (with_content &&
@@ -870,20 +865,34 @@ pgf_write_concrete(PgfConcr* concr, PgfWriter* wtr, bool with_content)
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
} }
static void PGF_API void
pgf_write_concretes(size_t n_concrs, PgfConcr** concrs, PgfWriter* wtr, bool with_content) pgf_concrete_save(PgfConcr* concr, GuOut* out, GuExn* err)
{ {
GuPool* pool = gu_new_pool();
PgfWriter* wtr = pgf_new_writer(out, pool, err);
pgf_write_concrete(concr, wtr, true);
gu_pool_free(pool);
}
static void
pgf_write_concretes(PgfConcrs* concretes, PgfWriter* wtr, bool with_content)
{
size_t n_concrs = gu_seq_length(concretes);
pgf_write_len(n_concrs, wtr); pgf_write_len(n_concrs, wtr);
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
for (size_t i = 0; i < n_concrs; i++) { for (size_t i = 0; i < n_concrs; i++) {
pgf_write_concrete(concrs[i], wtr, with_content); PgfConcr* concr = gu_seq_index(concretes, PgfConcr, i);
pgf_write_concrete(concr, wtr, with_content);
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
} }
} }
PGF_INTERNAL void PGF_INTERNAL void
pgf_write_pgf(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, PgfWriter* wtr) { pgf_write_pgf(PgfPGF* pgf, PgfWriter* wtr) {
gu_out_u16be(wtr->out, pgf->major_version, wtr->err); gu_out_u16be(wtr->out, pgf->major_version, wtr->err);
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
@@ -898,7 +907,7 @@ pgf_write_pgf(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, PgfWriter* wtr) {
bool with_content = bool with_content =
(gu_seq_binsearch(pgf->gflags, pgf_flag_order, PgfFlag, "split") == NULL); (gu_seq_binsearch(pgf->gflags, pgf_flag_order, PgfFlag, "split") == NULL);
pgf_write_concretes(n_concrs, concrs, wtr, with_content); pgf_write_concretes(pgf->concretes, wtr, with_content);
gu_return_on_exn(wtr->err, ); gu_return_on_exn(wtr->err, );
} }

View File

@@ -33,10 +33,7 @@ pgf_write_len(size_t len, PgfWriter* wtr);
PGF_INTERNAL_DECL void PGF_INTERNAL_DECL void
pgf_write_cid(PgfCId id, PgfWriter* wtr); pgf_write_cid(PgfCId id, PgfWriter* wtr);
PGF_INTERNAL void
pgf_write_concrete(PgfConcr* concr, PgfWriter* wtr, bool with_content);
PGF_INTERNAL_DECL void PGF_INTERNAL_DECL void
pgf_write_pgf(PgfPGF* pgf, size_t n_concrs, PgfConcr** concrs, PgfWriter* wtr); pgf_write_pgf(PgfPGF* pgf, PgfWriter* wtr);
#endif // WRITER_H_ #endif // WRITER_H_

View File

@@ -22,21 +22,25 @@
module PGF2 (-- * PGF module PGF2 (-- * PGF
PGF,readPGF,showPGF, PGF,readPGF,showPGF,
-- * Identifiers
CId,
-- * Abstract syntax -- * Abstract syntax
AbsName,abstractName, AbsName,abstractName,
-- ** Categories -- ** Categories
Cat,categories,categoryContext,categoryProbability, Cat,categories,categoryContext,
-- ** Functions -- ** Functions
Fun, functions, functionsByCat, Fun, functions, functionsByCat,
functionType, functionIsDataCon, hasLinearization, functionType, functionIsConstructor, hasLinearization,
-- ** Expressions -- ** Expressions
Expr,showExpr,readExpr,pExpr,pIdent, Expr,showExpr,readExpr,pExpr,
mkAbs,unAbs, mkAbs,unAbs,
mkApp,unApp,unapply, mkApp,unApp,
mkStr,unStr, mkStr,unStr,
mkInt,unInt, mkInt,unInt,
mkFloat,unFloat, mkFloat,unFloat,
mkMeta,unMeta, mkMeta,unMeta,
mkCId,
exprHash, exprSize, exprFunctions, exprSubstitute, exprHash, exprSize, exprFunctions, exprSubstitute,
treeProbability, treeProbability,
-- ** Types -- ** Types
@@ -56,43 +60,33 @@ module PGF2 (-- * PGF
linearize,linearizeAll,tabularLinearize,tabularLinearizeAll,bracketedLinearize,bracketedLinearizeAll, linearize,linearizeAll,tabularLinearize,tabularLinearizeAll,bracketedLinearize,bracketedLinearizeAll,
FId, BracketedString(..), showBracketedString, flattenBracketedString, FId, BracketedString(..), showBracketedString, flattenBracketedString,
printName, categoryFields, printName, categoryFields,
alignWords, gizaAlignment, alignWords,
-- ** Parsing -- ** Parsing
ParseOutput(..), parse, parseWithHeuristics, ParseOutput(..), parse, parseWithHeuristics,
parseToChart, PArg(..), parseToChart, PArg(..),
complete, complete,
-- ** Sentence Lookup -- ** Sentence Lookup
lookupSentence, lookupSentence,
-- ** Generation -- ** Generation
generateAll, generateAllFrom, generateAll,
generateRandom, generateRandomFrom,
-- ** Morphological Analysis -- ** Morphological Analysis
MorphoAnalysis, lookupMorpho, lookupCohorts, fullFormLexicon, MorphoAnalysis, lookupMorpho, lookupCohorts, fullFormLexicon,
filterBest, filterLongest, filterBest, filterLongest,
-- ** Visualizations -- ** Visualizations
GraphvizOptions(..), graphvizDefaults, GraphvizOptions(..), graphvizDefaults,
graphvizAbstractTree, graphvizParseTree, graphvizAbstractTree, graphvizParseTree, graphvizWordAlignment,
Labels, getDepLabels,
graphvizDependencyTree, conlls2latexDoc, getCncDepLabels,
graphvizWordAlignment,
-- * Exceptions -- * Exceptions
PGFError(..), PGFError(..),
-- * Grammar specific callbacks -- * Grammar specific callbacks
LiteralCallback,literalCallbacks, LiteralCallback,literalCallbacks
-- * Auxiliaries
readProbabilitiesFromFile
) where ) where
import Prelude hiding (fromEnum,(<>)) import Prelude hiding (fromEnum,(<>)) -- GHC 8.4.1 clash with Text.PrettyPrint
import Control.Exception(Exception,throwIO) import Control.Exception(Exception,throwIO)
import Control.Monad(forM_) import Control.Monad(forM_)
import System.IO.Unsafe(unsafePerformIO,unsafeInterleaveIO) import System.IO.Unsafe(unsafePerformIO,unsafeInterleaveIO)
import System.Random
import System.IO(fixIO) import System.IO(fixIO)
import Text.PrettyPrint import Text.PrettyPrint
import PGF2.Expr import PGF2.Expr
@@ -105,8 +99,7 @@ import Data.Typeable
import qualified Data.Map as Map import qualified Data.Map as Map
import Data.IORef import Data.IORef
import Data.Char(isUpper,isSpace,isPunctuation) import Data.Char(isUpper,isSpace,isPunctuation)
import Data.List(isSuffixOf,maximumBy,nub,mapAccumL,intersperse,groupBy,find) import Data.List(isSuffixOf,maximumBy,nub)
import Data.Maybe(fromMaybe)
import Data.Function(on) import Data.Function(on)
import Data.Maybe(maybe) import Data.Maybe(maybe)
@@ -119,8 +112,8 @@ import Data.Maybe(maybe)
-- to Concr but has lost its reference to PGF. -- to Concr but has lost its reference to PGF.
type AbsName = String -- ^ Name of abstract syntax type AbsName = CId -- ^ Name of abstract syntax
type ConcName = String -- ^ Name of concrete syntax type ConcName = CId -- ^ Name of concrete syntax
-- | Reads file in Portable Grammar Format and produces -- | Reads file in Portable Grammar Format and produces
-- 'PGF' structure. The file is usually produced with: -- 'PGF' structure. The file is usually produced with:
@@ -145,22 +138,7 @@ readPGF fpath =
throwIO (PGFError "The grammar cannot be loaded") throwIO (PGFError "The grammar cannot be loaded")
else return pgf else return pgf
pgfFPtr <- newForeignPtr gu_pool_finalizer pool pgfFPtr <- newForeignPtr gu_pool_finalizer pool
let touch = touchForeignPtr pgfFPtr return (PGF pgf (touchForeignPtr pgfFPtr))
ref <- newIORef Map.empty
allocaBytes (#size GuMapItor) $ \itor ->
do fptr <- wrapMapItorCallback (getLanguages ref touch)
(#poke GuMapItor, fn) itor fptr
pgf_iter_languages pgf itor nullPtr
freeHaskellFunPtr fptr
langs <- readIORef ref
return (PGF pgf langs touch)
where
getLanguages :: IORef (Map.Map String Concr) -> Touch -> MapItorCallback
getLanguages ref touch itor key value exn = do
langs <- readIORef ref
name <- peekUtf8CString (castPtr key)
concr <- fmap (\ptr -> Concr ptr touch) $ peek (castPtr value)
writeIORef ref $! Map.insert name concr langs
showPGF :: PGF -> String showPGF :: PGF -> String
showPGF p = showPGF p =
@@ -168,25 +146,36 @@ showPGF p =
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->
do (sb,out) <- newOut tmpPl do (sb,out) <- newOut tmpPl
exn <- gu_new_exn tmpPl exn <- gu_new_exn tmpPl
withArrayLen ((map concr . Map.elems . languages) p) $ \n_concrs concrs -> pgf_print (pgf p) out exn
pgf_print (pgf p) (fromIntegral n_concrs) concrs out exn
touchPGF p touchPGF p
s <- gu_string_buf_freeze sb tmpPl s <- gu_string_buf_freeze sb tmpPl
peekUtf8CString s peekUtf8CString s
-- | List of all languages available in the grammar. -- | List of all languages available in the grammar.
languages :: PGF -> Map.Map ConcName Concr languages :: PGF -> Map.Map ConcName Concr
languages p = langs p languages p =
unsafePerformIO $
do ref <- newIORef Map.empty
allocaBytes (#size GuMapItor) $ \itor ->
do fptr <- wrapMapItorCallback (getLanguages ref)
(#poke GuMapItor, fn) itor fptr
pgf_iter_languages (pgf p) itor nullPtr
freeHaskellFunPtr fptr
readIORef ref
where
getLanguages :: IORef (Map.Map String Concr) -> MapItorCallback
getLanguages ref itor key value exn = do
langs <- readIORef ref
name <- peekUtf8CString (castPtr key)
concr <- fmap (\ptr -> Concr ptr (touchPGF p)) $ peek (castPtr value)
writeIORef ref $! Map.insert name concr langs
concreteName :: Concr -> ConcName concreteName :: Concr -> ConcName
concreteName c = unsafePerformIO (peekUtf8CString =<< pgf_concrete_name (concr c)) concreteName c = unsafePerformIO (peekUtf8CString =<< pgf_concrete_name (concr c))
languageCode :: Concr -> Maybe String languageCode :: Concr -> String
languageCode c = unsafePerformIO $ do languageCode c = unsafePerformIO (peekUtf8CString =<< pgf_language_code (concr c))
c_code <- pgf_language_code (concr c)
if c_code == nullPtr
then return Nothing
else fmap Just (peekUtf8CString c_code)
-- | Generates an exhaustive possibly infinite list of -- | Generates an exhaustive possibly infinite list of
-- all abstract syntax expressions of the given type. -- all abstract syntax expressions of the given type.
@@ -202,15 +191,6 @@ generateAll p (Type ctype _) =
exprFPl <- newForeignPtr gu_pool_finalizer exprPl exprFPl <- newForeignPtr gu_pool_finalizer exprPl
fromPgfExprEnum enum genFPl (touchPGF p >> touchForeignPtr exprFPl) fromPgfExprEnum enum genFPl (touchPGF p >> touchForeignPtr exprFPl)
generateAllFrom :: PGF -> Expr -> [(Expr,Float)]
generateAllFrom = error "generateAllFrom is not implemented yet"
generateRandom :: RandomGen gen => gen -> PGF -> Type -> [a]
generateRandom = error "generateRandom is not implemented yet"
generateRandomFrom :: RandomGen gen => gen -> PGF -> Expr -> [a]
generateRandomFrom = error "generateRandomFrom is not implemented yet"
-- | The abstract language name is the name of the top-level -- | The abstract language name is the name of the top-level
-- abstract module -- abstract module
abstractName :: PGF -> AbsName abstractName :: PGF -> AbsName
@@ -262,8 +242,8 @@ functionType p fn =
else Just (Type c_type (touchPGF p))) else Just (Type c_type (touchPGF p)))
-- | The type of a function -- | The type of a function
functionIsDataCon :: PGF -> Fun -> Bool functionIsConstructor :: PGF -> Fun -> Bool
functionIsDataCon p fn = functionIsConstructor p fn =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
c_fn <- newUtf8CString fn tmpPl c_fn <- newUtf8CString fn tmpPl
@@ -273,15 +253,15 @@ functionIsDataCon p fn =
-- | Checks an expression against a specified type. -- | Checks an expression against a specified type.
checkExpr :: PGF -> Expr -> Type -> Either String Expr checkExpr :: PGF -> Expr -> Type -> Either String Expr
checkExpr p (Expr c_expr touch1) (Type c_ty touch2) = checkExpr (PGF p _) (Expr c_expr touch1) (Type c_ty touch2) =
unsafePerformIO $ unsafePerformIO $
alloca $ \pexpr -> alloca $ \pexpr ->
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
exn <- gu_new_exn tmpPl exn <- gu_new_exn tmpPl
exprPl <- gu_new_pool exprPl <- gu_new_pool
poke pexpr c_expr poke pexpr c_expr
pgf_check_expr (pgf p) pexpr c_ty exn exprPl pgf_check_expr p pexpr c_ty exn exprPl
touchPGF p >> touch1 >> touch2 touch1 >> touch2
status <- gu_exn_is_raised exn status <- gu_exn_is_raised exn
if not status if not status
then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl
@@ -300,15 +280,15 @@ checkExpr p (Expr c_expr touch1) (Type c_ty touch2) =
-- possible to infer its type in the GF type system. -- possible to infer its type in the GF type system.
-- In this case the function returns an error. -- In this case the function returns an error.
inferExpr :: PGF -> Expr -> Either String (Expr, Type) inferExpr :: PGF -> Expr -> Either String (Expr, Type)
inferExpr p (Expr c_expr touch1) = inferExpr (PGF p _) (Expr c_expr touch1) =
unsafePerformIO $ unsafePerformIO $
alloca $ \pexpr -> alloca $ \pexpr ->
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
exn <- gu_new_exn tmpPl exn <- gu_new_exn tmpPl
exprPl <- gu_new_pool exprPl <- gu_new_pool
poke pexpr c_expr poke pexpr c_expr
c_ty <- pgf_infer_expr (pgf p) pexpr exn exprPl c_ty <- pgf_infer_expr p pexpr exn exprPl
touchPGF p >> touch1 touch1
status <- gu_exn_is_raised exn status <- gu_exn_is_raised exn
if not status if not status
then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl
@@ -326,15 +306,15 @@ inferExpr p (Expr c_expr touch1) =
-- | Check whether a type is consistent with the abstract -- | Check whether a type is consistent with the abstract
-- syntax of the grammar. -- syntax of the grammar.
checkType :: PGF -> Type -> Either String Type checkType :: PGF -> Type -> Either String Type
checkType p (Type c_ty touch1) = checkType (PGF p _) (Type c_ty touch1) =
unsafePerformIO $ unsafePerformIO $
alloca $ \pty -> alloca $ \pty ->
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
exn <- gu_new_exn tmpPl exn <- gu_new_exn tmpPl
typePl <- gu_new_pool typePl <- gu_new_pool
poke pty c_ty poke pty c_ty
pgf_check_type (pgf p) pty exn typePl pgf_check_type p pty exn typePl
touchPGF p >> touch1 touch1
status <- gu_exn_is_raised exn status <- gu_exn_is_raised exn
if not status if not status
then do typeFPl <- newForeignPtr gu_pool_finalizer typePl then do typeFPl <- newForeignPtr gu_pool_finalizer typePl
@@ -349,13 +329,13 @@ checkType p (Type c_ty touch1) =
else throwIO (PGFError msg) else throwIO (PGFError msg)
compute :: PGF -> Expr -> Expr compute :: PGF -> Expr -> Expr
compute p (Expr c_expr touch1) = compute (PGF p _) (Expr c_expr touch1) =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
exn <- gu_new_exn tmpPl exn <- gu_new_exn tmpPl
exprPl <- gu_new_pool exprPl <- gu_new_pool
c_expr <- pgf_compute (pgf p) c_expr exn tmpPl exprPl c_expr <- pgf_compute p c_expr exn tmpPl exprPl
touchPGF p >> touch1 touch1
status <- gu_exn_is_raised exn status <- gu_exn_is_raised exn
if not status if not status
then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl then do exprFPl <- newForeignPtr gu_pool_finalizer exprPl
@@ -366,10 +346,10 @@ compute p (Expr c_expr touch1) =
throwIO (PGFError msg) throwIO (PGFError msg)
treeProbability :: PGF -> Expr -> Float treeProbability :: PGF -> Expr -> Float
treeProbability p (Expr c_expr touch1) = treeProbability (PGF p _) (Expr c_expr touch1) =
unsafePerformIO $ do unsafePerformIO $ do
res <- pgf_compute_tree_probability (pgf p) c_expr res <- pgf_compute_tree_probability p c_expr
touchPGF p >> touch1 touch1
return (realToFrac res) return (realToFrac res)
exprHash :: Int32 -> Expr -> Int32 exprHash :: Int32 -> Expr -> Int32
@@ -468,436 +448,6 @@ graphvizWordAlignment cs opts e =
s <- gu_string_buf_freeze sb tmpPl s <- gu_string_buf_freeze sb tmpPl
peekUtf8CString s peekUtf8CString s
type Labels = Map.Map Fun [String]
getDepLabels :: String -> Labels
getDepLabels s = Map.fromList [(f,ls) | f:ls <- map words (lines s)]
-- | Visualize word dependency tree.
graphvizDependencyTree
:: String -- ^ Output format: @"latex"@, @"conll"@, @"malt_tab"@, @"malt_input"@ or @"dot"@
-> Bool -- ^ Include extra information (debug)
-> Maybe Labels -- ^ abstract label information obtained with 'getDepLabels'
-> Maybe CncLabels -- ^ concrete label information obtained with ' ' (was: unused (was: @Maybe String@))
-> Concr
-> Expr
-> String -- ^ Rendered output in the specified format
graphvizDependencyTree format debug mlab mclab concr t =
case format of
"latex" -> render . ppLaTeX $ conll2latex' conll
"svg" -> render . ppSVG . toSVG $ conll2latex' conll
"conll" -> printCoNLL conll
"malt_tab" -> render $ vcat (map (hcat . intersperse (char '\t') . (\ws -> [ws !! 0,ws !! 1,ws !! 3,ws !! 6,ws !! 7])) wnodes)
"malt_input" -> render $ vcat (map (hcat . intersperse (char '\t') . take 6) wnodes)
_ -> render $ text "digraph {" $$
space $$
nest 2 (text "rankdir=LR ;" $$
text "node [shape = plaintext] ;" $$
vcat nodes $$
vcat links) $$
text "}"
where
conll = maybe conll0 (\ls -> fixCoNLL ls conll0) mclab
conll0 = (map.map) render wnodes
nodes = map mkNode leaves
links = map mkLink [(fid, fromMaybe (dep_lbl,nil) (lookup fid deps)) | ((cat,fid,fun),_,w) <- tail leaves]
-- CoNLL format: ID FORM LEMMA PLEMMA POS PPOS FEAT PFEAT HEAD PHEAD DEPREL PDEPREL
-- P variants are automatically predicted rather than gold standard
wnodes = [[int i, maltws ws, text fun, text (posCat cat), text cat, unspec, int parent, text lab, unspec, unspec] |
((cat,fid,fun),i,ws) <- tail leaves,
let (lab,parent) = fromMaybe (dep_lbl,0)
(do (lbl,fid) <- lookup fid deps
(_,i,_) <- find (\((_,fid1,_),i,_) -> fid == fid1) leaves
return (lbl,i))
]
maltws = text . concat . intersperse "+" . words -- no spaces in column 2
nil = -1
bss = bracketedLinearize concr t
root = ("_",nil,"_")
leaves = (root,0,root_lbl) : (groupAndIndexIt 1 . concatMap (getLeaves root)) bss
deps = let (_,(h,deps)) = getDeps 0 [] t
in (h,(dep_lbl,nil)):deps
groupAndIndexIt id [] = []
groupAndIndexIt id ((p,w):pws) = (p,id,w) : groupAndIndexIt (id+1) pws
--- groupAndIndexIt id ((p,w):pws) = let (ws,pws1) = collect pws
--- in (p,id,unwords (w:ws)) : groupAndIndexIt (id+1) pws1
where
collect pws@((p1,w):pws1)
| p == p1 = let (ws,pws2) = collect pws1
in (w:ws,pws2)
collect pws = ([],pws)
getLeaves parent bs =
case bs of
Leaf w -> [(parent,w)]
Bracket cat fid _ fun bss -> concatMap (getLeaves (cat,fid,fun)) bss
mkNode ((_,p,_),i,w) =
tag p <+> brackets (text "label = " <> doubleQuotes (int i <> char '.' <+> text w)) <+> semi
mkLink (x,(lbl,y)) = tag y <+> text "->" <+> tag x <+> text "[label = " <> doubleQuotes (text lbl) <> text "] ;"
labels = maybe Map.empty id mlab
clabels = maybe [] id mclab
posCat cat = case Map.lookup cat labels of
Just [p] -> p
_ -> cat
getDeps n_fid xs e =
case unAbs e of
Just (_, x, e) -> getDeps n_fid (x:xs) e
Nothing -> case unApp e of
Just (f,es) -> let (n_fid_1,ds) = descend n_fid xs es
(mb_h, deps) = selectHead f ds
in case mb_h of
Just (fid,deps0) -> (n_fid_1+1,(fid,deps0++
[(n_fid_1,(dep_lbl,fid))]++
concat [(m,(lbl,fid)):ds | (lbl,(m,ds)) <- deps]))
Nothing -> (n_fid_1+1,(n_fid_1,concat [(m,(lbl,n_fid_1)):ds | (lbl,(m,ds)) <- deps]))
Nothing -> (n_fid+1,(n_fid,[]))
descend n_fid xs es = mapAccumL (\n_fid e -> getDeps n_fid xs e) n_fid es
selectHead f ds =
case Map.lookup f labels of
Just lbls -> extractHead (zip lbls ds)
Nothing -> extractLast ds
where
extractHead [] = (Nothing, [])
extractHead (ld@(l,d):lds)
| l == head_lbl = (Just d,lds)
| otherwise = let (mb_h,deps) = extractHead lds
in (mb_h,ld:deps)
extractLast [] = (Nothing, [])
extractLast (d:ds)
| null ds = (Just d,[])
| otherwise = let (mb_h,deps) = extractLast ds
in (mb_h,(dep_lbl,d):deps)
dep_lbl = "dep"
head_lbl = "head"
root_lbl = "ROOT"
unspec = text "_"
---------------------- should be a separate module?
-- visualization with latex output. AR Nov 2015
conlls2latexDoc :: [String] -> String
conlls2latexDoc =
render .
latexDoc .
vcat .
intersperse (text "" $+$ app "vspace" (text "4mm")) .
map conll2latex .
filter (not . null)
conll2latex :: String -> Doc
conll2latex = ppLaTeX . conll2latex' . parseCoNLL
conll2latex' :: CoNLL -> [LaTeX]
conll2latex' = dep2latex . conll2dep'
data Dep = Dep {
wordLength :: Int -> Double -- length of word at position int -- was: fixed width, millimetres (>= 20.0)
, tokens :: [(String,String)] -- word, pos (0..)
, deps :: [((Int,Int),String)] -- from, to, label
, root :: Int -- root word position
}
-- some general measures
defaultWordLength = 20.0 -- the default fixed width word length, making word 100 units
defaultUnit = 0.2 -- unit in latex pictures, 0.2 millimetres
spaceLength = 10.0
charWidth = 1.8
wsize rwld w = 100 * rwld w + spaceLength -- word length, units
wpos rwld i = sum [wsize rwld j | j <- [0..i-1]] -- start position of the i'th word
wdist rwld x y = sum [wsize rwld i | i <- [min x y .. max x y - 1]] -- distance between words x and y
labelheight h = h + arcbase + 3 -- label just above arc; 25 would put it just below
labelstart c = c - 15.0 -- label starts 15u left of arc centre
arcbase = 30.0 -- arcs start and end 40u above the bottom
arcfactor r = r * 600 -- reduction of arc size from word distance
xyratio = 3 -- width/height ratio of arcs
putArc :: (Int -> Double) -> Int -> Int -> Int -> String -> [DrawingCommand]
putArc frwld height x y label = [oval,arrowhead,labelling] where
oval = Put (ctr,arcbase) (OvalTop (wdth,hght))
arrowhead = Put (endp,arcbase + 5) (ArrowDown 5) -- downgoing arrow 5u above the arc base
labelling = Put (labelstart ctr,labelheight (hght/2)) (TinyText label)
dxy = wdist frwld x y -- distance between words, >>= 20.0
ndxy = 100 * rwld * fromIntegral height -- distance that is indep of word length
hdxy = dxy / 2 -- half the distance
wdth = dxy - (arcfactor rwld)/dxy -- longer arcs are wider in proportion
hght = ndxy / (xyratio * rwld) -- arc height is independent of word length
begp = min x y -- begin position of oval
ctr = wpos frwld begp + hdxy + (if x < y then 20 else 10) -- LR arcs are farther right from center of oval
endp = (if x < y then (+) else (-)) ctr (wdth/2) -- the point of the arrow
rwld = 0.5 ----
dep2latex :: Dep -> [LaTeX]
dep2latex d =
[Comment (unwords (map fst (tokens d))),
Picture defaultUnit (width,height) (
[Put (wpos rwld i,0) (Text w) | (i,w) <- zip [0..] (map fst (tokens d))] -- words
++ [Put (wpos rwld i,15) (TinyText w) | (i,w) <- zip [0..] (map snd (tokens d))] -- pos tags 15u above bottom
++ concat [putArc rwld (aheight x y) x y label | ((x,y),label) <- deps d] -- arcs and labels
++ [Put (wpos rwld (root d) + 15,height) (ArrowDown (height-arcbase))]
++ [Put (wpos rwld (root d) + 20,height - 10) (TinyText "ROOT")]
)]
where
wld i = wordLength d i -- >= 20.0
rwld i = (wld i) / defaultWordLength -- >= 1.0
aheight x y = depth (min x y) (max x y) + 1 ---- abs (x-y)
arcs = [(min u v, max u v) | ((u,v),_) <- deps d]
depth x y = case [(u,v) | (u,v) <- arcs, (x < u && v <= y) || (x == u && v < y)] of ---- only projective arcs counted
[] -> 0
uvs -> 1 + maximum (0:[depth u v | (u,v) <- uvs])
width = {-round-} (sum [wsize rwld w | (w,_) <- zip [0..] (tokens d)]) + {-round-} spaceLength * fromIntegral ((length (tokens d)) - 1)
height = 50 + 20 * {-round-} (maximum (0:[aheight x y | ((x,y),_) <- deps d]))
type CoNLL = [[String]]
parseCoNLL :: String -> CoNLL
parseCoNLL = map words . lines
--conll2dep :: String -> Dep
--conll2dep = conll2dep' . parseCoNLL
conll2dep' :: CoNLL -> Dep
conll2dep' ls = Dep {
wordLength = wld
, tokens = toks
, deps = dps
, root = head $ [read x-1 | x:_:_:_:_:_:"0":_ <- ls] ++ [1]
}
where
wld i = maximum (0:[charWidth * fromIntegral (length w) | w <- let (tok,pos) = toks !! i in [tok,pos]])
toks = [(w,c) | _:w:_:c:_ <- ls]
dps = [((read y-1, read x-1),lab) | x:_:_:_:_:_:y:lab:_ <- ls, y /="0"]
--maxdist = maximum [abs (x-y) | ((x,y),_) <- dps]
-- * LaTeX Pictures (see https://en.wikibooks.org/wiki/LaTeX/Picture)
-- We render both LaTeX and SVG from this intermediate representation of
-- LaTeX pictures.
data LaTeX = Comment String | Picture UnitLengthMM Size [DrawingCommand]
data DrawingCommand = Put Position Object
data Object = Text String | TinyText String | OvalTop Size | ArrowDown Length
type UnitLengthMM = Double
type Size = (Double,Double)
type Position = (Double,Double)
type Length = Double
-- * latex formatting
ppLaTeX = vcat . map ppLaTeX1
where
ppLaTeX1 el =
case el of
Comment s -> comment s
Picture unit size cmds ->
app "setlength{\\unitlength}" (text (show unit ++ "mm"))
$$ hang (app "begin" (text "picture")<>text (show size)) 2
(vcat (map ppDrawingCommand cmds))
$$ app "end" (text "picture")
$$ text ""
ppDrawingCommand (Put pos obj) = put pos (ppObject obj)
ppObject obj =
case obj of
Text s -> text s
TinyText s -> small (text s)
OvalTop size -> text "\\oval" <> text (show size) <> text "[t]"
ArrowDown len -> app "vector(0,-1)" (text (show len))
put p@(_,_) = app ("put" ++ show p)
small w = text "{\\tiny" <+> w <> text "}"
comment s = text "%%" <+> text s -- line break show follow
app macro arg = text "\\" <> text macro <> text "{" <> arg <> text "}"
latexDoc :: Doc -> Doc
latexDoc body =
vcat [text "\\documentclass{article}",
text "\\usepackage[utf8]{inputenc}",
text "\\begin{document}",
body,
text "\\end{document}"]
-- * SVG (see https://www.w3.org/Graphics/SVG/IG/resources/svgprimer.html)
-- | Render LaTeX pictures as SVG
toSVG = concatMap toSVG1
where
toSVG1 el =
case el of
Comment s -> []
Picture unit size@(w,h) cmds ->
[Elem "svg" ["width".=x1,"height".=y0+5,
("viewBox",unwords (map show [0,0,x1,y0+5])),
("version","1.1"),
("xmlns","http://www.w3.org/2000/svg")]
(white_bg:concatMap draw cmds)]
where
white_bg =
Elem "rect" ["x".=0,"y".=0,"width".=x1,"height".=y0+5,
("fill","white")] []
draw (Put pos obj) = objectSVG pos obj
objectSVG pos obj =
case obj of
Text s -> [text 16 pos s]
TinyText s -> [text 10 pos s]
OvalTop size -> [ovalTop pos size]
ArrowDown len -> arrowDown pos len
text h (x,y) s =
Elem "text" ["x".=xc x,"y".=yc y-2,"font-size".=h]
[CharData s]
ovalTop (x,y) (w,h) =
Elem "path" [("d",path),("stroke","black"),("fill","none")] []
where
x1 = x-w/2
x2 = min x (x1+r)
x3 = max x (x4-r)
x4 = x+w/2
y1 = y
y2 = y+r
r = h/2
sx = show . xc
sy = show . yc
path = unwords (["M",sx x1,sy y1,"Q",sx x1,sy y2,sx x2,sy y2,
"L",sx x3,sy y2,"Q",sx x4,sy y2,sx x4,sy y1])
arrowDown (x,y) len =
[Elem "line" ["x1".=xc x,"y1".=yc y,"x2".=xc x,"y2".=y2,
("stroke","black")] [],
Elem "path" [("d",unwords arrowhead)] []]
where
x2 = xc x
y2 = yc (y-len)
arrowhead = "M":map show [x2,y2,x2-3,y2-6,x2+3,y2-6]
xc x = num x+5
yc y = y0-num y
x1 = num w+10
y0 = num h+20
num x = round (scale*x)
scale = unit*5
infix 0 .=
n.=v = (n,show v)
-- * SVG is XML
data SVG = CharData String | Elem TagName Attrs [SVG]
type TagName = String
type Attrs = [(String,String)]
ppSVG svg =
vcat [text "<?xml version=\"1.0\" standalone=\"no\"?>",
text "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"",
text "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">",
text "",
vcat (map ppSVG1 svg)] -- It should be a single <svg> element...
where
ppSVG1 svg1 =
case svg1 of
CharData s -> text (encode s)
Elem tag attrs [] ->
text "<"<>text tag<>cat (map attr attrs) <> text "/>"
Elem tag attrs svg ->
cat [text "<"<>text tag<>cat (map attr attrs) <> text ">",
nest 2 (cat (map ppSVG1 svg)),
text "</"<>text tag<>text ">"]
attr (n,v) = text " "<>text n<>text "=\""<>text (encode v)<>text "\""
encode s = foldr encodeEntity "" s
encodeEntity = encodeEntity' (const False)
encodeEntity' esc c r =
case c of
'&' -> "&amp;"++r
'<' -> "&lt;"++r
'>' -> "&gt;"++r
_ -> c:r
----------------------------------
-- concrete syntax annotations (local) on top of conll
-- examples of annotations:
-- UseComp {"not"} PART neg head
-- UseComp {*} AUX cop head
type CncLabels = [(String, String -> Maybe (String -> String,String,String))]
-- (fun, word -> (pos,label,target))
-- the pos can remain unchanged, as in the current notation in the article
fixCoNLL :: CncLabels -> CoNLL -> CoNLL
fixCoNLL labels conll = map fixc conll where
fixc row = case row of
(i:word:fun:pos:cat:x_:"0":"dep":xs) -> (i:word:fun:pos:cat:x_:"0":"root":xs) --- change the root label from dep to root
(i:word:fun:pos:cat:x_:j:label:xs) -> case look (fun,word) of
Just (pos',label',"head") -> (i:word:fun:pos' pos:cat:x_:j :label':xs)
Just (pos',label',target) -> (i:word:fun:pos' pos:cat:x_: getDep j target:label':xs)
_ -> row
_ -> row
look (fun,word) = case lookup fun labels of
Just relabel -> case relabel word of
Just row -> Just row
_ -> case lookup "*" labels of
Just starlabel -> starlabel word
_ -> Nothing
_ -> case lookup "*" labels of
Just starlabel -> starlabel word
_ -> Nothing
getDep j label = maybe j id $ lookup (label,j) [((label,j),i) | i:word:fun:pos:cat:x_:j:label:xs <- conll]
getCncDepLabels :: String -> CncLabels
getCncDepLabels = map merge . groupBy (\ (x,_) (a,_) -> x == a) . concatMap analyse . filter choose . lines where
--- choose is for compatibility with the general notation
choose line = notElem '(' line && elem '{' line --- ignoring non-local (with "(") and abstract (without "{") rules
analyse line = case break (=='{') line of
(beg,_:ws) -> case break (=='}') ws of
(toks,_:target) -> case (words beg, words target) of
(fun:_,[ label,j]) -> [(fun, (tok, (id, label,j))) | tok <- getToks toks]
(fun:_,[pos,label,j]) -> [(fun, (tok, (const pos,label,j))) | tok <- getToks toks]
_ -> []
_ -> []
_ -> []
merge rules@((fun,_):_) = (fun, \tok ->
case lookup tok (map snd rules) of
Just new -> return new
_ -> lookup "*" (map snd rules)
)
getToks = words . map (\c -> if elem c "\"," then ' ' else c)
printCoNLL :: CoNLL -> String
printCoNLL = unlines . map (concat . intersperse "\t")
newGraphvizOptions :: Ptr GuPool -> GraphvizOptions -> IO (Ptr PgfGraphvizOptions) newGraphvizOptions :: Ptr GuPool -> GraphvizOptions -> IO (Ptr PgfGraphvizOptions)
newGraphvizOptions pool opts = do newGraphvizOptions pool opts = do
c_opts <- gu_malloc pool (#size PgfGraphvizOptions) c_opts <- gu_malloc pool (#size PgfGraphvizOptions)
@@ -1127,6 +677,7 @@ parseWithHeuristics lang (Type ctype touchType) sent heuristic callbacks =
sent <- newUtf8CString sent parsePl sent <- newUtf8CString sent parsePl
callbacks_map <- mkCallbacksMap (concr lang) callbacks parsePl callbacks_map <- mkCallbacksMap (concr lang) callbacks parsePl
enum <- pgf_parse_with_heuristics (concr lang) ctype sent heuristic callbacks_map exn parsePl exprPl enum <- pgf_parse_with_heuristics (concr lang) ctype sent heuristic callbacks_map exn parsePl exprPl
touchType
failed <- gu_exn_is_raised exn failed <- gu_exn_is_raised exn
if failed if failed
then do is_parse_error <- gu_exn_caught exn gu_exn_type_PgfParseError then do is_parse_error <- gu_exn_caught exn gu_exn_type_PgfParseError
@@ -1276,8 +827,7 @@ parseToChart lang (Type ctype touchType) sent heuristic callbacks roots =
peekPArg chart ptr = do peekPArg chart ptr = do
c_hypos <- (#peek PgfPArg, hypos) ptr c_hypos <- (#peek PgfPArg, hypos) ptr
hypos <- if c_hypos /= nullPtr hypos <- if c_hypos /= nullPtr
then do res <- peekSequence (deRef peekFId) (#size int) c_hypos then peekSequence (deRef peekFId) (#size int) c_hypos
return [(fid,fid) | fid <- res]
else return [] else return []
c_ccat <- (#peek PgfPArg, ccat) ptr c_ccat <- (#peek PgfPArg, ccat) ptr
(fid,chart) <- peekCCat get_range chart c_ccat (fid,chart) <- peekCCat get_range chart c_ccat
@@ -1427,7 +977,7 @@ complete :: Concr -- ^ the language with which we parse
-> Type -- ^ the start category -> Type -- ^ the start category
-> String -- ^ the input sentence (excluding token being completed) -> String -- ^ the input sentence (excluding token being completed)
-> String -- ^ prefix (partial token being completed) -> String -- ^ prefix (partial token being completed)
-> ParseOutput [(String, Fun, Cat, Float)] -- ^ (token, category, function, probability) -> ParseOutput [(String, CId, CId, Float)] -- ^ (token, category, function, probability)
complete lang (Type ctype _) sent pfx = complete lang (Type ctype _) sent pfx =
unsafePerformIO $ do unsafePerformIO $ do
parsePl <- gu_new_pool parsePl <- gu_new_pool
@@ -1463,7 +1013,7 @@ complete lang (Type ctype _) sent pfx =
fpl <- newForeignPtr gu_pool_finalizer parsePl fpl <- newForeignPtr gu_pool_finalizer parsePl
ParseOk <$> fromCompletions enum fpl ParseOk <$> fromCompletions enum fpl
where where
fromCompletions :: Ptr GuEnum -> ForeignPtr GuPool -> IO [(String, Cat, Fun, Float)] fromCompletions :: Ptr GuEnum -> ForeignPtr GuPool -> IO [(String, CId, CId, Float)]
fromCompletions enum fpl = fromCompletions enum fpl =
withGuPool $ \tmpPl -> do withGuPool $ \tmpPl -> do
cmpEntry <- alloca $ \ptr -> cmpEntry <- alloca $ \ptr ->
@@ -1606,6 +1156,14 @@ tabularLinearizeAll lang e = unsafePerformIO $
ss <- collectTable lang ctree (lin_idx+1) labels exn tmpPl ss <- collectTable lang ctree (lin_idx+1) labels exn tmpPl
return ((label,s):ss) return ((label,s):ss)
throwExn exn = do
is_exn <- gu_exn_caught exn gu_exn_type_PgfExn
if is_exn
then do c_msg <- (#peek GuExn, data.data) exn
msg <- peekUtf8CString c_msg
throwIO (PGFError msg)
else do throwIO (PGFError "The abstract tree cannot be linearized")
categoryFields :: Concr -> Cat -> Maybe [String] categoryFields :: Concr -> Cat -> Maybe [String]
categoryFields lang cat = categoryFields lang cat =
unsafePerformIO $ do unsafePerformIO $ do
@@ -1634,8 +1192,8 @@ categoryFields lang cat =
data BracketedString data BracketedString
= Leaf String -- ^ this is the leaf i.e. a single token = Leaf String -- ^ this is the leaf i.e. a single token
| BIND -- ^ the surrounding tokens must be bound together | BIND -- ^ the surrounding tokens must be bound together
| Bracket Cat {-# UNPACK #-} !FId String Fun [BracketedString] | Bracket CId {-# UNPACK #-} !FId String CId [BracketedString]
-- ^ this is a bracket. The 'Cat' is the category of -- ^ this is a bracket. The 'CId' is the category of
-- the phrase. The 'FId' is an unique identifier for -- the phrase. The 'FId' is an unique identifier for
-- every phrase in the sentence. For context-free grammars -- every phrase in the sentence. For context-free grammars
-- i.e. without discontinuous constituents this identifier -- i.e. without discontinuous constituents this identifier
@@ -1646,7 +1204,7 @@ data BracketedString
-- the analysis string. If the grammar is reduplicating -- the analysis string. If the grammar is reduplicating
-- then the constituent indices will be the same for all brackets -- then the constituent indices will be the same for all brackets
-- that represents the same constituent. -- that represents the same constituent.
-- The 'Fun' is the name of the abstract function that generated -- The second 'CId' is the name of the abstract function that generated
-- this phrase. -- this phrase.
-- | Renders the bracketed string as a string where -- | Renders the bracketed string as a string where
@@ -1662,6 +1220,7 @@ ppBracketedString (Bracket cat fid _ _ bss) = parens (text cat <> colon <> int f
-- | Extracts the sequence of tokens from the bracketed string -- | Extracts the sequence of tokens from the bracketed string
flattenBracketedString :: BracketedString -> [String] flattenBracketedString :: BracketedString -> [String]
flattenBracketedString (Leaf w) = [w] flattenBracketedString (Leaf w) = [w]
flattenBracketedString BIND = []
flattenBracketedString (Bracket _ _ _ _ bss) = concatMap flattenBracketedString bss flattenBracketedString (Bracket _ _ _ _ bss) = concatMap flattenBracketedString bss
bracketedLinearize :: Concr -> Expr -> [BracketedString] bracketedLinearize :: Concr -> Expr -> [BracketedString]
@@ -1819,8 +1378,6 @@ alignWords lang e = unsafePerformIO $
(fids :: [CInt]) <- peekArray (fromIntegral (n_fids :: CInt)) (ptr `plusPtr` (#offset PgfAlignmentPhrase, fids)) (fids :: [CInt]) <- peekArray (fromIntegral (n_fids :: CInt)) (ptr `plusPtr` (#offset PgfAlignmentPhrase, fids))
return (phrase, map fromIntegral fids) return (phrase, map fromIntegral fids)
gizaAlignment = error "gizaAlignment is not implemented"
printName :: Concr -> Fun -> Maybe String printName :: Concr -> Fun -> Maybe String
printName lang fun = printName lang fun =
unsafePerformIO $ unsafePerformIO $
@@ -1902,17 +1459,16 @@ categories p =
name <- peekUtf8CString (castPtr key) name <- peekUtf8CString (castPtr key)
writeIORef ref $! (name : names) writeIORef ref $! (name : names)
categoryContext :: PGF -> Cat -> Maybe [Hypo] categoryContext :: PGF -> Cat -> [Hypo]
categoryContext p cat = categoryContext p cat =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->
do c_cat <- newUtf8CString cat tmpPl do c_cat <- newUtf8CString cat tmpPl
c_hypos <- pgf_category_context (pgf p) c_cat c_hypos <- pgf_category_context (pgf p) c_cat
if c_hypos == nullPtr if c_hypos == nullPtr
then return Nothing then return []
else do n_hypos <- (#peek GuSeq, len) c_hypos else do n_hypos <- (#peek GuSeq, len) c_hypos
hypos <- peekHypos (c_hypos `plusPtr` (#offset GuSeq, data)) 0 n_hypos peekHypos (c_hypos `plusPtr` (#offset GuSeq, data)) 0 n_hypos
return (Just hypos)
where where
peekHypos :: Ptr a -> Int -> Int -> IO [Hypo] peekHypos :: Ptr a -> Int -> Int -> IO [Hypo]
peekHypos c_hypo i n peekHypos c_hypo i n
@@ -1927,8 +1483,8 @@ categoryContext p cat =
toBindType (#const PGF_BIND_TYPE_EXPLICIT) = Explicit toBindType (#const PGF_BIND_TYPE_EXPLICIT) = Explicit
toBindType (#const PGF_BIND_TYPE_IMPLICIT) = Implicit toBindType (#const PGF_BIND_TYPE_IMPLICIT) = Implicit
categoryProbability :: PGF -> Cat -> Float categoryProb :: PGF -> Cat -> Float
categoryProbability p cat = categoryProb p cat =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->
do c_cat <- newUtf8CString cat tmpPl do c_cat <- newUtf8CString cat tmpPl
@@ -1939,7 +1495,7 @@ categoryProbability p cat =
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Helper functions -- Helper functions
fromPgfExprEnum :: Ptr GuEnum -> ForeignPtr GuPool -> Touch -> IO [(Expr, Float)] fromPgfExprEnum :: Ptr GuEnum -> ForeignPtr GuPool -> IO () -> IO [(Expr, Float)]
fromPgfExprEnum enum fpl touch = fromPgfExprEnum enum fpl touch =
do pgfExprProb <- alloca $ \ptr -> do pgfExprProb <- alloca $ \ptr ->
withForeignPtr fpl $ \pl -> withForeignPtr fpl $ \pl ->
@@ -1953,22 +1509,6 @@ fromPgfExprEnum enum fpl touch =
prob <- (#peek PgfExprProb, prob) pgfExprProb prob <- (#peek PgfExprProb, prob) pgfExprProb
return ((Expr expr touch,prob) : ts) return ((Expr expr touch,prob) : ts)
fromPgfTokenEnum :: Ptr GuEnum -> ForeignPtr GuPool -> IO [(String, Cat, Fun, Float)]
fromPgfTokenEnum enum fpl =
do pgfTokenProb <- alloca $ \ptr ->
withForeignPtr fpl $ \pl ->
do gu_enum_next enum ptr pl
peek ptr
if pgfTokenProb == nullPtr
then do finalizeForeignPtr fpl
return []
else do tok <- (#peek PgfTokenProb, tok) pgfTokenProb >>= peekUtf8CString
cat <- (#peek PgfTokenProb, cat) pgfTokenProb >>= peekUtf8CString
fun <- (#peek PgfTokenProb, fun) pgfTokenProb >>= peekUtf8CString
prob <- (#peek PgfTokenProb, prob) pgfTokenProb
ts <- unsafeInterleaveIO (fromPgfTokenEnum enum fpl)
return ((tok,cat,fun,prob) : ts)
----------------------------------------------------------------------- -----------------------------------------------------------------------
-- Exceptions -- Exceptions
@@ -2047,13 +1587,3 @@ capitalized' test s@(c:_) | test c =
case span isSpace rest1 of case span isSpace rest1 of
(space,rest2) -> Just (name++space,rest2) (space,rest2) -> Just (name++space,rest2)
capitalized' not s = Nothing capitalized' not s = Nothing
tag i
| i < 0 = char 'r' <> int (negate i)
| otherwise = char 'n' <> int i
readProbabilitiesFromFile :: FilePath -> IO (Map.Map String Double)
readProbabilitiesFromFile fpath = do
s <- readFile fpath
return $ Map.fromList [(f,read p) | f:p:_ <- map words (lines s)]

View File

@@ -10,13 +10,19 @@ import Data.Data
import PGF2.FFI import PGF2.FFI
import Data.Maybe(fromJust) import Data.Maybe(fromJust)
type Cat = String -- ^ Name of syntactic category -- | An data type that represents
type Fun = String -- ^ Name of function -- identifiers for functions and categories in PGF.
type CId = String
wildCId = "_" :: CId
type Cat = CId -- ^ Name of syntactic category
type Fun = CId -- ^ Name of function
data BindType = data BindType =
Explicit Explicit
| Implicit | Implicit
deriving (Show, Eq, Ord) deriving Show
----------------------------------------------------------------------------- -----------------------------------------------------------------------------
-- Expressions -- Expressions
@@ -53,7 +59,7 @@ exprDataType :: DataType
exprDataType = mkDataType "PGF2.Expr" [readExprConstr] exprDataType = mkDataType "PGF2.Expr" [readExprConstr]
-- | Constructs an expression by lambda abstraction -- | Constructs an expression by lambda abstraction
mkAbs :: BindType -> String -> Expr -> Expr mkAbs :: BindType -> CId -> Expr -> Expr
mkAbs bind_type var (Expr body bodyTouch) = mkAbs bind_type var (Expr body bodyTouch) =
unsafePerformIO $ do unsafePerformIO $ do
exprPl <- gu_new_pool exprPl <- gu_new_pool
@@ -68,7 +74,7 @@ mkAbs bind_type var (Expr body bodyTouch) =
Implicit -> (#const PGF_BIND_TYPE_IMPLICIT) Implicit -> (#const PGF_BIND_TYPE_IMPLICIT)
-- | Decomposes an expression into an abstraction and a body -- | Decomposes an expression into an abstraction and a body
unAbs :: Expr -> Maybe (BindType, String, Expr) unAbs :: Expr -> Maybe (BindType, CId, Expr)
unAbs (Expr expr touch) = unAbs (Expr expr touch) =
unsafePerformIO $ do unsafePerformIO $ do
c_abs <- pgf_expr_unabs expr c_abs <- pgf_expr_unabs expr
@@ -113,17 +119,6 @@ unApp (Expr expr touch) =
c_args <- peekArray (fromIntegral arity) (appl `plusPtr` (#offset PgfApplication, args)) c_args <- peekArray (fromIntegral arity) (appl `plusPtr` (#offset PgfApplication, args))
return $ Just (fun, [Expr c_arg touch | c_arg <- c_args]) return $ Just (fun, [Expr c_arg touch | c_arg <- c_args])
-- | Decomposes an expression into an application of a function
unapply :: Expr -> (Expr,[Expr])
unapply (Expr expr touch) =
unsafePerformIO $
withGuPool $ \pl -> do
appl <- pgf_expr_unapply_ex expr pl
efun <- (#peek PgfApplication, efun) appl
arity <- (#peek PgfApplication, n_args) appl :: IO CInt
c_args <- peekArray (fromIntegral arity) (appl `plusPtr` (#offset PgfApplication, args))
return (Expr efun touch, [Expr c_arg touch | c_arg <- c_args])
-- | Constructs an expression from a string literal -- | Constructs an expression from a string literal
mkStr :: String -> Expr mkStr :: String -> Expr
mkStr str = mkStr str =
@@ -207,6 +202,9 @@ unMeta (Expr expr touch) =
touch touch
return (Just (fromIntegral (id :: CInt))) return (Just (fromIntegral (id :: CInt)))
-- | this functions is only for backward compatibility with the old Haskell runtime
mkCId x = x
-- | parses a 'String' as an expression -- | parses a 'String' as an expression
readExpr :: String -> Maybe Expr readExpr :: String -> Maybe Expr
readExpr str = readExpr str =
@@ -224,22 +222,6 @@ readExpr str =
else do gu_pool_free exprPl else do gu_pool_free exprPl
return Nothing return Nothing
pIdent :: ReadS String
pIdent str =
unsafePerformIO $
withGuPool $ \tmpPl ->
do ref <- newIORef (str,str,str)
exn <- gu_new_exn tmpPl
c_fetch_char <- wrapParserGetc (fetch_char ref)
c_parser <- pgf_new_parser nullPtr c_fetch_char tmpPl tmpPl exn
c_ident <- pgf_expr_parser_ident c_parser
status <- gu_exn_is_raised exn
if (not status && c_ident /= nullPtr)
then do ident <- peekUtf8CString c_ident
(str,_,_) <- readIORef ref
return [(ident,str)]
else do return []
pExpr :: ReadS Expr pExpr :: ReadS Expr
pExpr str = pExpr str =
unsafePerformIO $ unsafePerformIO $
@@ -257,9 +239,9 @@ pExpr str =
return [(Expr c_expr (touchForeignPtr exprFPl),str)] return [(Expr c_expr (touchForeignPtr exprFPl),str)]
else do gu_pool_free exprPl else do gu_pool_free exprPl
return [] return []
where
fetch_char :: IORef (String,String,String) -> Ptr () -> (#type bool) -> Ptr GuExn -> IO (#type GuUCS) fetch_char :: IORef (String,String,String) -> Ptr () -> (#type bool) -> Ptr GuExn -> IO (#type GuUCS)
fetch_char ref _ mark exn = do fetch_char ref _ mark exn = do
(str1,str2,str3) <- readIORef ref (str1,str2,str3) <- readIORef ref
let str1' = if mark /= 0 let str1' = if mark /= 0
then str2 then str2
@@ -277,20 +259,16 @@ foreign import ccall "pgf/expr.h pgf_new_parser"
foreign import ccall "pgf/expr.h pgf_expr_parser_expr" foreign import ccall "pgf/expr.h pgf_expr_parser_expr"
pgf_expr_parser_expr :: Ptr PgfExprParser -> (#type bool) -> IO PgfExpr pgf_expr_parser_expr :: Ptr PgfExprParser -> (#type bool) -> IO PgfExpr
foreign import ccall "pgf/expr.h pgf_expr_parser_ident"
pgf_expr_parser_ident :: Ptr PgfExprParser -> IO CString
type ParserGetc = Ptr () -> (#type bool) -> Ptr GuExn -> IO (#type GuUCS) type ParserGetc = Ptr () -> (#type bool) -> Ptr GuExn -> IO (#type GuUCS)
foreign import ccall "wrapper" foreign import ccall "wrapper"
wrapParserGetc :: ParserGetc -> IO (FunPtr ParserGetc) wrapParserGetc :: ParserGetc -> IO (FunPtr ParserGetc)
-- | renders an expression as a 'String'. The list -- | renders an expression as a 'String'. The list
-- of identifiers is the list of all free variables -- of identifiers is the list of all free variables
-- in the expression in order reverse to the order -- in the expression in order reverse to the order
-- of binding. -- of binding.
showExpr :: [String] -> Expr -> String showExpr :: [CId] -> Expr -> String
showExpr scope e = showExpr scope e =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->

View File

@@ -16,13 +16,12 @@ import Control.Exception
import GHC.Ptr import GHC.Ptr
import Data.Int import Data.Int
import Data.Word import Data.Word
import qualified Data.Map as Map
type Touch = IO () type Touch = IO ()
-- | An abstract data type representing multilingual grammar -- | An abstract data type representing multilingual grammar
-- in Portable Grammar Format. -- in Portable Grammar Format.
data PGF = PGF {pgf :: Ptr PgfPGF, langs :: Map.Map String Concr, touchPGF :: Touch} data PGF = PGF {pgf :: Ptr PgfPGF, touchPGF :: Touch}
data Concr = Concr {concr :: Ptr PgfConcr, touchConcr :: Touch} data Concr = Concr {concr :: Ptr PgfConcr, touchConcr :: Touch}
------------------------------------------------------------------ ------------------------------------------------------------------
@@ -34,6 +33,7 @@ data GuIn
data GuOut data GuOut
data GuKind data GuKind
data GuType data GuType
data GuString
data GuStringBuf data GuStringBuf
data GuMap data GuMap
data GuMapItor data GuMapItor
@@ -239,7 +239,7 @@ newSequence elem_size pokeElem values pool = do
pokeElems (ptr `plusPtr` (fromIntegral elem_size)) xs pokeElems (ptr `plusPtr` (fromIntegral elem_size)) xs
type FId = Int type FId = Int
data PArg = PArg [(FId,FId)] {-# UNPACK #-} !FId deriving (Eq,Ord,Show) data PArg = PArg [FId] {-# UNPACK #-} !FId deriving (Eq,Ord,Show)
peekFId :: Ptr a -> IO FId peekFId :: Ptr a -> IO FId
peekFId c_ccat = do peekFId c_ccat = do
@@ -279,13 +279,7 @@ foreign import ccall "pgf/pgf.h pgf_read"
pgf_read :: CString -> Ptr GuPool -> Ptr GuExn -> IO (Ptr PgfPGF) pgf_read :: CString -> Ptr GuPool -> Ptr GuExn -> IO (Ptr PgfPGF)
foreign import ccall "pgf/pgf.h pgf_write" foreign import ccall "pgf/pgf.h pgf_write"
pgf_write :: Ptr PgfPGF -> CSizeT -> Ptr (Ptr PgfConcr) -> CString -> Ptr GuExn -> IO () pgf_write :: Ptr PgfPGF -> CString -> Ptr GuExn -> IO ()
foreign import ccall "pgf/writer.h pgf_concrete_save"
pgf_concrete_save :: Ptr PgfConcr -> CString -> Ptr GuExn -> IO ()
foreign import ccall "pgf/pgf.h pgf_have_same_abstract"
pgf_have_same_abstract :: Ptr PgfPGF -> Ptr PgfPGF -> (#type bool)
foreign import ccall "pgf/pgf.h pgf_abstract_name" foreign import ccall "pgf/pgf.h pgf_abstract_name"
pgf_abstract_name :: Ptr PgfPGF -> IO CString pgf_abstract_name :: Ptr PgfPGF -> IO CString
@@ -311,9 +305,6 @@ foreign import ccall "pgf/pgf.h pgf_language_code"
foreign import ccall "pgf/pgf.h pgf_iter_categories" foreign import ccall "pgf/pgf.h pgf_iter_categories"
pgf_iter_categories :: Ptr PgfPGF -> Ptr GuMapItor -> Ptr GuExn -> IO () pgf_iter_categories :: Ptr PgfPGF -> Ptr GuMapItor -> Ptr GuExn -> IO ()
foreign import ccall "pgf/pgf.h pgf_concrete_fix_internals"
pgf_concrete_fix_internals :: Ptr PgfConcr -> IO ()
foreign import ccall "pgf/pgf.h pgf_start_cat" foreign import ccall "pgf/pgf.h pgf_start_cat"
pgf_start_cat :: Ptr PgfPGF -> Ptr GuPool -> IO PgfType pgf_start_cat :: Ptr PgfPGF -> Ptr GuPool -> IO PgfType
@@ -466,9 +457,6 @@ foreign import ccall "pgf/pgf.h pgf_expr_apply"
foreign import ccall "pgf/pgf.h pgf_expr_unapply" foreign import ccall "pgf/pgf.h pgf_expr_unapply"
pgf_expr_unapply :: PgfExpr -> Ptr GuPool -> IO (Ptr PgfApplication) pgf_expr_unapply :: PgfExpr -> Ptr GuPool -> IO (Ptr PgfApplication)
foreign import ccall "pgf/pgf.h pgf_expr_unapply_ex"
pgf_expr_unapply_ex :: PgfExpr -> Ptr GuPool -> IO (Ptr PgfApplication)
foreign import ccall "pgf/pgf.h pgf_expr_abs" foreign import ccall "pgf/pgf.h pgf_expr_abs"
pgf_expr_abs :: PgfBindType -> CString -> PgfExpr -> Ptr GuPool -> IO PgfExpr pgf_expr_abs :: PgfBindType -> CString -> PgfExpr -> Ptr GuPool -> IO PgfExpr
@@ -493,12 +481,12 @@ foreign import ccall "pgf/pgf.h pgf_expr_float"
foreign import ccall "pgf/pgf.h pgf_expr_unlit" foreign import ccall "pgf/pgf.h pgf_expr_unlit"
pgf_expr_unlit :: PgfExpr -> CInt -> IO (Ptr a) pgf_expr_unlit :: PgfExpr -> CInt -> IO (Ptr a)
foreign import ccall "pgf/expr.h pgf_expr_arity"
pgf_expr_arity :: PgfExpr -> IO CInt
foreign import ccall "pgf/expr.h pgf_expr_eq" foreign import ccall "pgf/expr.h pgf_expr_eq"
pgf_expr_eq :: PgfExpr -> PgfExpr -> IO CInt pgf_expr_eq :: PgfExpr -> PgfExpr -> IO CInt
foreign import ccall "pgf/expr.h pgf_type_eq"
pgf_type_eq :: PgfType -> PgfType -> IO (#type bool)
foreign import ccall "pgf/expr.h pgf_expr_hash" foreign import ccall "pgf/expr.h pgf_expr_hash"
pgf_expr_hash :: GuHash -> PgfExpr -> IO GuHash pgf_expr_hash :: GuHash -> PgfExpr -> IO GuHash
@@ -539,7 +527,7 @@ foreign import ccall "pgf/pgf.h pgf_generate_all"
pgf_generate_all :: Ptr PgfPGF -> PgfType -> Ptr GuExn -> Ptr GuPool -> Ptr GuPool -> IO (Ptr GuEnum) pgf_generate_all :: Ptr PgfPGF -> PgfType -> Ptr GuExn -> Ptr GuPool -> Ptr GuPool -> IO (Ptr GuEnum)
foreign import ccall "pgf/pgf.h pgf_print" foreign import ccall "pgf/pgf.h pgf_print"
pgf_print :: Ptr PgfPGF -> CSizeT -> Ptr (Ptr PgfConcr) -> Ptr GuOut -> Ptr GuExn -> IO () pgf_print :: Ptr PgfPGF -> Ptr GuOut -> Ptr GuExn -> IO ()
foreign import ccall "pgf/expr.h pgf_read_expr" foreign import ccall "pgf/expr.h pgf_read_expr"
pgf_read_expr :: Ptr GuIn -> Ptr GuPool -> Ptr GuPool -> Ptr GuExn -> IO PgfExpr pgf_read_expr :: Ptr GuIn -> Ptr GuPool -> Ptr GuPool -> Ptr GuExn -> IO PgfExpr

View File

@@ -2,28 +2,21 @@
module PGF2.Internal(-- * Access the internal structures module PGF2.Internal(-- * Access the internal structures
FId,isPredefFId, FId,isPredefFId,
FunId,SeqId,LIndex,Token,Production(..),PArg(..),Symbol(..),Literal(..), FunId,Token,Production(..),PArg(..),Symbol(..),Literal(..),
globalFlags, abstrFlags, concrFlags, globalFlags, abstrFlags, concrFlags,
concrTotalCats, concrCategories, concrProductions, concrTotalCats, concrCategories, concrProductions,
concrTotalFuns, concrFunction, concrTotalFuns, concrFunction,
concrTotalSeqs, concrSequence, concrTotalSeqs, concrSequence,
-- * Byte code
CodeLabel, Instr(..), IVal(..), TailInfo(..),
-- * Building new PGFs in memory -- * Building new PGFs in memory
build, Builder, B, build, eAbs, eApp, eMeta, eFun, eVar, eTyped, eImplArg, dTyp, hypo,
eAbs, eApp, eMeta, eFun, eVar, eLit, eTyped, eImplArg, dTyp, hypo,
AbstrInfo, newAbstr, ConcrInfo, newConcr, newPGF, AbstrInfo, newAbstr, ConcrInfo, newConcr, newPGF,
-- * Expose PGF and Concr for FFI with C -- * Expose PGF and Concr for FFI with C
PGF(..), Concr(..), PGF(..), Concr(..),
-- * Write an in-memory PGF to a file -- * Write an in-memory PGF to a file
unionPGF, writePGF, writeConcr, writePGF
-- * Predefined concrete categories
fidString, fidInt, fidFloat, fidVar, fidStart
) where ) where
#include <pgf/data.h> #include <pgf/data.h>
@@ -39,7 +32,7 @@ import Data.IORef
import Data.Maybe(fromMaybe) import Data.Maybe(fromMaybe)
import Data.List(sortBy) import Data.List(sortBy)
import Control.Exception(Exception,throwIO) import Control.Exception(Exception,throwIO)
import Control.Monad(foldM,when) import Control.Monad(foldM)
import qualified Data.Map as Map import qualified Data.Map as Map
type Token = String type Token = String
@@ -61,7 +54,6 @@ data Production
= PApply {-# UNPACK #-} !FunId [PArg] = PApply {-# UNPACK #-} !FunId [PArg]
| PCoerce {-# UNPACK #-} !FId | PCoerce {-# UNPACK #-} !FId
deriving (Eq,Ord,Show) deriving (Eq,Ord,Show)
type FunId = Int type FunId = Int
type SeqId = Int type SeqId = Int
data Literal = data Literal =
@@ -70,42 +62,6 @@ data Literal =
| LFlt Double -- ^ a floating point constant | LFlt Double -- ^ a floating point constant
deriving (Eq,Ord,Show) deriving (Eq,Ord,Show)
type CodeLabel = Int
data Instr
= CHECK_ARGS {-# UNPACK #-} !Int
| CASE Fun {-# UNPACK #-} !CodeLabel
| CASE_LIT Literal {-# UNPACK #-} !CodeLabel
| SAVE {-# UNPACK #-} !Int
| ALLOC {-# UNPACK #-} !Int
| PUT_CONSTR Fun
| PUT_CLOSURE {-# UNPACK #-} !CodeLabel
| PUT_LIT Literal
| SET IVal
| SET_PAD
| PUSH_FRAME
| PUSH IVal
| TUCK IVal {-# UNPACK #-} !Int
| EVAL IVal TailInfo
| DROP {-# UNPACK #-} !Int
| JUMP {-# UNPACK #-} !CodeLabel
| FAIL
| PUSH_ACCUM Literal
| POP_ACCUM
| ADD
data IVal
= HEAP {-# UNPACK #-} !Int
| ARG_VAR {-# UNPACK #-} !Int
| FREE_VAR {-# UNPACK #-} !Int
| GLOBAL Fun
deriving Eq
data TailInfo
= RecCall
| TailCall {-# UNPACK #-} !Int
| UpdateCall
----------------------------------------------------------------------- -----------------------------------------------------------------------
-- Access the internal structures -- Access the internal structures
@@ -228,7 +184,7 @@ concrProductions c fid = unsafePerformIO $ do
hypos <- peekSequence (deRef peekFId) (#size int) c_hypos hypos <- peekSequence (deRef peekFId) (#size int) c_hypos
c_ccat <- (#peek PgfPArg, ccat) ptr c_ccat <- (#peek PgfPArg, ccat) ptr
fid <- peekFId c_ccat fid <- peekFId c_ccat
return (PArg [(fid,fid) | fid <- hypos] fid) return (PArg hypos fid)
concrTotalFuns :: Concr -> FunId concrTotalFuns :: Concr -> FunId
concrTotalFuns c = unsafePerformIO $ do concrTotalFuns c = unsafePerformIO $ do
@@ -240,9 +196,6 @@ concrTotalFuns c = unsafePerformIO $ do
concrFunction :: Concr -> FunId -> (Fun,[SeqId]) concrFunction :: Concr -> FunId -> (Fun,[SeqId])
concrFunction c funid = unsafePerformIO $ do concrFunction c funid = unsafePerformIO $ do
c_cncfuns <- (#peek PgfConcr, cncfuns) (concr c) c_cncfuns <- (#peek PgfConcr, cncfuns) (concr c)
c_len <- (#peek GuSeq, len) c_cncfuns
when (funid >= fromIntegral (c_len :: CSizeT)) $
throwIO (PGFError ("Invalid concrete function: F"++show funid))
c_cncfun <- peek (c_cncfuns `plusPtr` ((#offset GuSeq, data)+funid*(#size PgfCncFun*))) c_cncfun <- peek (c_cncfuns `plusPtr` ((#offset GuSeq, data)+funid*(#size PgfCncFun*)))
c_absfun <- (#peek PgfCncFun, absfun) c_cncfun c_absfun <- (#peek PgfCncFun, absfun) c_cncfun
c_name <- (#peek PgfAbsFun, name) c_absfun c_name <- (#peek PgfAbsFun, name) c_absfun
@@ -266,9 +219,6 @@ concrTotalSeqs c = unsafePerformIO $ do
concrSequence :: Concr -> SeqId -> [Symbol] concrSequence :: Concr -> SeqId -> [Symbol]
concrSequence c seqid = unsafePerformIO $ do concrSequence c seqid = unsafePerformIO $ do
c_sequences <- (#peek PgfConcr, sequences) (concr c) c_sequences <- (#peek PgfConcr, sequences) (concr c)
c_len <- (#peek GuSeq, len) c_sequences
when (seqid >= fromIntegral (c_len :: CSizeT)) $
throwIO (PGFError ("Invalid concrete sequence: S"++show seqid))
let c_sequence = c_sequences `plusPtr` ((#offset GuSeq, data)+seqid*(#size PgfSequence)) let c_sequence = c_sequences `plusPtr` ((#offset GuSeq, data)+seqid*(#size PgfSequence))
c_syms <- (#peek PgfSequence, syms) c_sequence c_syms <- (#peek PgfSequence, syms) c_sequence
res <- peekSequence (deRef peekSymbol) (#size GuVariant) c_syms res <- peekSequence (deRef peekSymbol) (#size GuVariant) c_syms
@@ -335,9 +285,6 @@ isPredefFId = (`elem` [fidString, fidInt, fidFloat, fidVar])
data Builder s = Builder (Ptr GuPool) Touch data Builder s = Builder (Ptr GuPool) Touch
newtype B s a = B a newtype B s a = B a
instance Functor (B s) where
fmap f (B x) = B (f x)
build :: (forall s . (?builder :: Builder s) => B s a) -> a build :: (forall s . (?builder :: Builder s) => B s a) -> a
build f = build f =
unsafePerformIO $ do unsafePerformIO $ do
@@ -426,21 +373,6 @@ eVar var =
where where
(Builder pool touch) = ?builder (Builder pool touch) = ?builder
eLit :: (?builder :: Builder s) => Literal -> B s Expr
eLit value =
unsafePerformIO $
alloca $ \pptr -> do
ptr <- gu_alloc_variant (#const PGF_EXPR_LIT)
(fromIntegral (#size PgfExprLit))
(#const gu_alignof(PgfExprLit))
pptr pool
c_value <- newLiteral value pool
(#poke PgfExprLit, lit) ptr c_value
e <- peek pptr
return (B (Expr e touch))
where
(Builder pool touch) = ?builder
eTyped :: (?builder :: Builder s) => B s Expr -> B s Type -> B s Expr eTyped :: (?builder :: Builder s) => B s Expr -> B s Type -> B s Expr
eTyped (B (Expr e _)) (B (Type ty _)) = eTyped (B (Expr e _)) (B (Type ty _)) =
unsafePerformIO $ unsafePerformIO $
@@ -470,7 +402,7 @@ eImplArg (B (Expr e _)) =
where where
(Builder pool touch) = ?builder (Builder pool touch) = ?builder
hypo :: BindType -> String -> B s Type -> (B s Hypo) hypo :: BindType -> CId -> B s Type -> (B s Hypo)
hypo bind_type var (B ty) = B (bind_type,var,ty) hypo bind_type var (B ty) = B (bind_type,var,ty)
dTyp :: (?builder :: Builder s) => [B s Hypo] -> Cat -> [B s Expr] -> B s Type dTyp :: (?builder :: Builder s) => [B s Hypo] -> Cat -> [B s Expr] -> B s Type
@@ -514,15 +446,15 @@ data AbstrInfo = AbstrInfo (Ptr GuSeq) (Ptr GuSeq) (Map.Map String (Ptr PgfAbsCa
newAbstr :: (?builder :: Builder s) => [(String,Literal)] -> newAbstr :: (?builder :: Builder s) => [(String,Literal)] ->
[(Cat,[B s Hypo],Float)] -> [(Cat,[B s Hypo],Float)] ->
[(Fun,B s Type,Int,[[Instr]],Float)] -> [(Fun,B s Type,Int,Float)] ->
B s AbstrInfo AbstrInfo
newAbstr aflags cats funs = unsafePerformIO $ do newAbstr aflags cats funs = unsafePerformIO $ do
c_aflags <- newFlags aflags pool c_aflags <- newFlags aflags pool
(c_cats,abscats) <- newAbsCats (sortByFst3 cats) pool (c_cats,abscats) <- newAbsCats (sortByFst3 cats) pool
(c_funs,absfuns) <- newAbsFuns (sortByFst5 funs) pool (c_funs,absfuns) <- newAbsFuns (sortByFst4 funs) pool
c_abs_lin_fun <- newAbsLinFun c_abs_lin_fun <- newAbsLinFun
c_non_lexical_buf <- gu_make_buf (#size PgfProductionIdxEntry) pool c_non_lexical_buf <- gu_make_buf (#size PgfProductionIdxEntry) pool
return (B (AbstrInfo c_aflags c_cats abscats c_funs absfuns c_abs_lin_fun c_non_lexical_buf touch)) return (AbstrInfo c_aflags c_cats abscats c_funs absfuns c_abs_lin_fun c_non_lexical_buf touch)
where where
(Builder pool touch) = ?builder (Builder pool touch) = ?builder
@@ -554,7 +486,7 @@ newAbstr aflags cats funs = unsafePerformIO $ do
absfuns <- pokeAbsFun ptr absfuns x absfuns <- pokeAbsFun ptr absfuns x
pokeElems (ptr `plusPtr` (#size PgfAbsFun)) absfuns xs pokeElems (ptr `plusPtr` (#size PgfAbsFun)) absfuns xs
pokeAbsFun ptr absfuns (name,B (Type c_ty _),arity,_,prob) = do pokeAbsFun ptr absfuns (name,B (Type c_ty _),arity,prob) = do
pfun <- gu_alloc_variant (#const PGF_EXPR_FUN) pfun <- gu_alloc_variant (#const PGF_EXPR_FUN)
(fromIntegral ((#size PgfExprFun)+utf8Length name)) (fromIntegral ((#size PgfExprFun)+utf8Length name))
(#const gu_flex_alignof(PgfExprFun)) (#const gu_flex_alignof(PgfExprFun))
@@ -590,7 +522,7 @@ newAbstr aflags cats funs = unsafePerformIO $ do
data ConcrInfo = ConcrInfo (Ptr GuSeq) (Ptr GuMap) (Ptr GuMap) (Ptr GuSeq) (Ptr GuSeq) (Ptr GuMap) (Ptr PgfConcr -> Ptr GuPool -> IO ()) CInt data ConcrInfo = ConcrInfo (Ptr GuSeq) (Ptr GuMap) (Ptr GuMap) (Ptr GuSeq) (Ptr GuSeq) (Ptr GuMap) (Ptr PgfConcr -> Ptr GuPool -> IO ()) CInt
newConcr :: (?builder :: Builder s) => B s AbstrInfo newConcr :: (?builder :: Builder s) => AbstrInfo
-> [(String,Literal)] -- ^ Concrete syntax flags -> [(String,Literal)] -- ^ Concrete syntax flags
-> [(String,String)] -- ^ Printnames -> [(String,String)] -- ^ Printnames
-> [(FId,[FunId])] -- ^ Lindefs -> [(FId,[FunId])] -- ^ Lindefs
@@ -600,8 +532,8 @@ newConcr :: (?builder :: Builder s) => B s AbstrInfo
-> [[Symbol]] -- ^ Sequences (must be sorted) -> [[Symbol]] -- ^ Sequences (must be sorted)
-> [(Cat,FId,FId,[String])] -- ^ Concrete categories -> [(Cat,FId,FId,[String])] -- ^ Concrete categories
-> FId -- ^ The total count of the categories -> FId -- ^ The total count of the categories
-> B s ConcrInfo -> ConcrInfo
newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)) cflags printnames lindefs linrefs prods cncfuns sequences cnccats total_cats = unsafePerformIO $ do newConcr (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _) cflags printnames lindefs linrefs prods cncfuns sequences cnccats total_cats = unsafePerformIO $ do
c_cflags <- newFlags cflags pool c_cflags <- newFlags cflags pool
c_printname <- newMap (#size GuString) gu_string_hasher newUtf8CString c_printname <- newMap (#size GuString) gu_string_hasher newUtf8CString
(#size GuString) (pokeString pool) (#size GuString) (pokeString pool)
@@ -618,12 +550,12 @@ newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)
mapM_ (addLinrefs c_ccats funs_ptr) linrefs mapM_ (addLinrefs c_ccats funs_ptr) linrefs
mk_index <- foldM (addProductions c_ccats funs_ptr c_non_lexical_buf) (\concr pool -> return ()) prods mk_index <- foldM (addProductions c_ccats funs_ptr c_non_lexical_buf) (\concr pool -> return ()) prods
c_cnccats <- newMap (#size GuString) gu_string_hasher newUtf8CString (#size PgfCncCat*) (pokeCncCat c_ccats) (map (\v@(k,_,_,_) -> (k,v)) cnccats) pool c_cnccats <- newMap (#size GuString) gu_string_hasher newUtf8CString (#size PgfCncCat*) (pokeCncCat c_ccats) (map (\v@(k,_,_,_) -> (k,v)) cnccats) pool
return (B (ConcrInfo c_cflags c_printname c_ccats c_cncfuns c_seqs c_cnccats mk_index (fromIntegral total_cats))) return (ConcrInfo c_cflags c_printname c_ccats c_cncfuns c_seqs c_cnccats mk_index (fromIntegral total_cats))
where where
(Builder pool touch) = ?builder (Builder pool touch) = ?builder
pokeCncFun seqs_ptr ptr cncfun@(funid,_) = do pokeCncFun seqs_ptr ptr cncfun = do
c_cncfun <- newCncFun absfuns seqs_ptr cncfun pool c_cncfun <- newCncFun absfuns nullPtr cncfun pool
poke ptr c_cncfun poke ptr c_cncfun
pokeSequence c_seq syms = do pokeSequence c_seq syms = do
@@ -648,9 +580,7 @@ newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)
(#poke PgfCCat, prods) c_ccat c_prods (#poke PgfCCat, prods) c_ccat c_prods
pokeProductions c_ccat (c_prods `plusPtr` (#offset GuSeq, data)) 0 (n_prods-1) mk_index prods pokeProductions c_ccat (c_prods `plusPtr` (#offset GuSeq, data)) 0 (n_prods-1) mk_index prods
where where
pokeProductions c_ccat ptr top bot mk_index [] = do pokeProductions c_ccat ptr top bot mk_index [] = return mk_index
(#poke PgfCCat, n_synprods) c_ccat (fromIntegral top :: CSizeT)
return mk_index
pokeProductions c_ccat ptr top bot mk_index (prod:prods) = do pokeProductions c_ccat ptr top bot mk_index (prod:prods) = do
(is_lexical,c_prod) <- newProduction c_ccats funs_ptr c_non_lexical_buf prod pool (is_lexical,c_prod) <- newProduction c_ccats funs_ptr c_non_lexical_buf prod pool
let mk_index' = \concr pool -> do pgf_parser_index concr c_ccat c_prod is_lexical pool let mk_index' = \concr pool -> do pgf_parser_index concr c_ccat c_prod is_lexical pool
@@ -663,7 +593,7 @@ newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)
pokeProductions c_ccat ptr top (bot-1) mk_index' prods pokeProductions c_ccat ptr top (bot-1) mk_index' prods
pokeRefDefFunId funs_ptr ptr funid = do pokeRefDefFunId funs_ptr ptr funid = do
c_fun <- peek (funs_ptr `plusPtr` (funid * (#size PgfCncFun*))) let c_fun = funs_ptr `plusPtr` (funid * (#size PgfCncFun))
(#poke PgfCncFun, absfun) c_fun c_abs_lin_fun (#poke PgfCncFun, absfun) c_fun c_abs_lin_fun
poke ptr c_fun poke ptr c_fun
@@ -675,15 +605,13 @@ newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)
case Map.lookup name abscats of case Map.lookup name abscats of
Just c_abscat -> (#poke PgfCncCat, abscat) c_cnccat c_abscat Just c_abscat -> (#poke PgfCncCat, abscat) c_cnccat c_abscat
Nothing -> throwIO (PGFError ("The category "++name++" is not in the abstract syntax")) Nothing -> throwIO (PGFError ("The category "++name++" is not in the abstract syntax"))
c_ccats <- newSequence (#size PgfCCat*) (pokeFId c_cnccat) [start..end] pool c_ccats <- newSequence (#size PgfCCat*) pokeFId [start..end] pool
(#poke PgfCncCat, cats) c_cnccat c_ccats (#poke PgfCncCat, cats) c_cnccat c_ccats
(#poke PgfCncCat, n_lins) c_cnccat n_lins
pokeLabels (c_cnccat `plusPtr` (#offset PgfCncCat, labels)) labels pokeLabels (c_cnccat `plusPtr` (#offset PgfCncCat, labels)) labels
poke ptr c_cnccat poke ptr c_cnccat
where where
pokeFId c_cnccat ptr fid = do pokeFId ptr fid = do
c_ccat <- getCCat c_ccats fid pool c_ccat <- getCCat c_ccats fid pool
(#poke PgfCCat, cnccat) c_ccat c_cnccat
poke ptr c_ccat poke ptr c_ccat
pokeLabels ptr [] = return [] pokeLabels ptr [] = return []
@@ -695,10 +623,10 @@ newConcr (B (AbstrInfo _ _ abscats _ absfuns c_abs_lin_fun c_non_lexical_buf _)
newPGF :: (?builder :: Builder s) => [(String,Literal)] -> newPGF :: (?builder :: Builder s) => [(String,Literal)] ->
AbsName -> AbsName ->
B s AbstrInfo -> AbstrInfo ->
[(ConcName,B s ConcrInfo)] -> [(ConcName,ConcrInfo)] ->
B s PGF B s PGF
newPGF gflags absname (B (AbstrInfo c_aflags c_cats _ c_funs _ c_abs_lin_fun _ _)) concrs = newPGF gflags absname (AbstrInfo c_aflags c_cats _ c_funs _ c_abs_lin_fun _ _) concrs =
unsafePerformIO $ do unsafePerformIO $ do
ptr <- gu_malloc_aligned pool ptr <- gu_malloc_aligned pool
(#size PgfPGF) (#size PgfPGF)
@@ -706,8 +634,7 @@ newPGF gflags absname (B (AbstrInfo c_aflags c_cats _ c_funs _ c_abs_lin_fun _ _
c_gflags <- newFlags gflags pool c_gflags <- newFlags gflags pool
c_absname <- newUtf8CString absname pool c_absname <- newUtf8CString absname pool
let c_abstr = ptr `plusPtr` (#offset PgfPGF, abstract) let c_abstr = ptr `plusPtr` (#offset PgfPGF, abstract)
c_concrs <- gu_make_seq (#size PgfConcr) (fromIntegral (length concrs)) pool c_concrs <- newSequence (#size PgfConcr) (pokeConcr c_abstr) concrs pool
langs <- pokeConcrs c_abstr (c_concrs `plusPtr` (#offset GuSeq, data)) Map.empty concrs
(#poke PgfPGF, major_version) ptr (2 :: (#type uint16_t)) (#poke PgfPGF, major_version) ptr (2 :: (#type uint16_t))
(#poke PgfPGF, minor_version) ptr (0 :: (#type uint16_t)) (#poke PgfPGF, minor_version) ptr (0 :: (#type uint16_t))
(#poke PgfPGF, gflags) ptr c_gflags (#poke PgfPGF, gflags) ptr c_gflags
@@ -718,18 +645,11 @@ newPGF gflags absname (B (AbstrInfo c_aflags c_cats _ c_funs _ c_abs_lin_fun _ _
(#poke PgfPGF, abstract.abs_lin_fun) ptr c_abs_lin_fun (#poke PgfPGF, abstract.abs_lin_fun) ptr c_abs_lin_fun
(#poke PgfPGF, concretes) ptr c_concrs (#poke PgfPGF, concretes) ptr c_concrs
(#poke PgfPGF, pool) ptr pool (#poke PgfPGF, pool) ptr pool
return (B (PGF ptr langs touch)) return (B (PGF ptr touch))
where where
(Builder pool touch) = ?builder (Builder pool touch) = ?builder
pokeConcrs c_abstr ptr langs [] = return langs pokeConcr c_abstr ptr (name, ConcrInfo c_cflags c_printnames c_ccats c_cncfuns c_seqs c_cnccats mk_index c_total_cats) = do
pokeConcrs c_abstr ptr langs ((name, B info):xs) = do
pokeConcr c_abstr ptr name info
pokeConcrs c_abstr (ptr `plusPtr` (fromIntegral (#size PgfConcr)))
(Map.insert name (Concr ptr touch) langs)
xs
pokeConcr c_abstr ptr name (ConcrInfo c_cflags c_printnames c_ccats c_cncfuns c_seqs c_cnccats mk_index c_total_cats) = do
c_name <- newUtf8CString name pool c_name <- newUtf8CString name pool
c_fun_indices <- gu_make_map (#size GuString) gu_string_hasher c_fun_indices <- gu_make_map (#size GuString) gu_string_hasher
(#size PgfCncOverloadMap*) gu_null_struct (#size PgfCncOverloadMap*) gu_null_struct
@@ -751,9 +671,7 @@ newPGF gflags absname (B (AbstrInfo c_aflags c_cats _ c_funs _ c_abs_lin_fun _ _
(#poke PgfConcr, cnccats) ptr c_cnccats (#poke PgfConcr, cnccats) ptr c_cnccats
(#poke PgfConcr, total_cats) ptr c_total_cats (#poke PgfConcr, total_cats) ptr c_total_cats
(#poke PgfConcr, pool) ptr nullPtr (#poke PgfConcr, pool) ptr nullPtr
mk_index ptr pool mk_index ptr pool
pgf_concrete_fix_internals ptr
newFlags :: [(String,Literal)] -> Ptr GuPool -> IO (Ptr GuSeq) newFlags :: [(String,Literal)] -> Ptr GuPool -> IO (Ptr GuSeq)
@@ -794,15 +712,15 @@ newLiteral (LFlt val) pool =
newProduction :: Ptr GuMap -> Ptr PgfCncFun -> Ptr GuBuf -> Production -> Ptr GuPool -> IO ((#type bool), GuVariant) newProduction :: Ptr GuMap -> Ptr PgfCncFun -> Ptr GuBuf -> Production -> Ptr GuPool -> IO ((#type bool), GuVariant)
newProduction c_ccats funs_ptr c_non_lexical_buf (PApply funid args) pool = newProduction c_ccats funs_ptr c_non_lexical_buf (PApply fun_id args) pool =
alloca $ \pptr -> do alloca $ \pptr -> do
c_fun <- peek (funs_ptr `plusPtr` (funid * (#size PgfCncFun*))) let c_fun = funs_ptr `plusPtr` (fun_id * (#size PgfCncFun))
c_args <- newSequence (#size PgfPArg) pokePArg args pool c_args <- newSequence (#size PgfPArg) pokePArg args pool
ptr <- gu_alloc_variant (#const PGF_PRODUCTION_APPLY) ptr <- gu_alloc_variant (#const PGF_PRODUCTION_APPLY)
(fromIntegral (#size PgfProductionApply)) (fromIntegral (#size PgfProductionApply))
(#const gu_alignof(PgfProductionApply)) (#const gu_alignof(PgfProductionApply))
pptr pool pptr pool
(#poke PgfProductionApply, fun) ptr (c_fun :: Ptr PgfCncFun) (#poke PgfProductionApply, fun) ptr c_fun
(#poke PgfProductionApply, args) ptr c_args (#poke PgfProductionApply, args) ptr c_args
is_lexical <- pgf_production_is_lexical ptr c_non_lexical_buf pool is_lexical <- pgf_production_is_lexical ptr c_non_lexical_buf pool
c_prod <- peek pptr c_prod <- peek pptr
@@ -811,7 +729,7 @@ newProduction c_ccats funs_ptr c_non_lexical_buf (PApply funid args) pool =
pokePArg ptr (PArg hypos ccat) = do pokePArg ptr (PArg hypos ccat) = do
c_ccat <- getCCat c_ccats ccat pool c_ccat <- getCCat c_ccats ccat pool
(#poke PgfPArg, ccat) ptr c_ccat (#poke PgfPArg, ccat) ptr c_ccat
c_hypos <- newSequence (#size PgfCCat*) pokeCCat (map snd hypos) pool c_hypos <- newSequence (#size PgfCCat*) pokeCCat hypos pool
(#poke PgfPArg, hypos) ptr c_hypos (#poke PgfPArg, hypos) ptr c_hypos
pokeCCat ptr ccat = do pokeCCat ptr ccat = do
@@ -986,18 +904,12 @@ newMap key_size hasher newKey elem_size pokeElem values pool = do
insert map values pool insert map values pool
unionPGF :: PGF -> PGF -> Maybe PGF
unionPGF one@(PGF ptr1 langs1 touch1) two@(PGF ptr2 langs2 touch2)
| pgf_have_same_abstract ptr1 ptr2 /= 0 = Just (PGF ptr1 (Map.union langs1 langs2) (touch1 >> touch2))
| otherwise = Nothing
writePGF :: FilePath -> PGF -> IO () writePGF :: FilePath -> PGF -> IO ()
writePGF fpath p = do writePGF fpath p = do
pool <- gu_new_pool pool <- gu_new_pool
exn <- gu_new_exn pool exn <- gu_new_exn pool
withArrayLen ((map concr . Map.elems . languages) p) $ \n_concrs concrs ->
withCString fpath $ \c_fpath -> withCString fpath $ \c_fpath ->
pgf_write (pgf p) (fromIntegral n_concrs) concrs c_fpath exn pgf_write (pgf p) c_fpath exn
touchPGF p touchPGF p
failed <- gu_exn_is_raised exn failed <- gu_exn_is_raised exn
if failed if failed
@@ -1012,26 +924,6 @@ writePGF fpath p = do
else do gu_pool_free pool else do gu_pool_free pool
return () return ()
writeConcr :: FilePath -> Concr -> IO ()
writeConcr fpath c = do
pool <- gu_new_pool
exn <- gu_new_exn pool
withCString fpath $ \c_fpath ->
pgf_concrete_save (concr c) c_fpath exn
touchConcr c
failed <- gu_exn_is_raised exn
if failed
then do is_errno <- gu_exn_caught exn gu_exn_type_GuErrno
if is_errno
then do perrno <- (#peek GuExn, data.data) exn
errno <- peek perrno
gu_pool_free pool
ioError (errnoToIOError "writeConcr" (Errno errno) Nothing (Just fpath))
else do gu_pool_free pool
throwIO (PGFError "The grammar cannot be stored")
else do gu_pool_free pool
return ()
sortByFst = sortBy (\(x,_) (y,_) -> compare x y) sortByFst = sortBy (\(x,_) (y,_) -> compare x y)
sortByFst3 = sortBy (\(x,_,_) (y,_,_) -> compare x y) sortByFst3 = sortBy (\(x,_,_) (y,_,_) -> compare x y)
sortByFst5 = sortBy (\(x,_,_,_,_) (y,_,_,_,_) -> compare x y) sortByFst4 = sortBy (\(x,_,_,_) (y,_,_,_) -> compare x y)

View File

@@ -17,18 +17,11 @@ import PGF2.FFI
data Type = Type {typ :: PgfExpr, touchType :: Touch} data Type = Type {typ :: PgfExpr, touchType :: Touch}
-- | 'Hypo' represents a hypothesis in a type i.e. in the type A -> B, A is the hypothesis -- | 'Hypo' represents a hypothesis in a type i.e. in the type A -> B, A is the hypothesis
type Hypo = (BindType,String,Type) type Hypo = (BindType,CId,Type)
instance Show Type where instance Show Type where
show = showType [] show = showType []
instance Eq Type where
(Type ty1 ty1_touch) == (Type ty2 ty2_touch) =
unsafePerformIO $ do
res <- pgf_type_eq ty1 ty2
ty1_touch >> ty2_touch
return (res /= 0)
-- | parses a 'String' as a type -- | parses a 'String' as a type
readType :: String -> Maybe Type readType :: String -> Maybe Type
readType str = readType str =
@@ -50,7 +43,7 @@ readType str =
-- of identifiers is the list of all free variables -- of identifiers is the list of all free variables
-- in the type in order reverse to the order -- in the type in order reverse to the order
-- of binding. -- of binding.
showType :: [String] -> Type -> String showType :: [CId] -> Type -> String
showType scope (Type ty touch) = showType scope (Type ty touch) =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->
@@ -66,7 +59,7 @@ showType scope (Type ty touch) =
-- a list of arguments for the category. The operation -- a list of arguments for the category. The operation
-- @mkType [h_1,...,h_n] C [e_1,...,e_m]@ will create -- @mkType [h_1,...,h_n] C [e_1,...,e_m]@ will create
-- @h_1 -> ... -> h_n -> C e_1 ... e_m@ -- @h_1 -> ... -> h_n -> C e_1 ... e_m@
mkType :: [Hypo] -> String -> [Expr] -> Type mkType :: [Hypo] -> CId -> [Expr] -> Type
mkType hypos cat exprs = unsafePerformIO $ do mkType hypos cat exprs = unsafePerformIO $ do
typPl <- gu_new_pool typPl <- gu_new_pool
let n_exprs = fromIntegral (length exprs) :: CSizeT let n_exprs = fromIntegral (length exprs) :: CSizeT
@@ -101,7 +94,7 @@ touchHypo (_,_,ty) = touchType ty
-- | Decomposes a type into a list of hypothesises, a category and -- | Decomposes a type into a list of hypothesises, a category and
-- a list of arguments for the category. -- a list of arguments for the category.
unType :: Type -> ([Hypo],String,[Expr]) unType :: Type -> ([Hypo],CId,[Expr])
unType (Type c_type touch) = unsafePerformIO $ do unType (Type c_type touch) = unsafePerformIO $ do
cid <- (#peek PgfType, cid) c_type >>= peekUtf8CString cid <- (#peek PgfType, cid) c_type >>= peekUtf8CString
c_hypos <- (#peek PgfType, hypos) c_type c_hypos <- (#peek PgfType, hypos) c_type
@@ -134,7 +127,7 @@ unType (Type c_type touch) = unsafePerformIO $ do
-- of identifiers is the list of all free variables -- of identifiers is the list of all free variables
-- in the type in order reverse to the order -- in the type in order reverse to the order
-- of binding. -- of binding.
showContext :: [String] -> [Hypo] -> String showContext :: [CId] -> [Hypo] -> String
showContext scope hypos = showContext scope hypos =
unsafePerformIO $ unsafePerformIO $
withGuPool $ \tmpPl -> withGuPool $ \tmpPl ->

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