diff options
author | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
---|---|---|
committer | William Joye <wjoye@cfa.harvard.edu> | 2016-10-27 19:39:39 (GMT) |
commit | ea28451286d3ea4a772fa174483f9a7a66bb1ab3 (patch) | |
tree | 6ee9d8a7848333a7ceeee3b13d492e40225f8b86 /tcllib/modules/page | |
parent | b5ca09bae0d6a1edce939eea03594dd56383f2c8 (diff) | |
parent | 7c621da28f07e449ad90c387344f07a453927569 (diff) | |
download | blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.zip blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.gz blt-ea28451286d3ea4a772fa174483f9a7a66bb1ab3.tar.bz2 |
Merge commit '7c621da28f07e449ad90c387344f07a453927569' as 'tcllib'
Diffstat (limited to 'tcllib/modules/page')
61 files changed, 23434 insertions, 0 deletions
diff --git a/tcllib/modules/page/ChangeLog b/tcllib/modules/page/ChangeLog new file mode 100644 index 0000000..05b2585 --- /dev/null +++ b/tcllib/modules/page/ChangeLog @@ -0,0 +1,419 @@ +2013-02-01 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.15 ======================== + * + +2011-12-13 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.14 ======================== + * + +2011-11-10 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * support/installation/modules.tcl: [Bug 3425271], reported + * support/installation/actions.tcl: by Stuart Cassoff. Extended + * apps/page: the installer to install the .template files as + well. Extended auto_path in the application to find the standard + plugins in the installation. + +2011-01-24 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.13 ======================== + * + +2009-12-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.12 ======================== + * + +2008-12-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11.1 ======================== + * + +2008-10-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.11 ======================== + * + +2007-09-12 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.10 ======================== + * + +2007-08-31 Andreas Kupries <andreask@activestate.com> + + * page_util_norm_peg.man: New files. Two, for the remaining utility + * page_util_norm_lemon.man: packages. Lemon docs are partial. + + * page_util_peg.man: New files. Three more manpages, for most of + * page_util_quote.man: the utility packages. + * page_util_flow.man: + +2007-08-30 Andreas Kupries <andreask@activestate.com> + + * page_pluginmgr.man: First documentation for the packages, intro, + * page_intro.man: and the plugin management. + * pluginmgr.tcl: Fixed a typo in a comment. + +2007-06-22 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * pluginmgr.tcl: Replaced deprecated {expand} syntax in comments + with {*}. + +2007-03-28 Andreas Kupries <andreask@activestate.com> + + * apps/page: Added a block of meta data. + +2007-03-23 Andreas Kupries <andreask@activestate.com> + + * pkgIndex.tcl: Added MD hints. + +2007-03-21 Andreas Kupries <andreask@activestate.com> + + * pkgIndex.tcl: Fixed version mismatches, index vs. package. + * plugins/pkgIndex.tcl: + * plugins/writer_mecpu.tcl: + +2007-03-07 Andreas Kupries <andreask@activestate.com> + + * compiler_peg_mecpu.tcl: Fixed typo in name of required package + * pkgIndex.tcl: ('gasm;, was incorrectly 'gas'). Bumped + to version 0.1.1. + +2006-10-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.9 ======================== + * + +2006-09-19 Andreas Kupries <andreask@activestate.com> + + * pkgIndex.tcl: Bumped version to 0.2 + * pluginmgr.tcl: + +2006-06-30 Andreas Kupries <akupries@shaw.ca> + + * compiler_peg_mecpu.tcl: New packages generating ME + * gen_peg_mecpu.tcl: instructions for the CPU(core). + * gen_peg_mecpu.template: And page plugins using them. + * plugins/pkgIndex.tcl: + * plugins/transform_mecpu.tcl: + * plugins/writer_mecpu.tcl: + * pkgIndex.tcl: + + * pluginmgr.tcl: Extended the page plugin environment with + commands allowing a plugin to write files. Intended for the + debugging of plugins, i.e. the dumping of internal state. The + destination for such files however are restricted to the current + working directory and its sub-directories. Currently only the + MEcpu compiler package has code to use this, to write the + intermediary graphs and some statistics (Disabled through + comments however). + +2006-01-11 Andreas Kupries <andreask@activestate.com> + + * util_norm_peg.tcl: Inserted pragmas for the MDgen + * util_norm_lemon.tcl: application hinting that the + * gen_peg_me.tcl: pseudo-package 'page::plugin' + * analysis_peg_emodes.tcl: is not a true dependency. + * analysis_peg_minimize.tcl: + * analysis_peg_realizable.tcl: + * analysis_peg_reachable.tcl: + + * analysis_peg_minimize.tcl: Changed bad reference to 'useful' to + the correct string, 'realizable'. + +2005-10-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * + * Released and tagged Tcllib 1.8 ======================== + * + +2005-09-28 Andreas Kupries <andreask@activestate.com> + + * NOTES.txt: Renamed from NOTES. This file had the same name as a + directory, causing the Windows and OS X filesystem to trip badly + as they considered both identical. + +2005-09-27 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * plugins/config_peg.tcl: Fixed version inconsistency. + + * gen_peg_canon.tcl: Fixed frink warnings. + * analysis_peg_emodes.tcl (compute): Fixed inconsistency in return + values. + +2005-08-02 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * ../../apps/page.man: Updated the documentation to list the two + new plugins. + + * plugins/writer_identity.tcl: Additional reader and writer plugins. + * plugins/reader_treeser.tcl: The writer dumps the incoming data + as is, for inspection. The reader takes a tree serialization, + validates it as such and then simply passes this one. + + * util_peg.tcl: Bugfixes in the computation of symbol(Node) for + tree nodes. Wrong var name, and missing tree reference. Also + change of output to stderr to use the regular page logging + instead. + + * util_norm_peg.tcl: Extended the code flattening nested x and / + operators to transform x and / operators without children into + epsilon's. + + * gen_peg_ser.tcl: Fixed output going directly to stderr to go + directly through the regular page logging. + +2005-07-29 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * ../../examples/page: Renamed the directory examples/pg to the + proper name now in use. + + * modules/page/plugins/reader_lemon.tcl: Reworked feedback generation + * modules/page/plugins/reader_peg.tcl: to properly signal end of + * modules/page/plugins/reader_ser.tcl: reading, for good linebreaks + * modules/page/plugins/reader_hb.tcl: in the output. Added to the + * ../../apps/page: plugins, manager, and application. + * pluginmgr.tcl: + + * modules/page/plugins/transform_reach.tcl: Added generic feature + * modules/page/plugins/transform_use.tcl: query command to plugin + * modules/page/plugins/reader_lemon.tcl: manager and plugins. + * modules/page/plugins/writer_null.tcl: Added code to application, + * modules/page/plugins/writer_tree.tcl: manager and plugins to ask + * modules/page/plugins/writer_peg.tcl: for a feature 'timeable'. The + * modules/page/plugins/writer_ser.tcl: application will defer the + * modules/page/plugins/writer_tpc.tcl: collection of timing data to + * modules/page/plugins/writer_hb.tcl: the plugin if it can do so. + * modules/page/plugins/writer_me.tcl: Extended collection of timing + * modules/page/plugins/reader_peg.tcl: data from reader to all uses + * modules/page/plugins/reader_ser.tcl: of plugins. also a better + * modules/page/plugins/reader_hb.tcl: report at the end. All plugins + * ../../apps/page: are now timeable. Especially + * pluginmgr.tcl: the readers now provide much better data about the + number of characters they have read per second. + +2005-07-26 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * ../../apps/page.man: Completed the documentation of the 'page' + application. + + * ../../apps/page: Rewrote option processing to handle the changes + in the handling of configuration plugins and files. Also now + handling the changes when the default options are used. Better + error messages when plugins are not found. Corrected the + handling of -a, -p and their long equivalents. + + * pluginmgr.tcl: Rewritten the configuration loader to accept + files containing lists of options as well, possibly quoted using + double-quotes and quotes. + + * plugins/config_peg.tcl: ** New file ** + * pkgIndex.tcl: Put the predefined configuration "peg" aka "PEG + parser generator" into a plugin. + + +2005-07-19 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * A lot of changes. All packages have been renamed, i.e. shuffled + to different places in the namespace hierarchy. All have been + placed under "::page", and made more consistent. The files have + been moved around too, so that their names reflect the namespace + hierarchy as well. A customized pluginmgr has been written on + top of the general one, and the application has been rewritten + to use it. The existing parsers, code generators and + transformers have been put into proper plugins. + +2005-05-15 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * examples/pg/parse.y: Used the new lemon frontend to convert the + * examples/pg/sql.log: SQL grammar spec found in sqlite into an + * examples/pg/sql.peg: incomplete PEG spec from which we can + derive a complete spec (Removal of left + recursion, and completion/correction of the + lexical definitions). + + * examples/pg/lemon.html: Created a frontend for reading grammar + * examples/pg/lemon.peg: specifications written for the LEMON + * peg_grammar_lemon.tcl: parser generator by Richard Hipp. The + * peg_norm_lemon.tcl: PEG grammar spec is based on the docu- + * apps/pg: mentation and the SQL spec found in + sqlite3. Integrated the frontend into + the PG application. + + * peg_tpcserwriter.tcl: Added code to deal with grammars without + * peg_tpcphbwriter.tcl: start expression (as can be generated by + * peg_pegwriter.tcl: the usefulness transform). + * peg_mewriter.tcl: + * peg_writer.tcl: + * peg_emodes.tcl: + * peg_useful.tcl: + +2005-05-11 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_emodes.tcl: Fixed problem in gen() phase. A gen() switching + to no can force acc() to no as well. + + * apps/pg: Reworked PEG frontend code a bit to allow quick + switching between four variants of the PEG fronted (ME parser + vs. interpreted grammar, timed vs. untimed). + + * pkgIndex.tcl: + * peg_grammar_me.tcl: New implementation of PEG frontend. PEG + parser as generated by the ME backend, after the fixes done + yesterday (PG dogfooding / bootstrapping). + +2005-05-11 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_mewriter.tcl: Fixed a number of bugs (special char classes + generated bogus code, nonterminals and ! have to import status + variable when sub expression is terminal). Added (inactive) code + for insertion of logging code into the generated parser. + +2005-05-10 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * apps/pg: Removed two superfluous commands. Added readable + descriptions for the backends. Added 'null' backend which does + nothing. Added non-verbose char reader, disabled right now. + + * peg_mewriter.tcl: Completely rewritten, removed all templates + save the main one, all code composition via lists, completed + option, kleene and pos kleene code generator, added comments + containing the parsing expressions matched by a particular + command. + + * peg_quote.tcl: More forms of quoting characters. + +2005-05-07 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * doc_emodes.txt: Recorded some thought about the transformation + which make it not worth to implement right now, as we need an + LL(1) grammar, otherwise we ran into either cache incoherency or + exponential time. + + * peg_emodes.tcl: New transformation. Mode analysis. Computes + accept/generate data for all nodes from the given mode hints, + finds places where the mode can be made more strict than + specified. + + * pg_flow.tcl: Fixed bug in setup of flow when using a start set + of nodes. + + * doc_useful.txt: Rewritten to use the new flow management. Stores + * peg_useful.tcl: the results differently as well, easier to use + by the remove code. Updated the documentation of the transform + as well. + +2005-05-06 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * doc_reachable.tcl: + * peg_reachable.tcl: Rewritten to use the new flow + management. Stores the results differently as well, easier to + use by the remove code. Updated the documentation of the + transform as well. + + * pg_flow.tcl: New utility. Generic tree walking, for both topdown + and bottomup walks, and anything in between. Maintains the state + of nodes to visit, and the code executed per node determines + what other nodes to visit. + +2005-05-05 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_utilities.tcl: Bugfix, have to update user information of + symbols when nodes are removed, their definitions may have lost + callers. + + * peg_tpcser.tcl: A first, two frontends, reading grammars which + * peg_tpcphb.tcl: are either in halfbaked form, or a serialization. + + * peg_tpcphbwriter.tcl: Two new backends, writing the grammar out + * peg_tpcserwriter.tcl: as a halfbaked package (See peg_writer, + reduced to the Start and Define commands), and as serialization + of a PEG container. + + * peg_writer.tcl: Moved a number of generally useful + * peg_utilities.tcl: functionality into the utilities. + +2005-05-03 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_useful.tcl: Tweaked the definition of usefulness, better + based on the definition for CFG's. + +2005-05-02 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_pegwriter.tcl, peg_mewriter.tcl: Updated to changed NPEGT + definition (changed attribute names, node classification). + + * peg_useful.tcl: Bugfix, we delete the subtrees of all unuseful + nodes. No cutting, deleting. + + * treewriter.tcl: Back unquoted characters. Better readable. + +2005-04-29 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * peg_useful.tcl: Ditto for the usefulness analysis and + * doc_useful.txt: transform. Rewrote algorithm to use more proc + local state with quicker access, and easier checks. + + * peg_reachable.tcl: Documented reachable computation and + * doc_reachable.txt: transform, updated to the changes in the + Normalized PE Grammar Tree. + + * peg_utilities.tcl: New helper package. Common operations on the + tree. + +2005-04-28 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Reworked documentation of 'Raw PE Grammar AS Tree', as generated + by frontend, updated mengine to that. + + * Reworked documentation of normalization step, and updated the + transformation code to it. Note: The non-generic backends are + broken by this. + + * updated generic treewriter to quote attribute strings, as they + now may contain unprintable characters (the attributes carrying + lexemes). + +2005-04-27 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Added backend writing the input back in the same format, i.e. as + PEG. Bug fixes. Added option processing to the application to + switch between backends, and to select optimization levels. + + * Character de/encode put into separate package. Started backend + writing a recursive descent parser for a grammar, based on + mengine. + +2005-04-26 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Bug fixes. Added transformations to com;ute reachable and useful + parts of the grammar, for minimizing it. + +2005-04-23 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Restructured utility packages (renames), and updated the + backends. + +2005-04-18 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Completed backend for writing the grammar as package providing a + PEG container. Bug fixes. First internal docs. + +2005-04-16 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * Bugfixes, added a transformation normalizing the raw AST. + +2005-04-15 Andreas Kupries <andreas_kupries@users.sourceforge.net> + + * New module: Utility packages for the parser generator + application. diff --git a/tcllib/modules/page/NOTES.txt b/tcllib/modules/page/NOTES.txt new file mode 100644 index 0000000..030353a --- /dev/null +++ b/tcllib/modules/page/NOTES.txt @@ -0,0 +1,64 @@ +Transforms +========== + +Consider: character sequence to string fusing in PEG writer. +Consider: expression enumeration, to determine if there are common + expressions in the grammar which could be factored into + their own match procedures (*). + +Consider: Transformations which expand the number of common + expressions. Example would be strings, i.e. macthing of + character sequences. Instead of matching all in one use a + nested sequence of matching ever-growing prefixes. This + ensures that common prefixes in terminal strings are + factored into one matcher. And if we use nonterminal + procedures (See * below) this also enhances the caching, + especially if common prefixes occur in different branches of + a choice. + +(*) This could simple procedures, i.e. _not_ nonterminals. This would +be without caching. Could also be nonterminal procedures, with mode +expand (for value, discard would stay), to make its presence invisible +to the AS tree structure. + + +Removal of nonterminal chains + + A <- B + B <- C + C <- ... + +Static match results !! + +mewriter has to be able to work with and +without static match. Basic expr modes are +something mewriter should do on its own +as well. + + +Compile *, + with helper nonterminals which are not shown as such +(mode: expand). + +static match result - sequence - ability to remove checks after an +always ok call, and abort sequence upon always fail. + +static match result - choice - ability to abort choice after ok, or +skip always fail branches. + + +Main parse routine can be simplified if start expression is a single +nonterminal, and not a real complex expression. + + +Need encoder for printable tcl char string. + +- The basic encoder generates a string acceptable to tcl parser for + use in a script, as part of the code. + +- The new encoder has to generate a string acceptable to the tcl + parser, for use in a script, which then written (puts) generates a + human readable representation of the character. + +I.e. LF in basic encode is \n, when printed it is an invislble +character, i.e. a linefeed. In string/human encode it is \\n, which +prints as \n, making it a readable representation of the character diff --git a/tcllib/modules/page/analysis_peg_emodes.tcl b/tcllib/modules/page/analysis_peg_emodes.tcl new file mode 100644 index 0000000..a6b224e --- /dev/null +++ b/tcllib/modules/page/analysis_peg_emodes.tcl @@ -0,0 +1,458 @@ +# -*- tcl -*- +# ### ### ### ######### ######### ######### + +# Perform mode analysis (x) on the PE grammar delivered by the +# frontend. The grammar is in normalized form (*). +# +# (x) = See "doc_emodes.txt". +# and "doc_emodes_alg.txt". +# (*) = See "doc_normalize.txt". + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require page::util::flow ; # Dataflow walking. +package require page::util::peg ; # General utilities. +package require treeql + +namespace eval ::page::analysis::peg::emodes { + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::analysis::peg::emodes::compute {t} { + + # Ignore call if already done before + if {[$t keyexists root page::analysis::peg::emodes]} {return 1} + + # We do not actually compute per node a mode, but rather their + # gen'erate and acc'eptance properties, as described in + # "doc_emodes.txt". + + # Note: This implementation will not compute acc/gen information + # for unreachable nodes. + + # --- --- --- --------- --------- --------- + + array set acc {} ; # Per node X, acc(X), undefined if no element + array set call {} ; # Per definition node, number of users + array set cala {} ; # Per definition node, number of (non-)accepting users + + foreach {sym def} [$t get root definitions] { + set call($def) [llength [$t get $def users]] + set cala(0,$def) 0 + set cala(1,$def) 0 + } + + set acc(root) 1 ; # Sentinel for root of start expression. + + # --- --- --- --------- --------- --------- + + #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ + #puts stderr Node\tAcc\tNew\tWhat\tOp + #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ + + # A node is visited if its value for acc() is either undefined or + # may have changed. Basic flow is top down, from the start + # expression and a definition a child of its invokers. + + set gstart [$t get root start] + if {$gstart eq ""} { + page_error " No start expression, unable to compute accept/generate properties" + return 0 + } + + page::util::flow [list $gstart] flow n { + # Determine first or new value. + + #puts -nonewline stderr [string replace $n 1 3] + + if {![info exists acc($n)]} { + set a [Accepting $t $n acc call cala] + set acc($n) $a + set change 0 + + #puts -nonewline stderr \t-\t$a\t^ + } else { + set a [Accepting $t $n acc call cala] + set old $acc($n) + if {$a == $old} { + #puts stderr \t$old\t$a\t\ = + continue + } + set change 1 + set acc($n) $a + + #puts -nonewline stderr \t$old\t$a\t\ \ * + } + + # Update counters in definitions, if the node invokes them. + # Also, schedule the children for their (re)definition. + + if {[$t keyexists $n symbol]} { + #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode] + } else { + #puts -nonewline stderr \t[$t get $n op]\t\t + } + + if {[$t keyexists $n op] && ([$t get $n op] eq "n")} { + #puts -nonewline stderr ->\ [$t get $n sym] + set def [$t get $n def] + if {$def eq ""} continue + + if {$change} { + incr cala($old,$def) -1 + } + incr cala($a,$def) + $flow visit $def + + #puts -nonewline stderr @$def\t(0a$cala(0,$def),\ 1a$cala(1,$def),\ #$call($def))\tv($def) + #puts stderr "" + continue + } + + #puts stderr \t\t\t\tv([$t children $n]) + $flow visitl [$t children $n] + } + + # --- --- --- --------- --------- --------- + + array set gen {} ; # Per node X, gen(X), undefined if no element + array set nc {} ; # Per node, number of children + array set ng {} ; # Per node, number of (non-)generating children + + foreach n [$t nodes] { + set nc($n) [$t numchildren $n] + set ng(0,$n) 0 + set ng(1,$n) 0 + } + + # --- --- --- --------- --------- --------- + + #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ + #puts stderr Node\tGen\tNew\tWhat\tOp + #puts stderr ~~~~\t~~~\t~~~\t~~~\t~~~ + + # A node is visited if its value for gen() is either undefined or + # may have changed. Basic flow is bottom up, from the all + # leaves (and lookahead operators). Users of a definition are + # considered as its parents. + + set start [$t leaves] + set q [treeql q -tree $t] + q query tree withatt op ! over n {lappend start $n} + q query tree withatt op & over n {lappend start $n} + q destroy + + page::util::flow $start flow n { + # Ignore root. + + if {$n eq "root"} continue + + #puts -nonewline stderr [string replace $n 1 3] + + # Determine first or new value. + + if {![info exists gen($n)]} { + set g [Generating $t $n gen nc ng acc call cala] + set gen($n) $g + + #puts -nonewline stderr \t-\t$g\t^ + + } else { + set g [Generating $t $n gen nc ng acc call cala] + set old $gen($n) + if {$g eq $old} { + #puts stderr \t$old\t$g\t\ = + continue + } + set gen($n) $g + + #puts -nonewline stderr \t$old\t$g\t\ \ * + } + + if {($g ne "maybe") && !$g && $acc($n)} { + # No generate here implies that none of our children will + # generate anything either. So the current acceptance of + # these non-existing values can be safely forced to + # non-acceptance. + + set acc($n) 0 + #puts -nonewline stderr "-a" + } + + if {0} { + if {[$t keyexists $n symbol]} { + #puts -nonewline stderr \t\ DEF\t[$t get $n symbol]\t[$t get $n mode] + } else { + #puts -nonewline stderr \t[$t get $n op]\t\t + } + } + + #puts -nonewline stderr \t(0g$ng(0,$n),1g$ng(1,$n),\ #$nc($n)) + + # Update counters in the (virtual) parents, and schedule them + # for a visit. + + if {[$t keyexists $n symbol]} { + # Users are virtual parents. + + set users [$t get $n users] + $flow visitl $users + + if {$g ne "maybe"} { + foreach u $users {incr ng($g,$u)} + } + #puts stderr \tv($users) + continue + } + + set p [$t parent $n] + $flow visit $p + if {$g ne "maybe"} { + incr ng($g,$p) + } + + #puts stderr \tv($p) + } + + # --- --- --- --------- --------- --------- + + # Copy the calculated data over into the tree. + # Note: There will be no data for unreachable nodes. + + foreach n [$t nodes] { + if {$n eq "root"} continue + if {![info exists acc($n)]} continue + $t set $n acc $acc($n) + $t set $n gen $gen($n) + } + + # Recompute the modes based on the current + # acc/gen status of the definitions. + + #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ + #puts stderr Node\tSym\tMode\tNew\tGen\tAcc + #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ + + foreach {sym def} [$t get root definitions] { + set m {} + + set old [$t get $def mode] + + if {[info exists acc($def)]} { + switch -exact -- $gen($def)/$acc($def) { + 0/0 {set m discard} + 0/1 {error "Bad gen/acc for $sym"} + 1/0 {# don't touch (match, leaf)} + 1/1 {set m value} + maybe/0 {error "Bad gen/acc for $sym"} + maybe/1 {set m value} + } + if {$m ne ""} { + # Should check correctness of change, if any (We can drop + # to discard, nothing else). + $t set $def mode $m + } + #puts stderr [string replace $def 1 3]\t$sym\t$old\t[$t get $def mode]\t[$t get $def gen]\t[$t get $def acc] + } else { + #puts stderr [string replace $def 1 3]\t$sym\t$old\t\t\t\tNOT_REACHED + } + } + + #puts stderr ~~~~\t~~~\t~~~~\t~~~\t~~~\t~~~ + + # Wrap up the whole state and save it in the tree. No need to + # throw this away, useful for other mode based transforms and + # easier to get in this way than walking the tree again. + + $t set root page::analysis::peg::emodes [list \ + [array get acc] \ + [array get call] \ + [array get cala] \ + [array get gen] \ + [array get nc] \ + [array get ng]] + return 1 +} + +proc ::page::analysis::peg::emodes::reset {t} { + # Remove marker, allow recalculation of emodesness after changes. + + $t unset root page::analysis::peg::emodes + return +} + +# ### ### ### ######### ######### ######### +## Internal + +proc ::page::analysis::peg::emodes::Accepting {t n av cv cav} { + upvar 1 $av acc $cv call $cav cala + + # Definitions accept based on how they are called first, and on + # their mode if that is not possible. + + if {[$t keyexists $n symbol]} { + # Call based acceptance. + # !acc if all callers do not accept. + + if {$cala(0,$n) >= $call($n)} { + return 0 + } + + # Falling back to mode specific accptance + return [expr {([$t get $n mode] eq "value") ? 1 : 0}] + } + + set op [$t get $n op] + + # Lookahead operators will never accept. + + if {($op eq "!") || ($op eq "&")} { + return 0 + } + + # All other operators inherit the acceptance + # of their parent. + + return $acc([$t parent $n]) +} + +proc ::page::analysis::peg::emodes::Generating {t n gv ncv ngv av cv cav} { + upvar 1 $gv gen $ncv nc $ngv ng $av acc $cv call $cav cala + # ~~~ ~~ ~~ ~~~ ~~~~ ~~~~ + + # Definitions generate based on their mode, their defining + # expression, and the acceptance of their callers. + + if {[$t keyexists $n symbol]} { + + # If no caller accepts a value, then this definition will not + # generate one, even if its own mode asked it to do so. + + if {$cala(0,$n) >= $call($n)} { + return 0 + } + + # The definition has callers accepting values and callres not + # doing so. It will generate as per its own mode and defining + # expression. + + # The special modes know if they generate a value or not. + # The pass through mode looks at the expression for the + # information. + + switch -exact -- [$t get $n mode] { + value {return $gen([lindex [$t children $n] 0])} + match {return 1} + leaf {return 1} + discard {return 0} + } + error PANIC + } + + set op [$t get $n op] + + # Inner nodes generate based on operator and children. + + if {$nc($n)} { + switch -exact -- $op { + ! - & {return 0} + ? - * { + # No for all children --> no + # Otherwise --> maybe + + if {$ng(0,$n) >= $nc($n)} { + return 0 + } else { + return maybe + } + } + + - / - | { + # Yes for all children --> yes + # No for all children --> no + # Otherwise --> maybe + + if {$ng(1,$n) >= $nc($n)} { + return 1 + } elseif {$ng(0,$n) >= $nc($n)} { + return 0 + } else { + return maybe + } + } + x { + # Yes for some children --> yes + # No for all children --> no + # Otherwise --> maybe + + if {$ng(1,$n) > 0} { + return 1 + } elseif {$ng(0,$n) >= $nc($n)} { + return 0 + } else { + return maybe + } + } + } + error PANIC + } + + # Nonterminal leaves generate based on acceptance from their + # parent and the referenced definition. + + # As acc(X) == acc(parent(X)) the test doesn't have to go to the + # parent itself. + + if {$op eq "n"} { + if {[info exists acc($n)] && !$acc($n)} {return 0} + + set def [$t get $n def] + + # Undefine symbols do not generate anything. + if {$def eq ""} {return 0} + + # Inherit directly from the definition, if existing. + if {![info exists gen($def)]} { + return maybe + } + + return $gen($def) + } + + # Terminal leaves generate values if and only if such values are + # accepted by their parent. As acc(X) == acc(parent(X) the test + # doesn't have to go to the parent itself. + + + return $acc($n) +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::analysis::peg::emodes 0.1 diff --git a/tcllib/modules/page/analysis_peg_minimize.tcl b/tcllib/modules/page/analysis_peg_minimize.tcl new file mode 100644 index 0000000..84cf07e --- /dev/null +++ b/tcllib/modules/page/analysis_peg_minimize.tcl @@ -0,0 +1,51 @@ +# -*- tcl -*- +# Transform - Minimize the grammar, through the removal of the +# unreachable and not useful nonterminals (and expressions). + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require page::analysis::peg::reachable +package require page::analysis::peg::realizable + +namespace eval ::page::analysis::peg {} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::analysis::peg::minimize {t} { + page_info {[PEG Minimization]} + page_log_info ..Reachability ; ::page::analysis::peg::reachable::remove! + page_log_info ..Realizability ; ::page::analysis::peg::realizable::remove! + + page_log_info Ok + return +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::analysis::peg::minimize 0.1 + diff --git a/tcllib/modules/page/analysis_peg_reachable.tcl b/tcllib/modules/page/analysis_peg_reachable.tcl new file mode 100644 index 0000000..27d12ff --- /dev/null +++ b/tcllib/modules/page/analysis_peg_reachable.tcl @@ -0,0 +1,150 @@ +# -*- tcl -*- +# ### ### ### ######### ######### ######### + +# Perform reachability analysis on the PE grammar delivered by the +# frontend. The grammar is in normalized form (reduced to essentials, +# graph like node-x-references, expression trees). + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require page::util::flow ; # Dataflow walking. +package require page::util::peg ; # General utilities. + +namespace eval ::page::analysis::peg::reachable { + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::analysis::peg::reachable::compute {t} { + + # Ignore call if already done before + if {[$t keyexists root page::analysis::peg::reachable]} return + + # We compute the set of all nodes which are reachable from the + # root node of the start expression. This is a simple topdown walk + # where the children of all reachable nodes are mode reachable as + # well, and invokations of nonterminals symbols are treated as + # children as well. At the end of the flow all reachable non- + # terminal symbols and their expressions are marked, and none + # other. + + # Initialize walking state: 2 arrays, all nodes (except root) are + # in or the other array, and their location tells if they are + # reachable or not. In the beginning no node is reachable. The + # goal array (reach) also serves as minder of which nodes have + # been seen, to cut multiple visits short. + + array set unreach {} ; foreach n [$t nodes] {set unreach($n) .} + unset unreach(root) + array set reach {} + + # A node is visited if it has been determined that it is indeed + # reachable. + + page::util::flow [list [$t get root start]] flow n { + # Ignore nodes already reached. + if {[info exists reach($n)]} continue + + # Reclassify node, has been reached now. + unset unreach($n) + set reach($n) . + + # Schedule children for visit --> topdown flow. + $flow visitl [$t children $n] + + # Treat n-Nodes as special, their definition as indirect + # child. But ignore invokations of undefined nonterminal + # symbols, or those already marked as reachable. + + if {![$t keyexists $n op]} continue + if {[$t get $n op] ne "n"} continue + + set def [$t get $n def] + if {$def eq ""} continue + if {[info exists reach($def)]} continue + $flow visit $def + } + + # Store results. This also serves as marker. + + $t set root page::analysis::peg::reachable [array names reach] + $t set root page::analysis::peg::unreachable [array names unreach] + return +} + +proc ::page::analysis::peg::reachable::remove! {t} { + + # Determine which nonterminal symbols are reachable from the root + # of the start expression. + + compute $t + + # Remove all nodes which are not reachable. + + set unreach [$t get root page::analysis::peg::unreachable] + foreach n [lsort $unreach] { + if {[$t exists $n]} { + $t delete $n + } + } + + # Notify the user of the definitions which were among the removed + # nodes. Keep only the still-existing definitions. + + set res {} + foreach {sym def} [$t get root definitions] { + if {![$t exists $def]} { + page_warning " $sym: Unreachable nonterminal symbol, deleting" + } else { + lappend res $sym $def + } + } + + # Clear computation results. + + $t unset root page::analysis::peg::reachable + $t unset root page::analysis::peg::unreachable + + $t set root definitions $res + updateUndefinedDueRemoval $t + return +} + +proc ::page::analysis::peg::reachable::reset {t} { + # Remove marker, allow recalculation of reachability after + # changes. + + $t unset root page::analysis::peg::reachable + $t unset root page::analysis::peg::unreachable + return +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::analysis::peg::reachable 0.1 diff --git a/tcllib/modules/page/analysis_peg_realizable.tcl b/tcllib/modules/page/analysis_peg_realizable.tcl new file mode 100644 index 0000000..ef32a68 --- /dev/null +++ b/tcllib/modules/page/analysis_peg_realizable.tcl @@ -0,0 +1,257 @@ +# -*- tcl -*- +# ### ### ### ######### ######### ######### + +# Perform realizability analysis (x) on the PE grammar delivered by +# the frontend. The grammar is in normalized form (reduced to +# essentials, graph like node-x-references, expression trees). +# +# (x) = See "doc_realizable.txt". + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require page::util::flow ; # Dataflow walking. +package require page::util::peg ; # General utilities. +package require treeql + +namespace eval ::page::analysis::peg::realizable { + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::analysis::peg::realizable::compute {t} { + + # Ignore call if already done before + + if {[$t keyexists root page::analysis::peg::realizable]} return + + # We compute the set of realizable nonterminal symbols by doing the + # computation for all partial PE's in the grammar. We start at the + # leaves and then iteratively propagate the property as far as + # possible using the rules defining it, see the specification. + + # --- --- --- --------- --------- --------- + + # Initialize all nodes and the local arrays. Everything is not + # realizable, except for the terminal leafs of the tree. Their parents + # are scheduled to be visited as well. + + array set realizable {} ; # Place where realizable nodes are held + array set unrealizable {} ; # Place where unrealizable nodes are held + array set nc {} ; # Per node, number of children. + array set uc {} ; # Per node, number of realizable children. + + set nodeset [$t leaves] + + set q [treeql q -tree $t] + $q query tree withatt op * over n {lappend nodeset $n} + $q query tree withatt op ? over n {lappend nodeset $n} + q destroy + + foreach n [$t nodes] { + set unrealizable($n) . + set nc($n) [$t numchildren $n] + set uc($n) 0 + } + + # A node is visited if it _may_ have changed its status (to + # realizability). + + page::util::flow $nodeset flow n { + # Realizable nodes cannot change, ignore them. + + if {[info exists realizable($n)]} continue + + # Determine new state of realizability, ignore a node if it is + # unchanged. + + if {![Realizable $t $n nc uc realizable]} continue + + # Reclassify changed node, it is now realizable. + unset unrealizable($n) + set realizable($n) . + + # Schedule visits to nodes which may have been affected by + # this change. Update the relevant counters as well. + + # @ root - none + # @ definition - users of the definition + # otherwise - parent of operator. + + if {$n eq "root"} continue + + if {[$t keyexists $n symbol]} { + set users [$t get $n users] + $flow visitl $users + foreach u $users { + incr uc($u) + } + continue + } + + set p [$t parent $n] + incr uc($p) + $flow visit $p + } + + # Set marker preventing future calls. + $t set root page::analysis::peg::realizable [array names realizable] + $t set root page::analysis::peg::unrealizable [array names unrealizable] + return +} + +proc ::page::analysis::peg::realizable::remove! {t} { + # Determine which parts of the grammar are realizable + + compute $t + + # Remove anything which is not realizable (and all their children), + # except for the root itself, should it be unrealizablel. + + set unreal [$t get root page::analysis::peg::unrealizable] + foreach n [lsort $unreal] { + if {$n eq "root"} continue + if {[$t exists $n]} { + $t delete $n + } + } + + # Notify the user of the definitions which were among the removed + # nodes. Keep only the still-existing definitions. + + set res {} + foreach {sym def} [$t get root definitions] { + if {![$t exists $def]} { + page_warning " $sym: Nonterminal symbol is not realizable, removed." + } else { + lappend res $sym $def + } + } + $t set root definitions $res + + if {![$t exists [$t get root start]]} { + page_warning " <Start expression>: Is not realizable, removed." + $t set root start {} + } + + # Find and cut operator chains, very restricted. Cut only chains + # of x- and /-operators. The other operators have only one child + # by definition and are thus not chains. + + set q [treeql q -tree $t] + # q query tree over n + foreach n [$t children -all root] { + if {[$t keyexists $n symbol]} continue + if {[llength [$t children $n]] != 1} continue + set op [$t get $n op] + if {($op ne "/") && ($op ne "x")} continue + $t cut $n + } + + flatten $q $t + q destroy + + # Clear computation results. + + $t unset root page::analysis::peg::realizable + $t unset root page::analysis::peg::unrealizable + + updateUndefinedDueRemoval $t + return +} + +proc ::page::analysis::peg::realizable::reset {t} { + # Remove marker, allow recalculation of realizability after changes. + + $t unset root page::analysis::peg::realizable + return +} + +# ### ### ### ######### ######### ######### +## Internal + +proc ::page::analysis::peg::realizable::First {v} { + upvar 1 $v visit + + set id [array startsearch visit] + set first [array nextelement visit $id] + array donesearch visit $id + + unset visit($first) + return $first +} + +proc ::page::analysis::peg::realizable::Realizable {t node ncv ucv uv} { + upvar 1 $ncv nc $ucv uc $uv realizable + + if {$node eq "root"} { + # Root inherits realizability of the start expression. + + return [info exists realizable([$t get root start])] + } + + if {[$t keyexists $node symbol]} { + # Symbol definitions inherit the realizability of their + # expression. + + return [expr {$uc($node) >= $nc($node)}] + } + + switch -exact -- [$t get $node op] { + t - .. - epsilon - alpha - alnum - dot - * - ? { + # The terminal symbols are all realizable. + return 1 + } + n { + # Symbol invokation inherits realizability of its definition. + # Calls to undefined symbols are not realizable. + + set def [$t get $node def] + if {$def eq ""} {return 0} + return [info exists realizable($def)] + } + / - | { + # Choice, ordered and unordered. Realizable if we have at + # least one realizable branch. A quick test based on the count + # of realizable children is used. + + return [expr {$uc($node) > 0}] + } + default { + # Sequence, and all other operators, are realizable if and + # only if all its children are realizable. A quick test based + # on the count of realizable children is used. + + return [expr {$uc($node) >= $nc($node)}] + } + } +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::analysis::peg::realizable 0.1 diff --git a/tcllib/modules/page/compiler_peg_mecpu.tcl b/tcllib/modules/page/compiler_peg_mecpu.tcl new file mode 100644 index 0000000..123c6da --- /dev/null +++ b/tcllib/modules/page/compiler_peg_mecpu.tcl @@ -0,0 +1,1642 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Transformation - Compile grammar to ME cpu instructions. + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Dumping the input grammar. But not as Tcl or other code. In PEG +## format again, pretty printing. + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. + +package require grammar::me::cpu::gasm +package require textutil +package require struct::graph + +package require page::analysis::peg::emodes +package require page::util::quote +package require page::util::peg + +namespace eval ::page::compiler::peg::mecpu { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl) + + namespace import ::page::util::quote::* + namespace import ::page::util::peg::* + + + namespace eval gas { + namespace import ::grammar::me::cpu::gas::begin + namespace import ::grammar::me::cpu::gas::done + namespace import ::grammar::me::cpu::gas::lift + namespace import ::grammar::me::cpu::gas::state + namespace import ::grammar::me::cpu::gas::state! + } + namespace import ::grammar::me::cpu::gas::* + rename begin {} + rename done {} + rename lift {} + rename state {} + rename state! {} +} + +# ### ### ### ######### ######### ######### +## Data structures for the generated code. + +## All data is held in node attributes of the tree. Per node: +## +## asm - List of instructions implementing the node. + + + +# ### ### ### ######### ######### ######### +## API + +proc ::page::compiler::peg::mecpu {t} { + # Resolve the mode hints. Every gen(X) having a value of 'maybe' + # (or missing) is for the purposes of this code a 'yes'. + + if {![page::analysis::peg::emodes::compute $t]} { + page_error " Unable to generate a ME parser without accept/generate properties" + return + } + + foreach n [$t nodes] { + if {![$t keyexists $n gen] || ([$t get $n gen] eq "maybe")} { + $t set $n gen 1 + } + if {![$t keyexists $n acc]} {$t set $n acc 1} + } + + # Synthesize a program, then the assembly code. + + mecpu::Synth $t + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::compiler::peg::mecpu::Synth {t} { + # Phase 2: Bottom-up, synthesized attributes + + # We use a global graph to capture instructions and their + # relations. The graph is then converted into a linear list of + # instructions, with proper labeling and jump instructions to + # handle all non-linear control-flow. + + set g [struct::graph g] + $t set root gas::called {} + + page_info "* Synthesize graph code" + + $t walk root -order post -type dfs n { + SynthNode $n + } + + status $g ; gdump $g synth + remove_unconnected $g ; gdump $g nounconnected + remove_dead $g ; gdump $g nodead + denop $g ; gdump $g nonops + parcmerge $g ; gdump $g parcmerge + forwmerge $g ; gdump $g fmerge + backmerge $g ; gdump $g bmerge + status $g + pathlengths $g ; gdump $g pathlen + jumps $g ; gdump $g jumps + status $g + symbols $g $t + + set cc [2code $t $g] + #write asm/mecode [join $cc \n] + + statistics $cc + + $t set root asm $cc + $g destroy + return +} + +proc ::page::compiler::peg::mecpu::SynthNode {n} { + upvar 1 t t g g + if {$n eq "root"} { + set code Root + } elseif {[$t keyexists $n symbol]} { + set code Nonterminal + } elseif {[$t keyexists $n op]} { + set code [$t get $n op] + } else { + return -code error "PANIC. Bad node $n, cannot classify" + } + + page_log_info " [np $n] := ([linsert [$t children $n] 0 $code])" + + SynthNode/$code $n + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/Root {n} { + upvar 1 t t g g + + # Root is the grammar itself. + + set gstart [$t get root start] + set gname [$t get root name] + + if {$gstart eq ""} { + page_error " No start expression." + return + } + + gas::begin $g $n halt "<Start Expression> '$gname'" + $g node set [Who entry] instruction .C + $g node set [Who entry] START . + + Inline $t $gstart sexpr + /At sexpr/exit/ok ; /Ok ; Jmp exit/return + /At sexpr/exit/fail ; /Fail ; Jmp exit/return + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/Nonterminal {n} { + upvar 1 t t g g + + # This is the root of a definition. + # + # The text is a procedure wrapping the match code of its + # expression into the required the nonterminal handling (caching + # and such), plus the support code for the expression matcher. + + set sym [$t get $n symbol] + set label [$t get $n label] + set gen [$t get $n gen] + set mode [$t get $n mode] + + set pe [lindex [$t children $n] 0] + set egen [$t get $pe gen] + + # -> inc_restore -found-> NOP gen: -> ok -> ias_push -> RETURN + # /!found \ / + # / \-fail --------->/ + # / !gen: -> RETURN + # / + # \-> icl_push (-> ias_mark) -> (*) -> SV -> inc_save (-> ias_mrewind) -X + # + # X -ok----> ias_push -> ier_nonterminal + # \ / + # \-fail ----------/ + + # Poking into the generated instructions, converting the initial + # .NOP into a .C'omment. + + set first [gas::begin $g $n !okfail "Nonterminal '$sym'"] + $g node set [Who entry] instruction .C + $g node set [Who entry] START . + + Cmd inc_restore $label ; /Label restore ; /Ok + + if {$gen} { + Bra ; /Label @ + /Fail ; Nop ; Exit + /At @ + /Ok ; Cmd ias_push ; Exit + } else { + Nop ; Exit + } + + /At restore ; /Fail + Cmd icl_push ; # Balanced by inc_save (XX) + Cmd icl_push ; # Balanced by pop after ier_terminal + + if {$egen} { + # [*] Needed for removal of SV's from stack after handling by + # this symbol, only if expression actually generates an SV. + + Cmd ias_mark + } + + Inline $t $pe subexpr ; /Ok ; Nop ; /Label unified + /At subexpr/exit/fail ; /Fail ; Jmp unified + /At unified + + switch -exact -- $mode { + value {Cmd isv_nonterminal_reduce $label} + match {Cmd isv_nonterminal_range $label} + leaf {Cmd isv_nonterminal_leaf $label} + discard {Cmd isv_clear} + default {return -code error "Bad nonterminal mode \"$mode\""} + } + + Cmd inc_save $label ; # Implied icl_pop (XX) + + if {$egen} { + # See [*], this is the removal spoken about before. + Cmd ias_mrewind + } + + /Label hold + + if {$gen} { + /Ok + Cmd ias_push + Nop ; /Label merge + /At hold ; /Fail ; Jmp merge + /At merge + } + + Cmd ier_nonterminal "Expected $label" + Cmd icl_pop + Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/? {n} { + upvar 1 t t g g + + # The expression e? is equivalent to e/epsilon. + # And like this it is compiled. + + set pe [lindex [$t children $n] 0] + + gas::begin $g $n okfail ? + + # -> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop -ok----------------> OK + # \ / + # \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -ok-/ + + Cmd icl_push + Cmd ier_push + + Inline $t $pe subexpr + + /Ok + Cmd ier_merge + Cmd icl_pop + /Ok ; Exit + + /At subexpr/exit/fail ; /Fail + Cmd ier_merge + Cmd icl_rewind + Cmd iok_ok + /Ok ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/* {n} { + upvar 1 t t g g + + # Kleene star is like a repeated ? + + # Note: Compilation as while loop, as done now + # means that the parser has no information about + # the intermediate structure of the input in his + # cache. + + # Future: Create a helper symbol X and compile + # the expression e = e'* as: + # e = X; X <- (e' X)? + # with match data for X put into the cache. This + # is not exactly equivalent, the structure of the + # AST is different (right-nested tree instead of + # a list). This however can be handled with a + # special nonterminal mode to expand the current + # SV on the stack. + + # Note 2: This is a transformation which can be + # done on the grammar itself, before the actual + # backend is let loose. This "strength reduction" + # allows us to keep this code here. + + set pe [lindex [$t children $n] 0] + set egen [$t get $pe gen] + + # Build instruction graph. + + # /<---------------------------------------------------------------\ + # \_ \_ + # ---> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop ->/ + # \ + # \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -> OK + + gas::begin $g $n okfail * + + Cmd icl_push ; /Label header + Cmd ier_push + + Inline $t $pe loop + + /Ok + Cmd ier_merge + Cmd icl_pop + Jmp header ; /CloseLoop + + /At loop/exit/fail ; /Fail + Cmd ier_merge + Cmd icl_rewind + Cmd iok_ok + /Ok ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/+ {n} { + upvar 1 t t g g + + # Positive Kleene star x+ is equivalent to x x* + # This is how it is compiled. See also the notes + # at the * above, they apply in essence here as + # well, except that the transformat scheme is + # slighty different: + # + # e = e'* ==> e = X; X <- e' X? + + set pe [lindex [$t children $n] 0] + + # Build instruction graph. + + # icl_push -> ier_push -> (*) -fail-> ier_merge/fl -> icl_rewind -> FAIL + # \ + # \--ok---> ier_merge/ok -> icl_pop ->\_ + # / + # /<--------------------------------------------------------/ + # / + # /<---------------------------------------------------------------\ + # \_ \_ + # -> icl_push -> ier_push -> (*) -ok--> ier_merge/ok --> icl_pop ->/ + # \ + # \-fail-> ier_merge/f ---> icl_rewind -> iok_ok -> OK + + gas::begin $g $n okfail + + + Cmd icl_push + Cmd ier_push + + Inline $t $pe first + /At first/exit/fail ; /Fail + Cmd ier_merge + Cmd icl_rewind + /Fail ; Exit + + /At first/exit/ok ; /Ok + Cmd ier_merge + Cmd icl_pop + + # Loop copied from Kleene *, it is * + + Cmd icl_push ; /Label header + Cmd ier_push + + # For the loop we create the sub-expression instruction graph a + # second time. This is done by walking the subtree a second time + # and constructing a completely new node set. The result is + # imported under a new name. + + set save [gas::state] + $t walk $pe -order post -type dfs n {SynthNode $n} + gas::state! $save + Inline $t $pe loop + + /Ok + Cmd ier_merge + Cmd icl_pop + Jmp header ; /CloseLoop + + /At loop/exit/fail ; /Fail + Cmd ier_merge + Cmd icl_rewind + Cmd iok_ok + /Ok ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode// {n} { + upvar 1 t t g g + + set args [$t children $n] + + if {![llength $args]} { + error "PANIC. Empty choice." + + } elseif {[llength $args] == 1} { + # A choice over one branch is no real choice. The code + # generated for the child applies here as well. + + gas::lift $t $n <-- [lindex $args 0] + return + } + + # Choice over at least two branches. + # Build instruction graph. + + # -> BRA + # + # BRA -> icl_push (-> ias_mark) -> ier_push -> (*) -ok -> ier_merge -> BRA'OK + # \-fail -> ier_merge (-> ias_mrewind) -> icl_rewind -> BRA'FAIL + # + # BRA'FAIL -> BRA + # BRA'FAIL -> FAIL (last branch) + # + # BRA'OK -> icl_pop -> OK + + gas::begin $g $n okfail / + + /Clear + Cmd icl_pop ; /Label BRA'OK ; /Ok ; Exit + /At entry + + foreach pe $args { + set egen [$t get $pe gen] + + # Note: We do not check for static match results. Doing so is + # an optimization we can do earlier, directly on the tree. + + Cmd icl_push + if {$egen} {Cmd ias_mark} + + Cmd ier_push + Inline $t $pe subexpr + + /Ok + Cmd ier_merge + Jmp BRA'OK + + /At subexpr/exit/fail ; /Fail + Cmd ier_merge + if {$egen} {Cmd ias_mrewind} + Cmd icl_rewind + + # Branch failed. Go to the next branch. Fail completely at + # last branch. + } + + /Fail ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/x {n} { + upvar 1 t t g g + + set args [$t children $n] + + if {![llength $args]} { + error "PANIC. Empty sequence." + + } elseif {[llength $args] == 1} { + # A sequence of one element is no real sequence. The code + # generated for the child applies here as well. + + gas::lift $t $n <-- [lindex $args 0] + return + } + + # Sequence of at least two elements. + # Build instruction graph. + + # -> icl_push -> SEG + # + # SEG (-> ias_mark) -> ier_push -> (*) -ok -> ier_merge -> SEG'OK + # \-fail -> ier_merge -> SEG'FAIL + # + # SEG'OK -> SEG + # SEG'OK -> icl_pop -> OK (last segment) + # + # SEG'FAIL (-> ias_mrewind) -> icl_rewind -> FAIL + + gas::begin $g $n okfail x + + /Clear + Cmd icl_rewind ; /Label SEG'FAIL ; /Fail ; Exit + + /At entry + Cmd icl_push + + set gen 0 + foreach pe $args { + set egen [$t get $pe gen] + if {$egen && !$gen} { + set gen 1 + + # From here on out is the sequence able to generate + # semantic values which have to be canceled when + # backtracking. + + Cmd ias_mark ; /Label @mark + + /Clear + Cmd ias_mrewind ; Jmp SEG'FAIL ; /Label SEG'FAIL + + /At @mark + } + + Cmd ier_push + Inline $t $pe subexpr + + /At subexpr/exit/fail ; /Fail + Cmd ier_merge + Jmp SEG'FAIL + + /At subexpr/exit/ok ; /Ok + Cmd ier_merge + } + + Cmd icl_pop + /Ok ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/& {n} { + upvar 1 t t g g + SynthLookahead $n no + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/! {n} { + upvar 1 t t g g + SynthLookahead $n yes + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/dot {n} { + upvar 1 t t g g + SynthTerminal $n {} "any character" + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/epsilon {n} { + upvar 1 t t g g + + gas::begin $g $n okfail epsilon + + Cmd iok_ok ; /Ok ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/alnum {n} { + upvar 1 t t g g + SynthClass $n alnum + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/alpha {n} { + upvar 1 t t g g + SynthClass $n alpha + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/digit {n} { + upvar 1 t t g g + SynthClass $n digit + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/xdigit {n} { + upvar 1 t t g g + SynthClass $n xdigit + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/punct {n} { + upvar 1 t t g g + SynthClass $n punct + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/space {n} { + upvar 1 t t g g + SynthClass $n space + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/.. {n} { + upvar 1 t t g g + # Range is [x-y] + + set b [$t get $n begin] + set e [$t get $n end] + + set tb [quote'tcl $b] + set te [quote'tcl $e] + + set pb [quote'tclstr $b] + set pe [quote'tclstr $e] + + SynthTerminal $n [list ict_match_tokrange $tb $te] "\\\[${pb}..${pe}\\\]" + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/t {n} { + upvar 1 t t g g + + # Terminal node. Primitive matching. + # Code is parameterized by gen(X) of this node X. + + set ch [$t get $n char] + set tch [quote'tcl $ch] + set pch [quote'tclstr $ch] + + SynthTerminal $n [list ict_match_token $tch] $pch + return +} + +proc ::page::compiler::peg::mecpu::SynthNode/n {n} { + upvar 1 t t g g + + # Nonterminal node. Primitive matching. + # The code is parameterized by acc(X) of this node X, and gen(D) + # of the invoked nonterminal D. + + set sym [$t get $n sym] + set def [$t get $n def] + + gas::begin $g $n okfail call'$sym' + + if {$def eq ""} { + # Invokation of an undefined nonterminal. This will always fail. + + Note "Match for undefined symbol '$sym'" + Cmdd iok_fail ; /Fail ; Exit + gas::done --> $t + + } else { + # Combinations + # Acc Gen Action + # --- --- ------ + # 0 0 Plain match + # 0 1 Match with canceling of the semantic value. + # 1 0 Plain match + # 1 1 Plain match + # --- --- ------ + + if {[$t get $n acc] || ![$t get $def gen]} { + Cmd icf_ntcall sym_$sym ; /Label CALL + /Ok ; Exit + /Fail ; Exit + + } else { + Cmd ias_mark + Cmd icf_ntcall sym_$sym ; /Label CALL + Cmd ias_mrewind + /Ok ; Exit + /Fail ; Exit + } + + set caller [Who CALL] + gas::done --> $t + + $t lappend $def gas::callers $caller + $t lappend root gas::called $def + } + + return +} + +proc ::page::compiler::peg::mecpu::SynthLookahead {n negated} { + upvar 1 g g t t + + # Note: Per the rules about expression modes (! is a lookahead + # ____| operator) this node has a mode of 'discard', and its child + # ____| has so as well. + + # assert t get n mode == discard + # assert t get pe mode == discard + + set op [$t get $n op] + set pe [lindex [$t children $n] 0] + set eop [$t get $pe op] + + # -> icl_push -> (*) -ok--> icl_rewind -> OK + # \--fail-> icl_rewind -> FAIL + + # -> icl_push -> (*) -ok--> icl_rewind -> iok_negate -> FAIL + # \--fail-> icl_rewind -> iok_negate -> OK + + gas::begin $g $n okfail [expr {$negated ? "!" : "&"}] + + Cmd icl_push + Inline $t $pe subexpr + + /Ok + Cmd icl_rewind + if {$negated} { Cmd iok_negate ; /Fail } else /Ok ; Exit + + /At subexpr/exit/fail ; /Fail + Cmd icl_rewind + if {$negated} { Cmd iok_negate ; /Ok } else /Fail ; Exit + + gas::done --> $t + return +} + +proc ::page::compiler::peg::mecpu::SynthClass {n op} { + upvar 1 t t g g + SynthTerminal $n [list ict_match_tokclass $op] <$op> + return +} + +proc ::page::compiler::peg::mecpu::SynthTerminal {n cmd msg} { + upvar 1 t t g g + + # 4 cases (+/- cmd, +/- sv). + # + # (A) +cmd+sv + # entry -> advance -ok-> match -ok-> sv -> OK + # \ \ + # \ \-fail----------> FAIL + # \-fail----------------------/ + # + # (B) -cmd+sv + # entry -> advance -ok-> sv -> OK + # \ + # \-fail-----------> FAIL + # + # (C) +cmd-sv + # entry -> advance -ok-> match -ok-> OK + # \ \ + # \ \-fail---> FAIL + # \-fail---------------/ + # + # (D) -cmd-sv + # entry -> advance -ok-> OK + # \ + # \-fail-----> FAIL + + gas::begin $g $n okfail M'[lindex $cmd 0] + + Cmd ict_advance "Expected $msg (got EOF)" + /Fail ; Exit + /Ok + + if {[llength $cmd]} { + lappend cmd "Expected $msg" + eval [linsert $cmd 0 Cmd] + /Fail ; Exit + /Ok + } + + if {[$t get $n gen]} { + Cmd isv_terminal + /Ok + } + + Exit + + gas::done --> $t + return +} + +# ### ### ### ######### ######### ######### +## Internal. Extending the graph of instructions (expression +## framework, new instructions, (un)conditional sequencing). + +# ### ### ### ######### ######### ######### +## Internal. Working on the graph of instructions. + +proc ::page::compiler::peg::mecpu::2code {t g} { + page_info "* Generating ME assembler code" + + set insn {} + set start [$t get root gas::entry] + set cat 0 + set calls [list $start] + + while {$cat < [llength $calls]} { + set now [lindex $calls $cat] + incr cat + + set at 0 + set pending [list $now] + + while {$at < [llength $pending]} { + set current [lindex $pending $at] + incr at + + while {$current ne ""} { + if {[$g node keyexists $current WRITTEN]} break + + insn $g $current insn + $g node set $current WRITTEN . + + if {[$g node keyexists $current SAVE]} { + lappend pending [$g node get $current SAVE] + } + if {[$g node keyexists $current CALL]} { + lappend calls [$g node get $current CALL] + } + + set current [$g node get $current NEXT] + if {$current eq ""} break + if {[$g node keyexists $current WRITTEN]} { + lappend insn [list {} icf_jalways \ + [$g node get $current LABEL]] + break + } + + # Process the following instruction, + # if there is any. + } + } + } + + return $insn +} + +proc ::page::compiler::peg::mecpu::insn {g current iv} { + upvar 1 $iv insn + + set code [$g node get $current instruction] + set args [$g node get $current arguments] + + set label {} + if {[$g node keyexists $current LABEL]} { + set label [$g node get $current LABEL] + } + + lappend insn [linsert $args 0 $label $code] + return +} + +if 0 { + if {[lindex $ins 0] eq "icf_ntcall"} { + set tmp {} + foreach b $branches { + if {[$g node keyexists $b START]} { + set sym [$g node get $b symbol] + lappend ins sym_$sym + } else { + lappend tmp $b + } + } + set branches $tmp + } +} + +# ### ### ### ######### ######### ######### +## Optimizations. +# +## I. Remove all nodes which are not connected to anything. +## There should be none. + +proc ::page::compiler::peg::mecpu::remove_unconnected {g} { + page_info "* Remove unconnected instructions" + + foreach n [$g nodes] { + if {[$g node degree $n] == 0} { + page_error "$n ([printinsn $g $n])" + page_error "Found unconnected node. This should not have happened." + page_error "Removing the bad node." + + $g node delete $n + } + } +} + +proc ::page::compiler::peg::mecpu::remove_dead {g} { + page_info "* Remove dead instructions" + + set count 0 + set runs 0 + set hasdead 1 + while {$hasdead} { + set hasdead 0 + foreach n [$g nodes] { + if {[$g node keyexists $n START]} continue + if {[$g node degree -in $n] > 0} continue + + page_log_info " [np $n] removed, dead ([printinsn $g $n])" + + $g node delete $n + + set hasdead 1 + incr count + } + incr runs + } + + page_info " Removed [plural $count instruction] in [plural $runs run]" + return +} + +# ### ### ### ######### ######### ######### +## Optimizations. +# +## II. We have lots of .NOP instructions in the control flow, as part +## of the framework. They made the handling of expressions easier, +## providing clear and fixed anchor nodes to connect to from +## inside and outside, but are rather like the epsilon-transitions +## in a (D,N)FA. Now is the time to get rid of them. +# +## We keep the .C'omments, and explicit .BRA'nches. +## We should not have any .NOP which is a dead-end (without +## successor), nor should we find .NOPs with more than one +## successor. The latter should have been .BRA'nches. Both +## situations are reported on. Dead-ends we +## remove. Multi-destination NOPs we keep. +# +## Without the nops in place to confus the flow we can perform a +## series peep-hole optimizations to merge/split branches. + +proc ::page::compiler::peg::mecpu::denop {g} { + # Remove the .NOPs and reroute control flow. We keep the pseudo + # instructions for comments (.C) and the explicit branch points + # (.BRA). + + page_info "* Removing the helper .NOP instructions." + + set count 0 + foreach n [$g nodes] { + # Skip over nodes already deleted by a previous iteration. + if {[$g node get $n instruction] ne ".NOP"} continue + + # We keep branching .NOPs, and warn user. There shouldn't be + # any. such should explicit bnrachpoints. + + set destinations [$g arcs -out $n] + + if {[llength $destinations] > 1} { + page_error "$n ([printinsn $g $n])" + page_error "Found a .NOP with more than one destination." + page_error "This should have been a .BRA instruction." + page_error "Not removed. Internal error. Fix the transformation." + continue + } + + # Nops without a destination, dead-end's are not wanted. They + # should not exist either too. We will do a general dead-end + # and dead-start removal as well. + + if {[llength $destinations] < 1} { + page_error "$n ([printinsn $g $n])" + page_error "Found a .NOP without any destination, i.e. a dead end." + page_error "This should not have happened. Removed the node." + + $g node delete $n + continue + } + + page_log_info " [np $n] removed, updated cflow ([printinsn $g $n])" + + # As there is exactly one destination we can now reroute all + # incoming arcs around the nop to the new destination. + + set target [$g arc target [lindex $destinations 0]] + foreach a [$g arcs -in $n] { + $g arc move-target $a $target + } + + $g node delete $n + incr count + } + + page_info " Removed [plural $count instruction]" + return +} + + +# ### ### ### ######### ######### ######### +## Optimizations. +# + +# Merge parallel arcs (remove one, make the other unconditional). + +proc ::page::compiler::peg::mecpu::parcmerge {g} { + page_info "* Search for identical parallel arcs and merge them" + + #puts [join [info loaded] \n] /seg.fault induced with tcllibc! - tree! + + set count 0 + foreach n [$g nodes] { + set arcs [$g arcs -out $n] + + if {[llength $arcs] < 2} continue + if {[llength $arcs] > 2} { + page_error " $n ([printinsn $g $n])" + page_error " Instruction has more than two destinations." + page_error " That is not possible. Internal error." + continue + } + # Two way branch. Both targets the same ? + + foreach {a b} $arcs break + + if {[$g arc target $a] ne [$g arc target $b]} continue + + page_log_info " [np $n] outbound arcs merged ([printinsn $g $n])" + + $g arc set $a condition always + $g arc delete $b + + incr count 2 + } + + page_info " Merged [plural $count arc]" + return +} + +# Use knowledge of the match status before and after an instruction to +# label the arcs a bit better (This may guide the forward and backward +# merging.). + +# Forward merging of instructions. +# An ok/fail decision is done as late as possible. +# +# /- ok ---> Y -> U /- ok ---> U +# X ==> X -> Y +# \- fail -> Y -> V \- fail -> V + +# The Y must not have additional inputs. This more complex case we +# will look at later. + +proc ::page::compiler::peg::mecpu::forwmerge {g} { + page_info "* Forward merging of identical instructions" + page_info " Delaying decisions" + set count 0 + set runs 0 + + set merged 1 + while {$merged} { + set merged 0 + foreach n [$g nodes] { + # Skip nodes already killed in previous rounds. + if {![$g node exists $n]} continue + + set outbound [$g arcs -out $n] + if {[llength $outbound] != 2} continue + + foreach {aa ab} $outbound break + set na [$g arc target $aa] + set nb [$g arc target $ab] + + set ia [$g node get $na instruction][$g node get $na arguments] + set ib [$g node get $nb instruction][$g node get $nb arguments] + if {$ia ne $ib} continue + + # Additional condition: Inbounds in the targets not > 1 + + if {([$g node degree -in $na] > 1) || + ([$g node degree -in $nb] > 1)} continue + + page_log_info " /Merge [np $n] : [np $na] <- [np $nb] ([printinsn $g $na])" + + # Label all arcs out of na with the condition of the arc + # into it. Ditto for the arcs out of nb. The latter also + # get na as their new origin. The arcs out of n relabeled + # to always. The nb is deleted. This creates the desired + # control structure without having to create a new node + # and filling it. We simply use na, discard nb, and + # properly rewrite the arcs to have the correct + # conditions. + + foreach a [$g arcs -out $na] { + $g arc set $a condition [$g arc get $aa condition] + } + foreach a [$g arcs -out $nb] { + $g arc set $a condition [$g arc get $ab condition] + $g arc move-source $a $na + } + $g arc set $aa condition always + $g node delete $nb + set merged 1 + incr count + } + incr runs + } + + # NOTE: This may require a parallel arc merge, with identification + # of merge-able arcs based on the arc condition, i.e. labeling. + + page_info " Merged [plural $count instruction] in [plural $runs run]" + return +} + +# Backward merging of instructions. +# Common backends are put together. +# +# U -> Y ->\ U ->\ +# -> X ==> -> Y -> X +# V -> Y ->/ V ->/ + +# Note. It is possible for an instruction to be amenable to both for- +# and backward merging. No heuristics are known to decide which is +# better. + +proc ::page::compiler::peg::mecpu::backmerge {g} { + page_info "* Backward merging of identical instructions" + page_info " Unifying paths" + set count 0 + set runs 0 + + set merged 1 + while {$merged} { + set merged 0 + foreach n [$g nodes] { + # Skip nodes already killed in previous rounds. + if {![$g node exists $n]} continue + + set inbound [$g arcs -in $n] + if {[llength $inbound] < 2} continue + + # We have more than 1 inbound arcs on this node. Check all + # pairs of pre-decessors for possible unification. + + # Additional condition: Outbounds in the targets not > 1 + # We check in different levels, to avoid redundant calls. + + while {[llength $inbound] > 2} { + set aa [lindex $inbound 0] + set tail [lrange $inbound 1 end] + + set na [$g arc source $aa] + if {[$g node degree -out $na] > 1} { + set inbound $tail + continue + } + + set inbound {} + foreach ab $tail { + set nb [$g arc source $ab] + if {[$g node degree -out $nb] > 1} continue + + set ia [$g node get $na instruction][$g node get $na arguments] + set ib [$g node get $nb instruction][$g node get $nb arguments] + + if {$ia ne $ib} { + lappend inbound $ab + continue + } + + page_log_info " \\Merge [np $n] : [np $na] <- [np $nb] ([printinsn $g $na])" + + # Discard the second node in the pair. Move all + # arcs inbound into it so that they reach the + # first node instead. + + foreach a [$g arcs -in $nb] {$g arc move-target $a $na} + $g node delete $nb + set merged 1 + incr count + } + } + } + incr runs + } + + page_info " Merged [plural $count instruction] in [plural $runs run]" + return +} + +# ### ### ### ######### ######### ######### + +proc ::page::compiler::peg::mecpu::pathlengths {g} { + page_info "* Find maximum length paths" + + set pending [llength [$g nodes]] + + set nodes {} + set loops {} + foreach n [$g nodes] { + $g node set $n WAIT [$g node degree -out $n] + set insn [$g node get $n instruction] + if {($insn eq "icf_halt") || ($insn eq "icf_ntreturn")} { + lappend nodes $n + } + if {[$g node keyexists $n LOOP]} { + lappend loops $n + } + } + + set level 0 + while {[llength $nodes]} { + incr pending -[llength $nodes] + set nodes [closure $g $nodes $level] + incr level + } + + if {[llength $loops]} { + page_info " Loop levels" + + set nodes $loops + while {[llength $nodes]} { + incr pending -[llength $nodes] + set nodes [closure $g $nodes $level] + incr level + } + } + + if {$pending} { + page_info " Remainder" + + while {$pending} { + set nodes {} + foreach n [$g nodes] { + if {[$g node keyexists $n LEVEL]} continue + if {[$g node get $n WAIT] < [$g node degree -out $n]} { + lappend nodes $n + } + } + while {[llength $nodes]} { + incr pending -[llength $nodes] + set nodes [closure $g $nodes $level] + incr level + } + } + } + return +} + +proc ::page::compiler::peg::mecpu::closure {g nodes level} { + page_log_info " \[[format %6d $level]\] : $nodes" + + foreach n $nodes {$g node set $n LEVEL $level} + + set tmp {} + foreach n $nodes { + foreach pre [$g nodes -in $n] { + # Ignore instructions already given a level. + if {[$g node keyexists $pre LEVEL]} continue + $g node set $pre WAIT [expr {[$g node get $pre WAIT] - 1}] + if {[$g node get $pre WAIT] > 0} continue + lappend tmp $pre + } + } + return [lsort -uniq -dict $tmp] +} + +proc ::page::compiler::peg::mecpu::jumps {g} { + page_info "* Insert explicit jumps and branches" + + foreach n [$g nodes] { + # Inbound > 1, at least one is from a jump, so a label is + # needed. + + if {[llength [$g arcs -in $n]] > 1} { + set go bra[string range $n 4 end] + $g node set $n LABEL $go + } + + set darcs [$g arcs -out $n] + + if {[llength $darcs] == 0} { + $g node set $n NEXT "" + continue + } + + if {[llength $darcs] == 1} { + set da [lindex $darcs 0] + set dn [$g arc target $da] + + if {[$g node get $dn LEVEL] > [$g node get $n LEVEL]} { + # Flow is backward, an uncond. jump + # is needed here. + + set go bra[string range $dn 4 end] + $g node set $dn LABEL $go + set j [$g node insert] + $g arc move-target $da $j + $g node set $j instruction icf_jalways + $g node set $j arguments $go + + $g arc insert $j $dn + + $g node set $n NEXT $j + $g node set $j NEXT "" + } else { + $g node set $n NEXT $dn + } + continue + } + + set aok {} + set afl {} + foreach a $darcs { + if {[$g arc get $a condition] eq "ok"} { + set aok $a + } else { + set afl $a + } + } + set nok [$g arc target $aok] + set nfl [$g arc target $afl] + + if {[$g node get $n instruction] eq "inc_restore"} { + set go bra[string range $nok 4 end] + $g node set $nok LABEL $go + + $g node set $n NEXT $nfl + $g node set $n SAVE $nok + + $g node set $n arguments [linsert [$g node get $n arguments] 0 $go] + continue + } + + if {[$g node get $n instruction] ne ".BRA"} { + set bra [$g node insert] + $g arc move-source $aok $bra + $g arc move-source $afl $bra + $g arc insert $n $bra + $g node set $n NEXT $bra + set n $bra + } + + if {[$g node get $nok LEVEL] > [$g node get $nfl LEVEL]} { + # Ok branch is direct, Fail is jump. + + $g node set $n NEXT $nok + $g node set $n SAVE $nfl + + set go bra[string range $nfl 4 end] + $g node set $nfl LABEL $go + $g node set $n instruction icf_jfail + $g node set $n arguments $go + } else { + + # Fail branch is direct, Ok is jump. + + $g node set $n NEXT $nfl + $g node set $n SAVE $nok + + set go bra[string range $nok 4 end] + $g node set $nok LABEL $go + $g node set $n instruction icf_jok + $g node set $n arguments $go + } + } +} + +proc ::page::compiler::peg::mecpu::symbols {g t} { + page_info "* Label subroutine heads" + + # Label and mark the instructions where subroutines begin. + # These markers are used by 2code to locate all actually + # used subroutines. + + foreach def [lsort -uniq [$t get root gas::called]] { + set gdef [$t get $def gas::entry] + foreach caller [$t get $def gas::callers] { + + # Skip callers which are gone because of optimizations. + if {![$g node exists $caller]} continue + + $g node set $caller CALL $gdef + $g node set $gdef LABEL \ + [lindex [$g node set $caller arguments] 0] + } + } + return +} + +# ### ### ### ######### ######### ######### + +proc ::page::compiler::peg::mecpu::statistics {code} { + return + # disabled + page_info "* Statistics" + statistics_si $code + + # All higher order statistics are done only on the instructions in + # a basic block, i.e. a linear sequence. We are looking for + # high-probability blocks in itself, and then also for + # high-probability partials. + + set blocks [basicblocks $code] + + # Basic basic block statistics (full blocks) + + Init bl + foreach b $blocks {Incr bl($b)} + wrstat bl asm/statistics_bb.txt + wrstatk bl asm/statistics_bbk.txt + + # Statistics of all partial blocks, i.e. all possible + # sub-sequences with length > 1. + + Init ps + foreach b $blocks { + for {set s 0} {$s < [llength $b]} {incr s} { + for {set e [expr {$s + 1}]} {$e < [llength $b]} {incr e} { + Incr ps([lrange $b $s $e]) $bl($b) + } + } + } + + wrstat ps asm/statistics_ps.txt + wrstatk ps asm/statistics_psk.txt + return +} + +proc ::page::compiler::peg::mecpu::statistics_si {code} { + page_info " Single instruction probabilities." + + # What are the most used instructions, statically speaking, + # without considering context ? + + Init si + foreach i $code { + foreach {label name} $i break + if {$name eq ".C"} continue + Incr si($name) + } + + wrstat si asm/statistics_si.txt + return +} + +proc ::page::compiler::peg::mecpu::Init {v} { + upvar 1 $v var total total + array set var {} + set total 0 + return +} + +proc ::page::compiler::peg::mecpu::Incr {v {n 1}} { + upvar 1 $v var total total + if {![info exists var]} {set var $n ; incr total ; return} + incr var $n + incr total $n + return +} + +proc ::page::compiler::peg::mecpu::wrstat {bv file} { + upvar 1 $bv buckets total total + + set tmp {} + foreach {name count} [array get buckets] { + lappend tmp [list $name $count] + } + + set lines {} + lappend lines "Total: $total" + + set half [expr {$total / 2}] + set down $total + + foreach item [lsort -index 1 -decreasing -integer [lsort -index 0 $tmp]] { + foreach {key count} $item break + + set percent [format %6.2f [expr {$count*100.0/$total}]]% + set fcount [format %8d $count] + + lappend lines " $fcount $percent $key" + incr down -$count + if {$half && ($down < $half)} { + lappend lines ** + set half 0 + } + } + + write $file [join $lines \n]\n + return +} + +proc ::page::compiler::peg::mecpu::wrstatk {bv file} { + upvar 1 $bv buckets total total + + set tmp {} + foreach {name count} [array get buckets] { + lappend tmp [list $name $count] + } + + set lines {} + lappend lines "Total: $total" + + set half [expr {$total / 2}] + set down $total + + foreach item [lsort -index 0 [lsort -index 1 -decreasing -integer $tmp]] { + foreach {key count} $item break + + set percent [format %6.2f [expr {$count*100.0/$total}]]% + set fcount [format %8d $count] + + lappend lines " $fcount $percent $key" + incr down -$count + if {$down < $half} { + lappend lines ** + set half -1 + } + } + + write $file [join $lines \n]\n + return +} + +proc ::page::compiler::peg::mecpu::basicblocks {code} { + set blocks {} + set block {} + + foreach i $code { + foreach {label name} $i break + if { + ($name eq ".C") || + ($name eq "icf_jok") || + ($name eq "icf_jfail") || + ($name eq "icf_jalways") || + ($name eq "icf_ntreturn") + } { + # Jumps stop a block, and are not put into the block + # Except if the block is of length 1. Then it is of + # interest to see if certain combinations are used + # often. + + if {[llength $block]} { + if {[llength $block] == 1} {lappend block $name} + lappend blocks $block + } + set block {} + continue + } elseif {$label ne ""} { + # A labeled instruction starts a new block and belongs to + # it. Note that the previous block is saved only if it is + # of length > 1. A single instruction block is not + # something we can optimize. + + if {[llength $block] > 1} {lappend blocks $block} + set block [list $name] + continue + } + # Extend current block + lappend block $name + } + + if {[llength $block]} {lappend blocks $block} + return $blocks +} + +# ### ### ### ######### ######### ######### + +proc ::page::compiler::peg::mecpu::printinsn {g n} { + return "[$g node get $n instruction] <[$g node get $n arguments]>" +} + +proc ::page::compiler::peg::mecpu::plural {n prefix} { + return "$n ${prefix}[expr {$n == 1 ? "" : "s"}]" +} + +proc ::page::compiler::peg::mecpu::np {n} { + format %-*s 8 $n +} + +proc ::page::compiler::peg::mecpu::status {g} { + page_info "[plural [llength [$g nodes]] instruction]" + return +} + +proc ::page::compiler::peg::mecpu::gdump {g file} { + return + # disabled + variable gnext + page_info " %% Saving graph to \"$file\" %%" + write asm/[format %02d $gnext]_${file}.sgr [$g serialize] + incr gnext + return +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::compiler::peg::mecpu { + variable gnext 0 +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::compiler::peg::mecpu 0.1.1 diff --git a/tcllib/modules/page/gen_peg_canon.tcl b/tcllib/modules/page/gen_peg_canon.tcl new file mode 100644 index 0000000..27d5f4b --- /dev/null +++ b/tcllib/modules/page/gen_peg_canon.tcl @@ -0,0 +1,481 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - PEG as ... PEG + +# ### ### ### ######### ######### ######### +## Dumping the input grammar. But not as Tcl or other code. In PEG +## format again, pretty printing. + +# ### ### ### ######### ######### ######### +## Requisites + +package require textutil + +namespace eval ::page::gen::peg::canon {} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::canon {t chan} { + + # Generate data for inherited attributes + # used during synthesis. + canon::Setup $t + + # Synthesize all text fragments we need. + canon::Synth $t + + # And write the grammar text. + puts $chan [$t get root TEXT] + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::peg::canon::Setup {t} { + # Phase 1: Top-down, inherited attributes: + # + # - Max length of nonterminal symbols defined by the grammar. + # + # - Indentation put on all rules to get enough space for + # definition attributes. + + set max -1 + array set modes {} + + foreach {sym def} [$t get root definitions] { + set l [string length $sym] + if {$l > $max} {set max $l} + + set mode [string index [$t get $def mode] 0] + set modes($mode) . + } + set modeset [join [lsort [array names modes]] ""] + set mlen [AttrFieldLength $modeset] + set heading [expr {$max + $mlen + 4}] + # The constant 4 is for ' <- ', see + # SynthNode/Nonterminal + + # Save the computed information for access by the definitions and + # other operators. + + $t set root SYM_FIELDLEN $max + $t set root ATT_FIELDLEN $mlen + $t set root ATT_BASE $modeset + $t set root HEADLEN $heading + return +} + +proc ::page::gen::peg::canon::Synth {t} { + # Phase 2: Bottom-up, synthesized attributes + # + # - Text block per node, length and height. + + $t walk root -order post -type dfs n { + SynthNode $t $n + } + return +} + +proc ::page::gen::peg::canon::SynthNode {t n} { + if {$n eq "root"} { + set code Root + } elseif {[$t keyexists $n symbol]} { + set code Nonterminal + } elseif {[$t keyexists $n op]} { + set code [$t get $n op] + } else { + return -code error "PANIC. Bad node $n, cannot classify" + } + + #puts stderr "SynthNode/$code $t $n" + + SynthNode/$code $t $n + + #SHOW [$t get $n TEXT] 1 0 + #catch {puts stderr "\t.[$t get $n W]x[$t get $n H]"} + return +} + +proc ::page::gen::peg::canon::SynthNode/Root {t n} { + # Root is the grammar itself. + + # Get the data we need from our children, which are start + # expression and nonterminal definitions. + + set gname [$t get root name] + set gstart [$t get root start] + if {$gstart ne ""} { + set stext [$t get $gstart TEXT] + } else { + puts stderr "No start expression." + set stext "" + } + set rules {} + foreach {sym def} [$t get root definitions] { + lappend rules [list $sym [$t get $def TEXT]] + } + + # Combine them into a text for the whole grammar. + + set intro "PEG $gname \(" + set ispace [::textutil::blank [string length $intro]] + + set out "" + append out "# -*- text -*-" \n + append out "## Parsing Expression Grammar '$gname'." \n + append out "## Layouted by the PG backend 'PEGwriter'." \n + append out \n + append out $intro[::textutil::indent $stext $ispace 1]\) + append out \n + append out \n + + foreach e [lsort -dict -index 0 $rules] { + foreach {sym text} $e break + append out $text \n + append out \n + } + + append out "END\;" \n + + $t set root TEXT $out + return +} + +proc ::page::gen::peg::canon::SynthNode/Nonterminal {t n} { + # This is the root of a definition. We now + # have to combine the text block for the + # expression with nonterminal and attribute + # data. + + variable ms + + set abase [$t get root ATT_BASE] + set sfl [$t get root SYM_FIELDLEN] + set mode [$t get $n mode] + set sym [$t get $n symbol] + set etext [$t get [lindex [$t children $n] 0] TEXT] + + set out "" + append out $ms($abase,$mode) + append out $sym + append out [::textutil::blank [expr {$sfl - [string length $sym]}]] + append out " <- " + + set ispace [::textutil::blank [string length $out]] + + append out [::textutil::indent $etext $ispace 1] + append out " ;" + + $t set $n TEXT $out + return +} + +proc ::page::gen::peg::canon::SynthNode/t {t n} { + # Terminal node. Primitive layout. + # Put the char into single or double quotes. + + set ch [$t get $n char] + if {$ch eq "'"} {set q "\""} else {set q '} + + set text $q$ch$q + + SetBlock $t $n $text + return +} + +proc ::page::gen::peg::canon::SynthNode/n {t n} { + # Nonterminal node. Primitive layout. Text is the name of smybol + # itself. + + SetBlock $t $n [$t get $n sym] + return +} + +proc ::page::gen::peg::canon::SynthNode/.. {t n} { + # Range is [x-y] + set b [$t get $n begin] + set e [$t get $n end] + SetBlock $t $n "\[${b}-${e}\]" + return +} + +proc ::page::gen::peg::canon::SynthNode/alnum {t n} {SetBlock $t $n <alnum>} +proc ::page::gen::peg::canon::SynthNode/alpha {t n} {SetBlock $t $n <alpha>} +proc ::page::gen::peg::canon::SynthNode/dot {t n} {SetBlock $t $n .} +proc ::page::gen::peg::canon::SynthNode/epsilon {t n} {SetBlock $t $n ""} + +proc ::page::gen::peg::canon::SynthNode/? {t n} {SynthSuffix $t $n ?} +proc ::page::gen::peg::canon::SynthNode/* {t n} {SynthSuffix $t $n *} +proc ::page::gen::peg::canon::SynthNode/+ {t n} {SynthSuffix $t $n +} + +proc ::page::gen::peg::canon::SynthNode/! {t n} {SynthPrefix $t $n !} +proc ::page::gen::peg::canon::SynthNode/& {t n} {SynthPrefix $t $n &} + +proc ::page::gen::peg::canon::SynthSuffix {t n op} { + + set sub [lindex [$t children $n] 0] + set sop [$t get $sub op] + set etext [$t get $sub TEXT] + + WrapParens $op $sop etext + SetBlock $t $n $etext$op + return +} + +proc ::page::gen::peg::canon::SynthPrefix {t n op} { + + set sub [lindex [$t children $n] 0] + set sop [$t get $sub op] + set etext [$t get $sub TEXT] + + WrapParens $op $sop etext + SetBlock $t $n $op$etext + return +} + +proc ::page::gen::peg::canon::SynthNode/x {t n} { + variable llen + + # Space given to us for an expression. + set lend [expr {$llen - [$t get root HEADLEN]}] + + set clist [$t children $n] + if {[llength $clist] == 1} { + # Implicit cutting out of chains. + + CopyBlock $t $n [lindex $clist 0] + + #puts stderr <<implicit>> + return + } + + set out "" + + # We are not tracking the total width of the block, but only the + # width of the current line, as that is where we may have to + # wrap. The height however is the total height. + + #puts stderr <<$clist>> + #puts stderr \t___________________________________ + + set w 0 + set h 0 + foreach c $clist { + set sop [$t get $c op] + set sub [$t get $c TEXT] + set sw [$t get $c W] + set slw [$t get $c Wlast] + set sh [$t get $c H] + + #puts stderr \t<$sop/$sw/$slw/$sh>___________________________________ + #SHOW $sub $slw $sh + + if {[Paren x $sop]} { + set sub "([::textutil::indent $sub " " 1])" + incr slw 2 + incr sw 2 + + #puts stderr /paren/ + #SHOW $sub $slw $sh + } + + # Empty buffer ... Put element, and extend dimensions + + #puts stderr \t.============================= + #SHOW $out $w $h + + if {$w == 0} { + #puts stderr /init + append out $sub + set w $slw + set h $sh + } elseif {($w + $sw + 1) > $lend} { + #puts stderr /wrap/[expr {($w + $sw + 1)}]/$lend + # To large, wrap into next line. + append out \n $sub + incr h $sh + set w $slw + } else { + # We have still space to put the block in. Either by + # simply appending, or by indenting a multiline block + # properly so that its parts stay aligned with each other. + if {$sh == 1} { + #puts stderr /add/line + append out " " $sub + incr w ; incr w $slw + } else { + append out " " ; incr w + #puts stderr /add/block/$w + append out [::textutil::indent $sub [::textutil::blank $w] 1] + incr w $slw + incr h $sh ; incr h -1 + } + } + + #puts stderr \t.~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + #SHOW $out $w $h + } + + SetBlock $t $n $out + return +} + +proc ::page::gen::peg::canon::SynthNode// {t n} { + # We take all branches and put them together, nicely aligned under + # each other. + + set clist [$t children $n] + if {[llength $clist] == 1} { + # Implicit cutting out of chains. + + CopyBlock $t $n [lindex $clist 0] + return + } + + set out "" + foreach c $clist { + set sop [$t get $c op] + set sub [$t get $c TEXT] + WrapParens / $sop sub + append out "/ [::textutil::indent $sub " " 1]" \n + } + + SetBlock $t $n " [string range $out 1 end]" + return +} + +proc ::page::gen::peg::canon::WrapParens {op sop tvar} { + if {[Paren $op $sop]} { + upvar 1 $tvar text + set text "([::textutil::indent $text " " 1])" + } +} + +proc ::page::gen::peg::canon::Paren {op sop} { + # sop is nested under op. + # Parens are required if sop has a lower priority than op. + + return [expr {[Priority $sop] < [Priority $op]}] +} + +proc ::page::gen::peg::canon::Priority {op} { + switch -exact -- $op { + t - + n - + .. - + alnum - + alpha - + dot - + epsilon {return 4} + ? - + * - + + {return 3} + ! - + & {return 2} + x {return 1} + / {return 0} + } + return -code error "Internal error, bad operator \"$op\"" +} + +proc ::page::gen::peg::canon::CopyBlock {t n src} { + $t set $n TEXT [$t get $src TEXT] + $t set $n W [$t get $src W] + $t set $n Wlast [$t get $src Wlast] + $t set $n H [$t get $src H] + return +} + +proc ::page::gen::peg::canon::SetBlock {t n text} { + set text [string trimright $text] + set lines [split $text \n] + set height [llength $lines] + + if {$height > 1} { + set max -1 + set ntext {} + + foreach line $lines { + set line [string trimright $line] + set l [string length $line] + if {$l > $max} {set max $l} + lappend ntext $line + set wlast $l + } + set text [join $ntext \n] + set width $max + } else { + set width [string length $text] + set wlast $width + } + + $t set $n TEXT $text + $t set $n W $width + $t set $n Wlast $wlast + $t set $n H $height + return +} + +proc ::page::gen::peg::canon::AttrFieldLength {modeset} { + variable ms + return $ms($modeset,*) +} + +if {0} { + proc ::page::gen::peg::canon::SHOW {text w h} { + set wl $w ; incr wl -1 + puts stderr "\t/$h" + puts stderr "[textutil::indent $text \t|]" + puts stderr "\t\\[string repeat "-" $wl]^ ($w)" + return + } +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::canon { + variable llen 80 + variable ms ; array set ms { + dlmv,discard {void: } + dlmv,leaf {leaf: } + dlmv,match {match: } + dlmv,value { } + dlmv,* 7 + + dlm,discard {void: } dlv,discard {void: } + dlm,leaf {leaf: } dlv,leaf {leaf: } + dlm,match {match: } dlv,value { } + dlm,* 7 dlv,* 6 + + dmv,discard {void: } lmv,leaf {leaf: } + dmv,match {match: } lmv,match {match: } + dmv,value { } lmv,value { } + dmv,* 7 lmv,* 7 + + dl,discard {void: } dm,discard {void: } + dl,leaf {leaf: } dm,match {match: } + dl,* 6 dm,* 7 + + lm,leaf {leaf: } dv,discard {void: } + lm,match {match: } dv,value { } + lm,* 7 dv,* 6 + + lv,leaf {leaf: } mv,match {match: } + lv,value { } mv,value { } + lv,* 6 mv,* 7 + + d,discard {void: } d,* 6 + l,leaf {leaf: } l,* 6 + m,match {match: } m,* 7 + v,value {} v,* 0 + } +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::canon 0.1 diff --git a/tcllib/modules/page/gen_peg_cpkg.tcl b/tcllib/modules/page/gen_peg_cpkg.tcl new file mode 100644 index 0000000..5776f96 --- /dev/null +++ b/tcllib/modules/page/gen_peg_cpkg.tcl @@ -0,0 +1,171 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - PEG as Tcl script. + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::peg + +namespace eval ::page::gen::peg::cpkg { + # Get various utilities. + + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::cpkg {t chan} { + cpkg::printWarnings [cpkg::getWarnings $t] + + set grname [$t get root name] + + cpkg::Header $chan $grname + + set gstart [$t get root start] + if {$gstart ne ""} { + set gstart [cpkg::peOf $t $gstart] + } else { + puts stderr "No start expression." + } + + cpkg::Start $chan $gstart + + set temp {} + set max -1 + + foreach {sym def} [$t get root definitions] { + set eroot [lindex [$t children $def] 0] + set l [string length [list $sym]] + if {$l > $max} {set max $l} + lappend temp \ + [list $sym [$t get $def mode] [cpkg::peOf $t $eroot] $l] + } + + foreach e [lsort -dict -index 0 $temp] { + foreach {sym mode rule l} $e break + cpkg::Rule $chan $sym $mode $rule [expr {$max - $l}] + } + + cpkg::Trailer $chan $grname + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::peg::cpkg::Header {chan grname} { + variable header + variable headerb + + set stem [namespace tail $grname] + puts $chan [string map \ + [list \ + @@ [list $grname] \ + @stem@ [list $stem] \ + "\n\t" "\n" + ] \ + $header\n$headerb] +} + +proc ::page::gen::peg::cpkg::Start {chan pe} { + puts $chan " Start [printTclExpr $pe]\n" + return +} + +proc ::page::gen::peg::cpkg::Rule {chan sym mode pe off} { + variable ms + set off [string repeat " " $off] + puts $chan " Define $ms($mode) $sym$off [printTclExpr $pe]" + return +} + +proc ::page::gen::peg::cpkg::Trailer {chan grname} { + variable trailer + variable trailerb + puts $chan [string map \ + [list \ + @@ [list $grname] \ + "\n\t" "\n" + ] \ + $trailer\n$trailerb] +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::cpkg { + variable ms ; array set ms { + value {value } + discard {discard} + match {match } + leaf {leaf } + } + variable header {# -*- tcl -*- + ## Parsing Expression Grammar '@@'. + + # ### ### ### ######### ######### ######### + ## Package description + + ## It provides a single command returning the handle of a + ## grammar container in which the grammar '@@' + ## is stored. The container is usable by a PEG interpreter + ## or other packages taking PE grammars. + + # ### ### ### ######### ######### ######### + ## Requisites. + ## - PEG container type + + package require grammar::peg + + namespace eval ::@@ {} + + # ### ### ### ######### ######### ######### + ## API + + proc ::@@ {} { + return $@stem@::gr + } + + # ### ### ### ######### ######### ######### + # ### ### ### ######### ######### ######### + ## Data and helpers. + + namespace eval ::@@ { + # Grammar container + variable gr [::grammar::peg gr] + } + + proc ::@@::Start {pe} { + variable gr + $gr start $pe + return + } + + proc ::@@::Define {mode sym pe} { + variable gr + $gr nonterminal add $sym $pe + $gr nonterminal mode $sym $mode + return + } + + # ### ### ### ######### ######### ######### + ## Initialization = Grammar definition + } + variable headerb "namespace eval ::@@ \{" + + variable trailer "\}" + variable trailerb { + # ### ### ### ######### ######### ######### + ## Package Management - Ready + + package provide @@ 0.1 + } +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::cpkg 0.1 diff --git a/tcllib/modules/page/gen_peg_hb.tcl b/tcllib/modules/page/gen_peg_hb.tcl new file mode 100644 index 0000000..a4cbf6f --- /dev/null +++ b/tcllib/modules/page/gen_peg_hb.tcl @@ -0,0 +1,79 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - PEG in half baked form for PEG container. + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::peg + +namespace eval ::page::gen::peg::hb { + # Get various utilities. + + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::hb {t chan} { + hb::printWarnings [hb::getWarnings $t] + + set gstart [$t get root start] + if {$gstart ne ""} { + set gstart [hb::peOf $t $gstart] + } else { + puts stderr "No start expression." + } + + hb::Start $chan $gstart + + set temp {} + set max -1 + foreach {sym def} [$t get root definitions] { + set eroot [lindex [$t children $def] 0] + set l [string length [list $sym]] + if {$l > $max} {set max $l} + lappend temp \ + [list $sym [$t get $def mode] [hb::peOf $t $eroot] $l] + } + + foreach e [lsort -dict -index 0 $temp] { + foreach {sym mode rule l} $e break + hb::Rule $chan $sym $mode $rule [expr {$max - $l}] + } + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::peg::hb::Start {chan pe} { + puts $chan "Start [printTclExpr $pe]\n" + return +} + +proc ::page::gen::peg::hb::Rule {chan sym mode pe off} { + variable ms + set off [string repeat " " $off] + puts $chan "Define $ms($mode) $sym$off [printTclExpr $pe]" + return +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::hb { + variable ms ; array set ms { + value {value } + discard {discard} + match {match } + leaf {leaf } + } +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::hb 0.1 diff --git a/tcllib/modules/page/gen_peg_me.tcl b/tcllib/modules/page/gen_peg_me.tcl new file mode 100644 index 0000000..bb98902 --- /dev/null +++ b/tcllib/modules/page/gen_peg_me.tcl @@ -0,0 +1,888 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - Generate a grammar::mengine based parser. + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Dumping the input grammar. But not as Tcl or other code. In PEG +## format again, pretty printing. + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. + +package require textutil +package require page::analysis::peg::emodes +package require page::util::quote +package require page::util::peg + +namespace eval ::page::gen::peg::me { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl) + + namespace import ::page::util::quote::* + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::me::package {text} { + variable package $text + return +} + +proc ::page::gen::peg::me::copyright {text} { + variable copyright $text + return +} + +proc ::page::gen::peg::me {t chan} { + variable me::package + variable me::copyright + + # Resolve the mode hints. Every gen(X) having a value of 'maybe' + # (or missing) is for the purposes of this code a 'yes'. + + if {![page::analysis::peg::emodes::compute $t]} { + page_error " Unable to generate a ME parser without accept/generate properties" + return + } + + foreach n [$t nodes] { + if {![$t keyexists $n gen] || ([$t get $n gen] eq "maybe")} { + $t set $n gen 1 + } + if {![$t keyexists $n acc]} {$t set $n acc 1} + } + + $t set root Pcount 0 + + $t set root package $package + $t set root copyright $copyright + + # Synthesize all text fragments we need. + me::Synth $t + + # And write the grammar text. + puts $chan [$t get root TEXT] + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::peg::me::Synth {t} { + # Phase 2: Bottom-up, synthesized attributes + # + # - Text blocks per node. + + $t walk root -order post -type dfs n { + SynthNode $t $n + } + return +} + +proc ::page::gen::peg::me::SynthNode {t n} { + if {$n eq "root"} { + set code Root + } elseif {[$t keyexists $n symbol]} { + set code Nonterminal + } elseif {[$t keyexists $n op]} { + set code [$t get $n op] + } else { + return -code error "PANIC. Bad node $n, cannot classify" + } + + #puts stderr "SynthNode/$code $t $n" + + SynthNode/$code $t $n + + #SHOW [$t get $n TEXT] 1 0 + #catch {puts stderr "\t.[$t get $n W]x[$t get $n H]"} + return +} + +proc ::page::gen::peg::me::SynthNode/Root {t n} { + variable template + + # Root is the grammar itself. + + # Text blocks we have to combine: + # - Code for matching the start expression + # - Supporting code for the above. + # - Code per Nonterminal definition. + + set gname [$t get root name] + set gstart [$t get root start] + set gpackage [$t get root package] + set gcopy [$t get root copyright] + + if {$gcopy ne ""} { + set gcopyright "## (C) $gcopy\n" + } else { + set gcopyright "" + } + if {$gpackage eq ""} { + set gpackage $gname + } + + page_info " Grammar: $gname" + page_info " Package: $gpackage" + if {$gcopy ne ""} { + page_info " Copyright: $gcopy" + } + + if {$gstart ne ""} { + set match [textutil::indent \ + [$t get $gstart MATCH] \ + " "] + } else { + page_error " No start expression." + set match "" + } + + set crules {} + set rules {} + set support [$t get [$t get root start] SUPPORT] + if {[string length $support]} { + lappend rules $support + lappend rules {} + } + + lappend crules "# Grammar '$gname'" + lappend crules {#} + + array set def [$t get root definitions] + foreach sym [lsort -dict [array names def]] { + lappend crules [Pfx "# " [$t get $def($sym) EXPR]] + lappend crules {#} + + lappend rules [$t get $def($sym) TEXT] + lappend rules {} + } + set rules [join [lrange $rules 0 end-1] \n] + + lappend crules {} + lappend crules $rules + + set crules [join $crules \n] + + # @PKG@ and @NAME@ are handled after the other expansions as their + # contents may insert additional instances of these placeholders. + + $t set root TEXT \ + [string map \ + [list \ + @NAME@ $gname \ + @PKG@ $gpackage \ + @COPY@ $gcopyright] \ + [string map \ + [list \ + @MATCH@ $match \ + @RULES@ $crules \ + ] $template]] + return +} + +proc ::page::gen::peg::me::SynthNode/Nonterminal {t n} { + # This is the root of a definition. + # + # The text is a procedure wrapping the match code of its + # expression into the required the nonterminal handling (caching + # and such), plus the support code for the expression matcher. + + set sym [$t get $n symbol] + set label [$t get $n label] + set gen [$t get $n gen] + set mode [$t get $n mode] + + set pe [lindex [$t children $n] 0] + set egen [$t get $pe gen] + set esupport [$t get $pe SUPPORT] + set ematch [$t get $pe MATCH] + set eexpr [$t get $pe EXPR] + + # Combine the information. + + set sexpr [Cat "$sym = " $eexpr] + + set match {} + #lappend match "puts stderr \"$label << \[icl_get\]\"" + #lappend match {} + lappend match [Pfx "# " $sexpr] + lappend match {} + if {$gen} { + lappend match {variable ok} + lappend match "if \{\[inc_restore $label\]\} \{" + lappend match " if \{\$ok\} ias_push" + #lappend match " puts stderr \">> $label = \$ok (c) \[icl_get\]\"" + lappend match " return" + lappend match "\}" + } else { + set eop [$t get $pe op] + if { + ($eop eq "t") || ($eop eq "..") || + ($eop eq "alpha") || ($eop eq "alnum") + } { + # Required iff !dot + # Support for terminal expression + lappend match {variable ok} + } + + #lappend match "variable ok" + lappend match "if \{\[inc_restore $label\]\} return" + #lappend match "if \{\[inc_restore $label\]\} \{" + #lappend match " puts stderr \">> $label = \$ok (c) \[icl_get\]\"" + #lappend match " return" + #lappend match "\}" + } + lappend match {} + lappend match {set pos [icl_get]} + if {$egen} { + # [*] Needed for removal of SV's from stack after handling by + # this symbol, only if expression actually generates an SV. + lappend match {set mrk [ias_mark]} + } + lappend match {} + lappend match $ematch + lappend match {} + + switch -exact -- $mode { + value {lappend match "isv_nonterminal_reduce $label \$pos \$mrk"} + match {lappend match "isv_nonterminal_range $label \$pos"} + leaf {lappend match "isv_nonterminal_leaf $label \$pos"} + discard {lappend match "isv_clear"} + default {return -code error "Bad nonterminal mode \"$mode\""} + } + + lappend match "inc_save $label \$pos" + if {$egen} { + # See [*], this is the removal spoken about before. + lappend match {ias_pop2mark $mrk} + } + if {$gen} { + lappend match {if {$ok} ias_push} + } + lappend match "ier_nonterminal \"Expected $label\" \$pos" + #lappend match "puts stderr \">> $label = \$ok \[icl_get\]\"" + lappend match return + + # Final assembly + + set pname [Call $sym] + set match [list [Proc $pname [join $match \n]]] + + if {[string length $esupport]} { + lappend match {} + lappend match $esupport + } + + $t set $n TEXT [join $match \n] + $t set $n EXPR $sexpr + return +} + +proc ::page::gen::peg::me::SynthNode/? {t n} { + # The expression e? is equivalent to e/epsilon. + # And like this it is compiled. + + set pe [lindex [$t children $n] 0] + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set egen [$t get $pe gen] + set sexpr "[Cat "(? " $eexpr])" + + set match {} + lappend match {} + lappend match [Pfx "# " $sexpr] + lappend match {} + lappend match {variable ok} + lappend match {} + lappend match {set pos [icl_get]} + lappend match {} + lappend match {set old [ier_get]} + lappend match $ematch + lappend match {ier_merge $old} + lappend match {} + lappend match {if {$ok} return} + lappend match {icl_rewind $pos} + lappend match {iok_ok} + lappend match {return} + + # Final assembly + + set pname [NextProc $t opt] + set match [list [Proc $pname [join $match \n]]] + if {[string length $esupport]} { + lappend match {} + lappend match $esupport + } + + $t set $n EXPR $sexpr + $t set $n MATCH [Cat "$pname ; " [Pfx "# " $sexpr]] + $t set $n SUPPORT [join $match \n] + return +} + +proc ::page::gen::peg::me::SynthNode/* {t n} { + # Kleene star is like a repeated ? + + # Note: Compilation as while loop, as done now + # means that the parser has no information about + # the intermediate structure of the input in his + # cache. + + # Future: Create a helper symbol X and compile + # the expression e = e'* as: + # e = X; X <- (e' X)? + # with match data for X put into the cache. This + # is not exactly equivalent, the structure of the + # AST is different (right-nested tree instead of + # a list). This however can be handled with a + # special nonterminal mode to expand the current + # SV on the stack. + + # Note 2: This is a transformation which can be + # done on the grammar itself, before the actual + # backend is let loose. This "strength reduction" + # allows us to keep this code here. + + set pe [lindex [$t children $n] 0] + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set egen [$t get $pe gen] + set sexpr "[Cat "(* " $eexpr])" + + set match {} + lappend match {} + lappend match [Pfx "# " $sexpr] + lappend match {} + lappend match {variable ok} + lappend match {} + lappend match "while \{1\} \{" + lappend match { set pos [icl_get]} + lappend match {} + lappend match { set old [ier_get]} + lappend match [textutil::indent $ematch " "] + lappend match { ier_merge $old} + lappend match {} + lappend match { if {$ok} continue} + lappend match { break} + lappend match "\}" + lappend match {} + lappend match {icl_rewind $pos} + lappend match {iok_ok} + lappend match {return} + + # Final assembly + + set pname [NextProc $t kleene] + set match [list [Proc $pname [join $match \n]]] + if {[string length $esupport]} { + lappend match {} + lappend match $esupport + } + + $t set $n MATCH [Cat "$pname ; " [Pfx "# " $sexpr]] + $t set $n SUPPORT [join $match \n] + $t set $n EXPR $sexpr + return +} + +proc ::page::gen::peg::me::SynthNode/+ {t n} { + # Positive Kleene star x+ is equivalent to x x* + # This is how it is compiled. See also the notes + # at the * above, they apply in essence here as + # well, except that the transformat scheme is + # slighty different: + # + # e = e'* ==> e = X; X <- e' X? + + set pe [lindex [$t children $n] 0] + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set egen [$t get $pe gen] + set sexpr "[Cat "(+ " $eexpr])" + + set match {} + lappend match {} + lappend match [Pfx "# " $sexpr] + lappend match {} + lappend match {variable ok} + lappend match {} + lappend match {set pos [icl_get]} + lappend match {} + lappend match {set old [ier_get]} + lappend match $ematch + lappend match {ier_merge $old} + lappend match {} + lappend match "if \{!\$ok\} \{" + lappend match { icl_rewind $pos} + lappend match { return} + lappend match "\}" + lappend match {} + lappend match "while \{1\} \{" + lappend match { set pos [icl_get]} + lappend match {} + lappend match { set old [ier_get]} + lappend match [textutil::indent $ematch " "] + lappend match { ier_merge $old} + lappend match {} + lappend match { if {$ok} continue} + lappend match { break} + lappend match "\}" + lappend match {} + lappend match {icl_rewind $pos} + lappend match {iok_ok} + lappend match {return} + + # Final assembly + + set pname [NextProc $t pkleene] + set match [list [Proc $pname [join $match \n]]] + if {[string length $esupport]} { + lappend match {} + lappend match $esupport + } + + $t set $n MATCH [Cat "$pname ; " [Pfx "# " $sexpr]] + $t set $n SUPPORT [join $match \n] + $t set $n EXPR $sexpr + return +} + +proc ::page::gen::peg::me::SynthNode// {t n} { + set args [$t children $n] + + if {![llength $args]} { + error "PANIC. Empty choice." + + } elseif {[llength $args] == 1} { + # A choice over one branch is no real choice. The code + # generated for the child applies here as well. + + set pe [lindex $args 0] + $t set $n MATCH [$t get $pe MATCH] + $t set $n SUPPORT [$t get $pe SUPPORT] + return + } + + # Choice over at least two branches. + + set match {} + set support {} + set sexpr {} + + lappend match {} + lappend match {} + lappend match {variable ok} + lappend match {} + lappend match {set pos [icl_get]} + foreach pe $args { + lappend match {} + + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set egen [$t get $pe gen] + + # Note: We do not check for static match results. Doing so is + # an optimization we can do earlier, directly on the tree. + + lappend sexpr $eexpr + + if {[string length $esupport]} { + lappend support {} + lappend support $esupport + } + + if {$egen} { + lappend match "set mrk \[ias_mark\]" + } + + lappend match "set old \[ier_get\]" + lappend match $ematch + lappend match "ier_merge \$old" + lappend match {} + lappend match "if \{\$ok\} return" + + if {$egen} { + lappend match "ias_pop2mark \$mrk" + } + lappend match "icl_rewind \$pos" + } + lappend match {} + lappend match return + + # Final assembly + + set sexpr "[Cat "(/ " [join $sexpr \n]])" + set match [linsert $match 1 [Pfx "# " $sexpr]] + + set pname [NextProc $t bra] + set match [list [Proc $pname [join $match \n]]] + if {[llength $support]} { + lappend match {} + lappend match [join [lrange $support 1 end] \n] + } + + $t set $n MATCH [Cat "$pname ; " [Pfx "# " $sexpr]] + $t set $n SUPPORT [join $match \n] + $t set $n EXPR $sexpr + return +} + +proc ::page::gen::peg::me::SynthNode/x {t n} { + set args [$t children $n] + + if {![llength $args]} { + error "PANIC. Empty sequence." + + } elseif {[llength $args] == 1} { + # A sequence of one element is no real sequence. The code + # generated for the child applies here as well. + + set pe [lindex $args 0] + $t set $n MATCH [$t get $pe MATCH] + $t set $n SUPPORT [$t get $pe SUPPORT] + $t set $n EXPR [$t get $pe EXPRE] + return + } + + # Sequence of at least two elements. + + set match {} + set support {} + set sexpr {} + set gen 0 + + lappend match {} + lappend match {} + lappend match {variable ok} + lappend match {} + lappend match {set pos [icl_get]} + + foreach pe $args { + lappend match {} + + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set egen [$t get $pe gen] + + lappend sexpr $eexpr + + if {[string length $esupport]} { + lappend support {} + lappend support $esupport + } + + if {$egen && !$gen} { + # From here on out is the sequence + # able to generate semantic values + # which have to be canceled when + # backtracking. + + lappend match "set mrk \[ias_mark\]" + lappend match {} + set gen 1 + } + + lappend match "set old \[ier_get\]" + lappend match $ematch + lappend match "ier_merge \$old" + lappend match {} + + if {$gen} { + lappend match "if \{!\$ok\} \{" + lappend match " ias_pop2mark \$mrk" + lappend match " icl_rewind \$pos" + lappend match " return" + lappend match "\}" + } else { + lappend match "if \{!\$ok\} \{icl_rewind \$pos \; return\}" + } + } + lappend match {} + lappend match return + + # Final assembly + + set sexpr "[Cat "(x " [join $sexpr \n]])" + set match [linsert $match 1 [Pfx "# " $sexpr]] + + set pname [NextProc $t seq] + set match [list [Proc $pname [join $match \n]]] + if {[llength $support]} { + lappend match {} + lappend match [join [lrange $support 1 end] \n] + } + + $t set $n MATCH [Cat "$pname ; " [Pfx "# " $sexpr]] + $t set $n SUPPORT [join $match \n] + $t set $n EXPR $sexpr + return +} + +proc ::page::gen::peg::me::SynthNode/& {t n} { + SynthLookahead $t $n no + return +} + +proc ::page::gen::peg::me::SynthNode/! {t n} { + SynthLookahead $t $n yes + return +} + +proc ::page::gen::peg::me::SynthNode/dot {t n} { + SynthTerminal $t $n \ + "any character" {} + $t set $n EXPR "(dot)" + return +} + +proc ::page::gen::peg::me::SynthNode/epsilon {t n} { + $t set $n MATCH iok_ok + $t set $n SUPPORT {} + $t set $n EXPR "(epsilon)" + return +} + +proc ::page::gen::peg::me::SynthNode/alnum {t n} { + SynthClass $t $n alnum + return +} + +proc ::page::gen::peg::me::SynthNode/alpha {t n} { + SynthClass $t $n alpha + return +} + +proc ::page::gen::peg::me::SynthNode/.. {t n} { + # Range is [x-y] + + set b [$t get $n begin] + set e [$t get $n end] + + set tb [quote'tcl $b] + set te [quote'tcl $e] + + set pb [quote'tclstr $b] + set pe [quote'tclstr $e] + + set cb [quote'tclcom $b] + set ce [quote'tclcom $e] + + SynthTerminal $t $n \ + "\\\[${pb}..${pe}\\\]" \ + "ict_match_tokrange $tb $te" + $t set $n EXPR "(.. $cb $ce)" + return +} + +proc ::page::gen::peg::me::SynthNode/t {t n} { + # Terminal node. Primitive matching. + # Code is parameterized by gen(X) of this node X. + + set ch [$t get $n char] + set tch [quote'tcl $ch] + set pch [quote'tclstr $ch] + set cch [quote'tclcom $ch] + + SynthTerminal $t $n \ + $pch \ + "ict_match_token $tch" + $t set $n EXPR "(t $cch)" + return +} + +proc ::page::gen::peg::me::SynthNode/n {t n} { + # Nonterminal node. Primitive matching. + # The code is parameterized by acc(X) of this node X, and gen(D) + # of the invoked nonterminal D. + + set sym [$t get $n sym] + set def [$t get $n def] + + if {$def eq ""} { + # Invokation of an undefined nonterminal. This will always fail. + set match "iok_fail ; # Match for undefined symbol '$sym'." + } else { + # Combinations + # Acc Gen Action + # --- --- ------ + # 0 0 Plain match + # 0 1 Match with canceling of the semantic value. + # 1 0 Plain match + # 1 1 Plain match + # --- --- ------ + + if {[$t get $n acc] || ![$t get $def gen]} { + set match [Call $sym] + } else { + set match {} + lappend match "set p$sym \[ias_mark\]" + lappend match [Call $sym] + lappend match "ias_pop2mark \$p$sym" + set match [join $match \n] + } + } + + set sexpr "(n $sym)" + $t set $n EXPR $sexpr + $t set $n MATCH "$match ; # $sexpr" + $t set $n SUPPORT {} + return +} + +proc ::page::gen::peg::me::SynthLookahead {t n negated} { + # Note: Per the rules about expression modes (! is a lookahead + # ____| operator) this node has a mode of 'discard', and its child + # ____| has so as well. + + # assert t get n mode == discard + # assert t get pe mode == discard + + set op [$t get $n op] + set pe [lindex [$t children $n] 0] + set eop [$t get $pe op] + set ematch [$t get $pe MATCH] + set esupport [$t get $pe SUPPORT] + set eexpr [$t get $pe EXPR] + set pname [NextProc $t bang] + + set match {} + + if { + ($eop eq "t") || ($eop eq "..") || + ($eop eq "alpha") || ($eop eq "alnum") + } { + # Required iff !dot + # Support for terminal expression + lappend match {variable ok} + lappend match {} + } + + lappend match {set pos [icl_get]} + lappend match {} + lappend match $ematch + lappend match {} + lappend match {icl_rewind $pos} + + if {$negated} { + lappend match {iok_negate} + } + + lappend match return + + set match [list [Proc $pname [join $match \n]]] + if {[string length $esupport]} { + lappend match {} + lappend match $esupport + } + + $t set $n MATCH $pname + $t set $n SUPPORT [join $match \n] + $t set $n EXPR "($op $eexpr)" + return +} + +proc ::page::gen::peg::me::SynthClass {t n op} { + SynthTerminal $t $n \ + <$op> \ + "ict_match_tokclass $op" + $t set $n EXPR ($op) + return +} + +proc ::page::gen::peg::me::SynthTerminal {t n msg cmd} { + set match {} + lappend match "ict_advance \"Expected $msg (got EOF)\"" + + if {$cmd ne ""} { + lappend match "if \{\$ok\} \{$cmd \"Expected $msg\"\}" + } + if {[$t get $n gen]} { + lappend match "if \{\$ok\} isv_terminal" + } + + $t set $n MATCH [join $match \n] + $t set $n SUPPORT {} + return +} + +proc ::page::gen::peg::me::Call {sym} { + # Generator for proc names (nonterminal symbols). + return matchSymbol_$sym +} + +proc ::page::gen::peg::me::NextProc {t {mark {}}} { + set count [$t get root Pcount] + incr count + $t set root Pcount $count + return e$mark$count +} + +proc ::page::gen::peg::me::Proc {name body} { + set script {} + lappend script "proc ::@PKG@::$name \{\} \{" + lappend script [::textutil::indent $body " "] + lappend script "\}" + return [join $script \n] +} + +proc ::page::gen::peg::me::Cat {prefix suffix} { + return "$prefix[textutil::indent $suffix [textutil::blank [string length $prefix]] 1]" +} + +proc ::page::gen::peg::me::Pfx {prefix suffix} { + return [textutil::indent $suffix $prefix] +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::me { + + variable here [file dirname [info script]] + variable template_file [file join $here gen_peg_me.template] + + variable ch + variable template \ + [string trimright [read [set ch [open $template_file r]]][close $ch]] + unset ch + + variable package "" + variable copyright "" +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::me 0.1 diff --git a/tcllib/modules/page/gen_peg_me.template b/tcllib/modules/page/gen_peg_me.template new file mode 100644 index 0000000..070e671 --- /dev/null +++ b/tcllib/modules/page/gen_peg_me.template @@ -0,0 +1,61 @@ +# -*- tcl -*- +## Parsing Expression Grammar '@NAME@'. +## Recursive Descent Packrat parser generated +## by the PAGE writer plugin 'me'. +@COPY@ +# ### ### ### ######### ######### ######### +## Package description + +# The commands provided here match an input provided through a buffer +# command to the PE grammar '@NAME@'. The parser is based on the package +# 'grammar::me::tcl' (recursive-descent, packrat, pulling chars, +# pushing the generated AST). + +# ### ### ### ######### ######### ######### +## Requisites + +package require grammar::me::tcl + +# ### ### ### ######### ######### ######### +## Implementation + +namespace eval ::@PKG@ { + # Import the virtual machine for matching. + + namespace import ::grammar::me::tcl::* + upvar #0 ::grammar::me::tcl::ok ok +} + +# ### ### ### ######### ######### ######### +## API Implementation. + +proc ::@PKG@::parse {nxcmd emvar astvar} { + variable ok + variable se + + upvar 1 $emvar emsg $astvar ast + + init $nxcmd + +@MATCH@ + + isv_nonterminal_reduce ALL -1 + set ast [sv] + if {!$ok} { + foreach {l m} [ier_get] break + lappend l [lc $l] + set emsg [list $l $m] + } + + return $ok +} + +# ### ### ### ######### ######### ######### +## Internal helper methods + +@RULES@ + +# ### ### ### ######### ######### ######### +## Package Management + +package provide @PKG@ 0.1 diff --git a/tcllib/modules/page/gen_peg_mecpu.tcl b/tcllib/modules/page/gen_peg_mecpu.tcl new file mode 100644 index 0000000..e0b49aa --- /dev/null +++ b/tcllib/modules/page/gen_peg_mecpu.tcl @@ -0,0 +1,289 @@ +# -*- tcl -*- +# +# Copyright (c) 2006 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - Generate a grammar::me::cpu based parser. + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### + +## The input is a grammar, not as tree, but as a list of instructions +## (symbolic form). This backend converts that into machinecode for +## grammar::m::cpu::core and inserts the result into a template file. + +## The translation from grammar tree to assembler code was done in a +## preceding transformation. + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. + +package require grammar::me::cpu::core +package require textutil + +#package require page::analysis::peg::emodes +#package require page::util::quote +#package require page::util::peg + +namespace eval ::page::gen::peg::mecpu {} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::mecpu::package {text} { + variable package $text + return +} + +proc ::page::gen::peg::mecpu::copyright {text} { + variable copyright $text + return +} + +proc ::page::gen::peg::mecpu::template {path} { + variable template $path + return +} + +proc ::page::gen::peg::mecpu::cmarker {list} { + variable cmarker $list + return +} + +proc ::page::gen::peg::mecpu {asmcode chan} { + + # asmcode = list (name code) + # code = list (instruction) + # instruction = list (label name arg...) + + variable mecpu::package + variable mecpu::copyright + variable mecpu::cmarker + variable mecpu::template + variable mecpu::template_file + + # Import the config options, provide fallback to defaults for the + # unspecified parts. + + set gname [lindex $asmcode 0] + set gcode [lindex $asmcode 1] + + if {$package eq ""} {set package $gname} + + page_info " Grammar: $gname" + page_info " Package: $package" + + if {$copyright ne ""} { + page_info " Copyright: $copyright" + set copyright "\#\# (C) $copyright\n" + } + + if {$template eq ""} { + set template $template_file + } + + page_info " Template: $template" + + # Translate the incoming assembler to machine code. + + set mcode [grammar::me::cpu::core::asm $gcode] + + # We know that the machine code has three parts (instructions, + # string pool, token map). We take the data apart to allow separate + # insertion if the template so chooses (like for readability). + + foreach {minsn mpool mtmap} $mcode break + + set fminsn {} ; set i 0 ; set j 19 + while {$i < [llength $minsn]} { + append fminsn " [lrange $minsn $i $j]\n" + incr i 20 ; incr j 20 + } + + set fmpool {} ; set i 0 ; set j 4 + while {$i < [llength $mpool]} { + append fmpool " [lrange $mpool $i $j]\n" + incr i 5 ; incr j 5 + } + + # ------------------------------------ + # We also generate a readable representation of the assembler + # instructions for insertion into a comment area. + + set asmp [mecpu::2readable $gcode $minsn] + + # ------------------------------------ + + # And write the modified template + puts $chan [string map [list \ + @NAME@ $gname \ + @PKG@ $package \ + @COPY@ $copyright \ + @CODE@ $mcode \ + @INSN@ $minsn \ + @FNSN@ $fminsn \ + @POOL@ $mpool \ + @FPOL@ $fmpool \ + @TMAP@ $mtmap \ + @ASMP@ $asmp \ + ] [mecpu::Template]] + return +} + +proc ::page::gen::peg::mecpu::Template {} { + variable template + return [string trimright [read [set ch [open $template r]]][close $ch]] +} + +proc ::page::gen::peg::mecpu::2readable {asmcode mecode} { + return [2print $asmcode $mecode max [widths $asmcode max]] +} + +proc ::page::gen::peg::mecpu::widths {asmcode mv} { + upvar 1 $mv max + + # First iteration, column widths (instructions, and arguments). + # Ignore comments, they go across all columns. + # Also ignore labels (lrange 1 ..). + + set mc 0 + foreach insn $asmcode { + set i [lindex $insn 1] + if {$i eq ".C"} continue + set col 0 + + foreach x [lrange $insn 1 end] { + set xlen [string length $x] + if {![info exists max($col)] || ($xlen > $max($col))} {set max($col) $xlen} + incr col + + # Shift the strings of various commands into the third + # column, if they are not already there. + + if {$i eq "ier_nonterminal"} {incr col ; set i ""} + if {$i eq "isv_nonterminal_leaf"} {incr col ; set i ""} + if {$i eq "isv_nonterminal_range"} {incr col ; set i ""} + if {$i eq "isv_nonterminal_reduce"} {incr col ; set i ""} + if {$i eq "inc_save"} {incr col ; set i ""} + if {$i eq "ict_advance"} {incr col ; set i ""} + } + if {$col > $mc} {set mc $col} + } + + set max($mc) 0 + return $mc +} + +proc ::page::gen::peg::mecpu::2print {asmcode mecode mv mc} { + variable cmarker + upvar 1 $mv max + + set lines {} + set pc 0 + + foreach insn $asmcode { + foreach {label name} $insn break + if {$name eq ".C"} {lappend lines "" "-- [join [lrange $insn 2 end] " "]" ""} + if {$label ne ""} {lappend lines " ${label}:" } + if {$name eq ".C"} continue + + set line " [format %05d $pc] " + + set pcs $pc + incr pc [llength $insn] ; incr pc -1 + set pce $pc ; incr pce -1 + set imecode [lrange $mecode $pcs $pce] + + if { + ($name eq "ier_nonterminal") || + ($name eq "isv_nonterminal_leaf") || + ($name eq "isv_nonterminal_range") || + ($name eq "isv_nonterminal_reduce") || + ($name eq "inc_save") || + ($name eq "ict_advance") + } { + # Shift first argument into 2nd column, and quote it as well. + set insn [lreplace $insn 2 2 "" '[lindex $insn 2]'] + } elseif { + ($name eq "inc_restore") || + ($name eq "ict_match_token") || + ($name eq "ict_match_tokclass") + } { + # Command with quoted arguments, no shifting. + set insn [lreplace $insn 3 3 '[lindex $insn 3]'] + } elseif { + ($name eq "ict_match_tokrange") + } { + # Command with quoted arguments, no shifting. + set insn [lreplace $insn 4 4 '[lindex $insn 4]'] + } + + while {[llength $insn] <= $mc} {lappend insn ""} + lappend insn "-- $imecode" + + set col 0 + foreach x [lrange $insn 1 end] { + set xlen [string length $x] + append line " " + append line $x + append line [string repeat " " [expr {$max($col) - $xlen}]] + incr col + } + + lappend lines $line + } + + # Wrap the lines into a comment. + + if {$cmarker eq ""} {set cmarker "\#"} + + if {[llength $cmarker] > 1} { + # Comments are explictly closed as well. + + foreach {cs ce} $cmarker break + return "$cs [join $lines " $ce\n$cs "] $ce" + } else { + # Comments are not explicitly closed. Implicit by end-of-line + + return "$cmarker [join $lines "\n$cmarker "]" + } +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::mecpu { + + variable here [file dirname [info script]] + variable template_file [file join $here gen_peg_mecpu.template] + + variable package "" + variable copyright "" + variable template "" + variable cmarker "" +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::mecpu 0.1 diff --git a/tcllib/modules/page/gen_peg_mecpu.template b/tcllib/modules/page/gen_peg_mecpu.template new file mode 100644 index 0000000..38cf901 --- /dev/null +++ b/tcllib/modules/page/gen_peg_mecpu.template @@ -0,0 +1,48 @@ +# -*- tcl -*- +## Parsing Expression Grammar '@NAME@'. +## Recursive Descent Packrat parser generated +## by the PAGE writer plugin 'mecpu'. +@COPY@ +# ### ### ### ######### ######### ######### +## Package description + +# The commands provided here match an input provided through a buffer +# command to the PE grammar '@NAME@'. The parser is based on the ME +# virtual machine (recursive-descent, packrat, pulling chars, +# pushing the generated AST, suspendable). + +# ### ### ### ######### ######### ######### +## Requisites + +# Import the virtual machine for matching. +package require grammar::me::cpu + +# ### ### ### ######### ######### ######### +## Implementation + +snit::type ::@PKG@ { + constructor {} { + set cpu [grammar::me::cpu ${selfns}::cpu $mecode] + return + } + variable cpu + delegate method * to cpu + + typevariable mecode { + { +@FNSN@ } + { +@FPOL@ } + {@TMAP@} + } +} + +# ### ### ### ######### ######### ######### +## Documentation. Readable form of 'mecode' above. + +@ASMP@ + +# ### ### ### ######### ######### ######### +## Package Management + +package provide @PKG@ 0.1 diff --git a/tcllib/modules/page/gen_peg_ser.tcl b/tcllib/modules/page/gen_peg_ser.tcl new file mode 100644 index 0000000..7fb8266 --- /dev/null +++ b/tcllib/modules/page/gen_peg_ser.tcl @@ -0,0 +1,63 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - PEG as serialized PEG container. + +# ### ### ### ######### ######### ######### +## Requisites + +package require grammar::peg +package require page::util::quote +package require page::util::peg + +namespace eval ::page::gen::peg::ser { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl), and other utilities. + + namespace import ::page::util::quote::* + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::peg::ser {t chan} { + ser::printWarnings [ser::getWarnings $t] + + ::grammar::peg gr + + set gstart [$t get root start] + if {$gstart ne ""} { + gr start [ser::peOf $t $gstart] + } else { + page_info "No start expression." + } + + foreach {sym def} [$t get root definitions] { + set eroot [lindex [$t children $def] 0] + + gr nonterminal add $sym [ser::peOf $t $eroot] + gr nonterminal mode $sym [$t get $def mode] + } + + puts $chan [gr serialize] + gr destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::peg::ser::GetRules {t} { + return $res +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::gen::peg::ser {} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::peg::ser 0.1 diff --git a/tcllib/modules/page/gen_tree_text.tcl b/tcllib/modules/page/gen_tree_text.tcl new file mode 100644 index 0000000..44b674f --- /dev/null +++ b/tcllib/modules/page/gen_tree_text.tcl @@ -0,0 +1,94 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Backend - Dump (A)ST for inspection. + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::quote + +namespace eval ::page::gen::tree::text { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl) + + namespace import ::page::util::quote::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::gen::tree::text {t chan} { + set indent "" + set bystr " " + set bysiz [string length $bystr] + set byoff end-$bysiz + + $t walk root -order both -type dfs {a n} { + if {$a eq "enter"} { + text::WriteNode $indent $chan $t $n + append indent $bystr + } else { + set indent [string range $indent 0 $byoff] + } + } + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::gen::tree::text::WriteNode {indent chan t n} { + array set attr [$t getall $n] + + if {[array size attr] == 0} { + puts $chan "$indent$n <>" + } else { + puts -nonewline $chan "$indent$n < " + + set max -1 + set d {} + foreach k [array names attr] { + set l [string length $k] + if {$l > $max} {set max $l} + lappend d [list $k [Quote $attr($k)] $l] + } + + if {[llength $d] == 1} { + puts $chan "$k = $attr($k) >" + return + } + + set first 1 + set space $indent[string repeat " " [string length "$n < "]] + + foreach e [lsort -dict -index 0 $d] { + foreach {k v l} $e break + set off [string repeat " " [expr {$max-$l}]] + + if {$first} { + puts -nonewline $chan "$k$off = $v" + set first 0 + } else { + puts -nonewline $chan "\n$space$k$off = $v" + } + } + + puts $chan " >" + } +} + +proc ::page::gen::tree::text::Quote {str} { + return $str + + set res "" + foreach c [split $str {}] { + append res [quote'tcl $c] + } + return $res +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::gen::tree::text 0.1 diff --git a/tcllib/modules/page/notes/doc_emodes.txt b/tcllib/modules/page/notes/doc_emodes.txt new file mode 100644 index 0000000..dd9ea19 --- /dev/null +++ b/tcllib/modules/page/notes/doc_emodes.txt @@ -0,0 +1,180 @@ +PE Grammar Tree, after ExprMode Computation +=========================================== + +This file documents the augmentations to a Normalized PE Grammar Tree +as inserted by the transformational package 'pg::peg::emodes'. The +input to the transformation is assumed to be a 'Normalized PE Grammar +Tree' as described in 'doc_normalize.txt', possibly with augmentations +from other transformation, as long as they are not in conflict with +the augmentations specified here. + +Background +---------- + +The purpose of the computations and transformations specified here is +to determine as much static information as we can about the generation +of semantic values from terminal and non-terminal matchers. Such +information can then be used to reduce the places in a parser which +generate and/or handle AST nodes to the bare minimum. I.e we wish to +avoid the generation of AST nodes by the matcher for which we are sure +that they will be thrown away by the matcher either immediately, or +later on. + +The fundamental information from which everything else is derived are +the optional 'mode's which can specified for non-terminal symbols (See +nonterminal 'Attribute' in the PEG Grammar). They are the hints to the +matcher on how semantic values are generated and used. + +The mode of a nonterminal symbol controls three things at the same +time: What it does with semantic values generated by its associated +expression, if it generates its own semantic value, and how. These +three aspects are interelated to each other, hence using one piece of +information to set all of them. For this discussion the aspect of +"how" is not relevant, except where it intersects with the other two. + +For the first aspect, the nonterminal has two choices, to either keep +the semantic value from its expression, or to discard it. I.e this is +about accepting versus rejection of the value. This aspect is +'Acceptance'. + +For the second aspect the nonterminal has three choices, to either +generate a value, to not generate a value at all, or to simply pass +through whatever was done by its expression (a maybe). This aspect is +'Generation'. + +The interelation of these aspects shows up as impossible +combinations when selecting a value from each set, as made explicit in +the next table: + + Generation x Acceptance -> Mode + ---------- ---------- ---- + Maybe Yes value + Yes Yes value + No Yes -- impossible -- + Maybe No -- impossible -- + Yes No match, leaf [1] + No No discard + ---------- ---------- ---- + + [1] Here the third aspect, the "how" of the generation comes + into play to distinguish these two modes. As said, this + distinction is not relevant for the current discussion. + + +What we now wish to achieve is to define the vales for these two +aspects for all expression and definition modes, which, starting from +the settings of the modes by the input grammar, is the most definite, +restrictive and conservative as possible. + +- Most definite, try to remove as many Maybe's as possible in favor of + Yes or No. + +- Most restrictive, prefer a No over Yes when trying to remove a Maybe. + +- Most conversative, do not try to remove a Maybe if we are not truly + sure about its value. + +The two node properties computes are + +* gen(X): Node -> {Yes, No, Maybe} + + Yes: The node X definitely generates a semantic + value. + + No: The node X definitely does not generate a + semantic value. + + Maybe: The node X passes semantic values, should they + be generated by a child of X. We do not know + if values are generated or not. + + A derivative function is gen', defined as + + gen'(X,G): Node x {Yes, No, Pass} -> Bool + gen'(X,G) = (gen(X) == G) + + Conversely we could define gen in terms of gen' as + + gen (X) = G, iff gen'(X,G) + +* acc(X): Node -> Bool + + True: The node X will keep semantic values generated + by its children. + + False: The node X will throw any semantic values + generated by its children away. + +General information +------------------- + +* The specification of 'doc_normalize.txt' applies fully. + +Structure +--------- + +* The structure of the tree is unchanged. + +Attributes +---------- + +Name Type Details +---- ---- ------- +pg::peg::emodes + none + Root node only. The presence of this attribute + indicates that the emode computation has been been + executed on the grammar and that we have valid + results. + + Transformations whose execution invalidates the emode + information have to remove this marker. +==== ==== ======= +gen enum Expression and definition nodes only. Values are in + {yes, no, maybe}. Contains the value of gen(X). +---- ---- ------- +acc bool Expression and definition nodes only. Contains the + value of acc(X). +---- ---- ------- + +Transformation +-------------- + +It is possible to specify a mode transformation based on the mode +computation. It would resolve discontinuities in the gen/acc stati +(Use of expressions generating a value where none is accepted) by +duplicating relevant nonterminal definitions and forcing them into +specific modes (discard). If expressions to be duplicated contain +calls to undefined nonterminal symbols then the new definitions will +do so as well. + +This transformation will not be written for now. The reason for that +is that it essentially defeats packrat parsing. + +The definitions which are duplicated are used in both contexts which +accept and such which do not accept the generated value, otherwise the +regular analysis would have mode the definition itself non-accepting +and non-generating. + +While it is possible to put the match status of both the original and +duplicated definition into the packrat cache under the same label +there is one important distinction which cannot be avoided: The +duplicated definition does not generate a semantic value. And we +cannot exclude the possibilities that either a cached semantic value +is used in a non-accepting context which is not prepared to handle +this value (by discarding it), or that an accepting context uses +cached information which is short of one expected semantic value. + +So the two definitions have to cache their information under different +labels to avoid a mixup. But now it becomes possible that the match +engine has to fully reparse a segment of input during backtracking +despite actually having information about matchability, just under a +different label in the cache. And this then costs us the O(n) of the +packrat parser, pushing it back exponential time-complexity. + +Conclusion: The described transformation can be applied if and only if +we have ensured that the matcher will never backtrack in the input for +the grammar in question. In other words, other transformation like +left-factorisation, eliminiation of left-recursion etc. have to be +applied first. In even other words, the grammar has to be LL(1) (which +implies that it will not use any lookahead operators either). diff --git a/tcllib/modules/page/notes/doc_emodes_alg.txt b/tcllib/modules/page/notes/doc_emodes_alg.txt new file mode 100644 index 0000000..31b9508 --- /dev/null +++ b/tcllib/modules/page/notes/doc_emodes_alg.txt @@ -0,0 +1,171 @@ +PE Grammar Tree, after ExprMode Computation +=========================================== + +This file is a companion to the file 'doc_emodes.txt'. The former +describes the node attributes we wish to have, here we formally +specify the properties of the attributes, and then derive from that an +algorithm for their computation. + +Per node +-------- + +First we specify the properties of the attributes on a case by case +basis, for each possible type of node. This may invole a lot of +repetition, but this detail is necessary to be able to the patterns in +the definition which then allow us to simplify things. + +Legend +~~~~~~ + X Current node. + parent(X) Parent node of X. + users(X) n-Nodes invoking the definition. + def(X) Definition node invoked by X. + children(X) Set of all children of X. + child(X) Child of X (if X has only a single child) + |S| Cardinality of the set S. + AllYes(X) gen'(Y,yes), for all Y in children(X). + AllNo(X) gen'(Y,no), for all Y in children(X). + SomeYes(X) gen'(Y,yes), exists Y in children(X). + SomeNo(X) gen'(Y,no), exists Y in children(X). + Mode(X) Nonterminal mode provided by the input. + Discard(X) Mode(X) == discard + Value(X) Mode(X) == value + Data(X) Mode(X) in {match, leaf} + + +Node type acc(X) gen(X) +~~~~~~~~~ ~~~~~~ ~~~~~~ +DEF FALSE, !Value(X) || yes, Data(X) || (Value(X) && AllYes(X)) + !acc(child(X)) || no, Discard(X) || (Value(X) && AllNo(X)) + USER || maybe, Value(X) && !AllYes(X) && !AllNo(X) + gen(X,no) + TRUE, else + + USER = (|Users(X)| == 1) && + !acc(Users(X)) +~~~~~~~~~ ~~~~~~ ~~~~~~ +!, & FALSE no +~~~~~~~~~ ~~~~~~ ~~~~~~ +?, * acc(parent(X)) no, AllNo(X) [2] + maybe, else +~~~~~~~~~ ~~~~~~ ~~~~~~ ++ acc(parent(X)) yes, AllYes(X) + no, AllNo(X) + maybe, else +~~~~~~~~~ ~~~~~~ ~~~~~~ +x acc(parent(X)) yes, SomeYes(X) [3] + no, AllNo(X) + maybe, else +~~~~~~~~~ ~~~~~~ ~~~~~~ +/ acc(parent(X)) yes, AllYes(X) [3] + no, AllNo(X) + maybe, else +~~~~~~~~~ ~~~~~~ ~~~~~~ +t, epsilon, acc(parent(X)) [1] yes, acc(parent(X)) +dot, alnum, no, !acc(parent(X)) +alpha +~~~~~~~~~ ~~~~~~ ~~~~~~ +n acc(parent(X)) yes, acc(X) && gen'(def(X),yes) + no, !acc(X) || gen'(def(X),no) + maybe, acc(X) && gen'(def(X),maybe) +~~~~~~~~~ ~~~~~~ ~~~~~~ + +From this specification we can draw the following conclusions about +the properties and their calculation: + +- Acceptance data is defined top-down, from root to the leaves. + +- Generation data is defined bottom-up, from leaves to the root. + +- In the leaves Acceptance data is converted into Generation data. + Nonterminal calls additional hook in the Generation data of the + called symbols. + +- In the definition Generation data can convert into Acceptance data, + and Nonterminal uses hook in the Generation data from the calling + nodes, and may hook in Acceptance data as well. + +The important places are the two sides of boundaries, i.e. the +definition nodes, and the n-Nodes calling on definitions. Only there +the property values may need resolution of conflicts. Anywhere else +the values are derived in simple equations, allowing their computation +in trivial sweeps. + + +Algorithm +~~~~~~~~~ + +1. Initialization. + + acc(X), gen(X) for all DEFs, without consideration for + children and users (use maybe for unknown parts). + +2. Sweep + + For all definitions + + a. Sweep top-down. + acc(X) for all nodes. + + b. Sweep bottom-up + gen(X) for all nodes. + +3. Resolution. + + Recompute acc(X), gen(X) for all DEFs, using the full + equations. Remember which nodes changed. + +4. Repeat from 2 using the remembered set of DEFs, if not + empty. Stop if the set of changed DEFs is empty. + +Algorithm 2 +~~~~~~~~~~~ + +1. Initialization. + + acc(X), gen(X) for all DEFs, without consideration for + children and users (use maybe for unknown parts). + +2. Sweep + + For all definitions + + Sweep top-down. + acc(X), gen(X) for all nodes + + The gen(X) is possible because an initial value is + directly computable from acc(X), without having to + look at the children at all. + + !acc(X) => gen(X,No). + acc(X) => gen(X,Maybe) + + Even better. If !acc(X) we can count the type of + calls for invoked nonterminals, and if that is the + number of users we can immediately change their + acc(X) and sweep down further (similar to reachable). + + We remember the interesting places where things can change. + The leaf nodes, and lookahead operators. + +3. Sweep the 2nd, working up from each interesting place (sorted + by depth, deepest first) up through the ancestors, and when + reaching def-Nodes we can now sweep up further into the users. + + If this changes acc(X) for a definition (only down to discard) + we remember, and after completion go back to 2. + +_____________________________________________________ +[1] Actually the value is not really relevant as there are no childen + to consider. However with the chosen definition the number of + special cases to consider is reduced. I.e. the definition of the + function is more uniform. + +[2] The *- and ?-operators match even if the expression underneath them + does not. In which case there will be no SV. So the best we can + say even if the expression surely does generates an SV is maybe. + +[3] The x- and /-operators can be made more accurate if we have data + about static match results, as this information can cut down the + set of children to actually consider for matching and generation + of values. diff --git a/tcllib/modules/page/notes/doc_grammar.txt b/tcllib/modules/page/notes/doc_grammar.txt new file mode 100644 index 0000000..9c02b3b --- /dev/null +++ b/tcllib/modules/page/notes/doc_grammar.txt @@ -0,0 +1,68 @@ +Raw PE Grammar AS Tree +====================== + +This file documents the tree generated by the frontend for reading a +PE grammar in textual form. + +General information +------------------- + +* The tree is implemented using 'struct::tree'. +* It is an abstract syntax tree semantically. + +Structure +--------- + +* The root node has one child, of type ALL (*), we call this ALL. + +* The structure of the tree under the node ALL reflects the structure + of the PE grammar used by the frontend to read grammar. + + - Void nonterminals are leafs of the tree. + + - Match nonterminals have one child node carrying the matched + terminal string. + + - All other nonterminals have children per the structure of the rule + matched and the nonterminals therein. + +~~ +(*) <=> ((type == nonterminal) && (text == ALL)) + +Attributes +---------- + +The node root is exceptional, it has no attributes. The "set of all +nodes" referenced in the descriptions below does not include it. + +Name Type Details +---- ---- ------- +type enum At all nodes. Values in {terminal, nonterminal}. The + type of the node, telling us if it is a container for + a nonterminal symbol or for a terminal string. +---- ---- ------- +detail string At all nodes. Meaning of its value is dependent on the + value of the attribute 'type'. + + nonterminal : Name of symbol + + terminal : Terminal data. + + This is always the lexeme, as found in + the input. For character data from + strings this means that the text + contains the special forms as well, + i.e. quoted with backslashes. +==== ==== ======= +at int Restricted to nodes of type 'terminal'. Represents the +atcol int location in the input where the terminal data starts +atline int (= the location of the first character), as offset, + and in line/column notation (*) +---- ---- ------- +to int As above, but representing the end location. +tocol int +toline int +---- ---- ------- + +~~ +(*) Lines are counted from 1. Columns are counted from 0. diff --git a/tcllib/modules/page/notes/doc_normalize.txt b/tcllib/modules/page/notes/doc_normalize.txt new file mode 100644 index 0000000..b64ad04 --- /dev/null +++ b/tcllib/modules/page/notes/doc_normalize.txt @@ -0,0 +1,138 @@ +Normalized PE Grammar Tree +========================== + +This file documents the tree generated by the transformational package +'pg::peg::normalize'. The input to the transformation is assumed to be +a 'Raw PE Grammar AS Tree', as generated by the PEG frontend, and +described in 'doc_grammar.txt'. + +General information +------------------- + +* The tree is implemented using 'struct::tree'. + +* The tree is used as a higher data structures keeping the various + parts of a grammar together, which are: Start parsing expression, + definitions, and their parsing expressions. The tree nature of the + parsing expressions map especially nicely to this data structure. + +Structure +--------- + +* The root node represents the overall grammar. It has one child node + for the start expression, and one child per definition of a + nonterminal symbol in the grammar. No other nodes are possible. The + order of the children is not specified and an implementation + detail. Attributes in the root provide quick access, and the nodes + can also be distinguished by the attributes they have and/or their + values. + +* A definition node represents a single nonterminal definition from + the grammar. Most of the information describing the definition is + stored in attributes of the node. Sole exception is the parsing + expression associated with the defined nonterminal symbol. This is + represented by an expression subtree, the root of which is the + single child of the definition node. + +* All other nodes represent a parsing expression with the operator + stored in the node and its arguments represented by the children of + the node. For operators allowing more than one argument the children + will be in the same order as specified in the grammar. I.e. the + first child represents the first argument to the operator, and so + on. + +Attributes +---------- + +Name Type Details +---- ---- ------- +name string Root only. The name of the grammar represented by the + tree. +---- ---- ------- +start noderef Root only. Id of the tree node which is the root of + the start expression. A child of the root node. Does + not intersect with the set of definition nodes. Can be + empty, representing a grammar without start expression. +---- ---- ------- +definitions Root only. Maps the names (strings) of nonterminal + dict symbols to the ids of the tree nodes (noderef) which + represents the definition of that symbol. The nodes + are all immediate children of the root node. None of + them can be the root of the start expression + however. The dictionary can be empty, representing a + grammar which has no nonterminal symbols. +---- ---- ------- +undefined Root only. Maps the name (string) of a nonterminal + dict symbol which has no definition in the grammar to a + list containting the ids of the tree nodes (noderef) + which use the symbol despite that. I.e. if this value + is not empty the grammar is invalid and has 'holes'. +==== ==== ======= +symbol string Root and definition nodes only. The name of the + nonterminal symbol whose definition the node is + representing. For root this is '<StartExpression>'. + It is defined for root so that some algorithms on + expressions can use it as a sentinel. +---- ---- ------- +label string Definition nodes only. The name of the input grammar + level nonterminal symbol represented by the node. This + is normally identical to 'symbol', but can differ for + helper definitions introduced by transformations. For + such 'symbol' will refer to the generated name of the + symbol, and 'label' to the name of the symbol in the + grammar the helper belongs to. +---- ---- ------- +mode enum Definition nodes only. Values in {value, discard, + leaf, match}. Specifies how the defined nonterminal + handles the generation of its semantic value during + matching. +---- ---- ------- +users list Definition nodes only. A list containing the ids of + the tree nodes which reference this definition. These + nodes are always expression nodes, with operator + 'n'. The list can be empty, representing a nonterminal + symbol which is defined, but not used anywhere in + grammar. +==== ==== ======= +op enum All expression nodes. Values in {n, t, .., epsilon, + alpha, alnum, x, /, ?, *, +, !, &}. Specifies the + operator part of the expression represented by the + node. +---- ---- ------- +char char Expression nodes with operator 't' (t-Nodes) + only. Value is the single character from the grammar + to match, as represented by Tcl. I.e. any quoting from + the input has been resolved. +---- ---- ------- +begin char ..-Nodes only. Values are like 'char' above, the first +end char and last character in the range to match. +---- ---- ------- +sym string n-Nodes only. The name of the nonterminal symbol to + match. +---- ---- ------- +def noderef n-Nodes only. The id of the definition node for the + nonterminal symbol to match. Can be empty. In that + case the node repesents a try to match an undefined + nonterminal symbol. The value of 'sym' will be a key + in the dictionary of root->undefined, and the id of + this node an element in the list associated with that + key. +==== ==== ======= +at*, to* See 'doc_grammar.txt' for the general definition. + + All nodes except root. + + Definition nodes: The span of input covered by the + definition. + + Expression nodes: The span of input covered by the + expression. + + The nodes for the operators + + dot, alpha, alnum, epsilon + + have no location information right now. Nodes based + on them may have only partial or no information as + well. +---- ---- ------- diff --git a/tcllib/modules/page/notes/doc_reachable.txt b/tcllib/modules/page/notes/doc_reachable.txt new file mode 100644 index 0000000..f6e3b10 --- /dev/null +++ b/tcllib/modules/page/notes/doc_reachable.txt @@ -0,0 +1,71 @@ +PE Grammar Tree, after Reachables Computation +============================================= + +This file documents the augmentations to a Normalized PE Grammar Tree +as inserted by the transformational package 'pg::peg::reachable'. The +input to the transformation is assumed to be a 'Normalized PE Grammar +Tree' as described in 'doc_normalize.txt', possibly with augmentations +from other transformation, as long as they are not in conflict with +the augmentations specified here. + +General information +------------------- + +* The specification of 'doc_normalize.txt' applies fully. + +Structure +--------- + +* The structure of the tree is unchanged. + +Attributes +---------- + +Name Type Details +---- ---- ------- +pg::peg::reachable + list + Root node only. The presence of this attribute + indicates that the reachable computation has been been + executed on the grammar and that we have valid + results. + + Contains a list of the nodes (definition, and + expression) which are reachable from the root node of + the start expression. +---- ---- ------- +pg::peg::unreachable + list + Root node only. Contains a list of the nodes + (definition, and expression) which are __not__ + reachable from the root node of the start expression. +---- ---- ------- + +Reachability is defined on the definition and expression nodes via + +- The root node of the start expression is reachable. +- An expression node is reachable if its parent node (expression or + definition) is reachable. +- A definition node is reachable, if at least one its using expression + nodes is reachable. +- No other node is reachable. + +This definition leads to a simple recursive (top-down) algorithm for +sweeping a grammar and marking all reachable nodes. We do however +remember only the reachbility of definitions, as that is the only +information truly relevant. + +Transformation +-------------- + +The reachable transformation is based on the reachable computation and +the agumented tree generated by it. The transformation removes all +definitions which are not reachable. This may leave the grammar +without definitions. + +Note that this change may remove invokations of undefined nonterminal +symbols. It however cannot produce new invokations of undefined +nonterminal symbols as the removed definitions have no actual users by +definition. Those which have invoking nodes (as recorded in 'users') +are used by an unreachable definition (This can be an unreachable +circle of definitions). diff --git a/tcllib/modules/page/notes/doc_realizable.txt b/tcllib/modules/page/notes/doc_realizable.txt new file mode 100644 index 0000000..6652d90 --- /dev/null +++ b/tcllib/modules/page/notes/doc_realizable.txt @@ -0,0 +1,101 @@ +PE Grammar Tree, after Realizability Computation +================================================ + +This file documents the augmentations to a Normalized PE Grammar Tree +as inserted by the transformational package +'page::analysis::peg::realizable'. The input to the transformation is +assumed to be a 'Normalized PE Grammar Tree' as described in +'doc_normalize.txt', possibly with augmentations from other +transformations, as long as they are not in conflict with the +augmentations specified here. + +Background +---------- + +The realizability of a nonterminal is usually defined for Context Free +Grammars. For PE grammars we define the property by treating them as +CFG and then following the usual definition, given below: + + A nonterminal symbol N of a CF grammar G is useful, if and + only if a terminal sentence can be derived from it in a finite + number of steps. A terminal sentence is a sentence which is + either empty or contains only terminal symbols. + +This intrinsic specification is equivalent to the following explicit +rules for the computation of realizability of arbitrary parsing +expressions: + +* A char expression (t) is realizable. +* A range expression (..) is realizable. +* A special expression (alnum, alpha) is realizable. +* A dot expression (.) is realizable. +* An epsilon expression (epsilon) is realizable. +* A sequence expression (x) is realizable if and only if all of its + argument expressions are realizable. +* A choice-expression (/) is realizable if and only if at least one + of its argument expressions is realizable [1]. +* A Kleene closure (*) is realizable. +* A positive Kleene closure (+) is realizable, if and only if its + argument expression is realizable. +* An optional expression (?) is realizable. +* A nonterminal expression is realizable if and only if the invoked + nonterminal definition is realizable. +* A nonterminal definition is realizable if and only if its + definining expression is realizable. +* All other expressions are not realizable. + +From the rules above it is clear that the property is defined by the +leaves of the expression trees and then flows upward towards the +roots, and at the roots it jumps over the gap from nonterminal +definition to nonterminal use for further propagation. + +This leads to an iterative algorithm which starts with the initial set +of realizable nodes and then works its way to find all of their parents +which are also realizable. + +[1] It is here where we treat the PEG like a CFG. The ordered choice +is implicitly handled like an unordered choice. + + +General information +------------------- + +* The specification of 'doc_normalize.txt' applies fully. + +Structure +--------- + +* The structure of the tree is unchanged. + +Attributes +---------- + +Name Type Details +---- ---- ------- +pg::peg::realizable Root node only. The presence of this attribute + list indicates that the realizability computation has been + been executed on the grammar and that we have valid + results. + + Contains a list of the nodes which are realizable. +---- ---- ------- +pg::peg::unrealizable + list + Root node only. Contains a list of the nodes which are + __not__ realizable. +---- ---- ------- + +Transformation +-------------- + +The realizability transformation is based on the realizability computation +and the agumented tree generated by it. The transformation removes all +(partial) expressions and definitions are not realizable. This may leave +the grammar without definitions, and without a start expression as +well. + +Note that this change may remove invokations of undefined nonterminal +symbols. It however cannot produce new invokations of undefined +nonterminal symbols as a unrealizable definition implies a +unrealizable invokation, i.e. the invokations of unrealizable +definitions are removed themselves as well. diff --git a/tcllib/modules/page/page_intro.man b/tcllib/modules/page/page_intro.man new file mode 100644 index 0000000..66f211a --- /dev/null +++ b/tcllib/modules/page/page_intro.man @@ -0,0 +1,35 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_intro n 1.0] +[keywords page] +[keywords {parser generator}] +[keywords {text processing}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page introduction}] +[category {Page Parser Generator}] +[description] +[para] + +[term page] (short for [emph {parser generator}]) stands for a set of +related packages which help in the construction of parser generators, +and other utilities doing text processing. + +[para] + +They are mainly geared towards supporting the Tcllib application +[syscmd page], with the package [package page::pluginmgr] in a central +role as the plugin management for the application. The other packages +are performing low-level text processing and utility tasks geared +towards parser generation and mainly accessed by [syscmd page] through +plugins. + +[para] + +The packages implementing the plugins are not documented as regular +packages, as they cannot be loaded into a general interpreter, like +tclsh, without extensive preparation of the interpreter. Preparation +which is done for them by the plugin manager. + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_pluginmgr.man b/tcllib/modules/page/page_pluginmgr.man new file mode 100644 index 0000000..c0e67d7 --- /dev/null +++ b/tcllib/modules/page/page_pluginmgr.man @@ -0,0 +1,800 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_pluginmgr n 1.0] +[keywords page] +[keywords {parser generator}] +[keywords {text processing}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page plugin manager}] +[category {Page Parser Generator}] +[require page::pluginmgr [opt 0.2]] +[require fileutil] +[description] +[para] + +This package provides the plugin manager central to the [syscmd page] +application. It manages the various reader, writer, configuration, and +transformation plugins which actually process the text (read, +transform, and write). + +[para] + +All plugins are loaded into slave interpreters specially prepared for +them. While implemented using packages they need this special +environment and are not usable in a plain interpreter, like +tclsh. Because of that they are only described in general terms in +section [sectref {PREDEFINED PLUGINS}], and not documented as regular +packages. It is expected that they follow the APIs specified in the +sections + +[list_begin enum] +[enum] [sectref {CONFIG PLUGIN API}] +[enum] [sectref {READER PLUGIN API}] +[enum] [sectref {WRITER PLUGIN API}] +[enum] [sectref {TRANSFORM PLUGIN API}] +[list_end] + +as per their type. + +[section API] + +[list_begin definitions] + +[call [cmd ::page::pluginmgr::reportvia] [arg cmd]] + +This command defines the callback command used by + +[cmd ::page::pluginmgr::report] (see below) to report input errors and +warnings. The default is to write such reports to the standard error +channel. + +[call [cmd ::page::pluginmgr::report] [arg level] [arg text] [opt "[arg from] [opt [arg to]]"]] + +This command is used to report input errors and warnings. By default +such reports are written to the standard error. This can be changed by +setting a user-specific callback command with +[cmd ::page::pluginmgr::reportvia] (see above). + +[para] + +The arguments [arg level] and [arg text] specify both the importance +of the message, and the message itself. For the former see the package +[package logger] for the allowed values. + +[para] + +The optional argument [arg from] and [arg to] can be used by the +caller to indicate the location (or range) in the input where the +reported problem occured. Each is a list containing two elements, the +line and the column in the input, in this order. + +[call [cmd ::page::pluginmgr::log] [arg cmd]] + +This command defines a log callback command to be used by loaded +plugins for the reporting of internal errors, warnings, and general +information. Specifying the empty string as callback disables logging. + +[para] + +Note: The [arg cmd] has to be created by the [package logger] package, +or follow the same API as such. + +[para] + +The command returns the empty string as its result. + +[call [cmd ::page::pluginmgr::configuration] [arg name]] + +This command loads the named configuration plugin, retrieves the +options encoded in it, and then immediately unloads it again. + +[para] + +If the [arg name] is the path to a file, then this files will be tried +to be loaded as a plugin first, and, if that fails, opened and its +contents read as a list of options and their arguments, separated by +spaces, tabs and newlines, possibly quotes with single and double +quotes. + +[para] + +See section [sectref {CONFIG PLUGIN API}] for the API expected of +configuration plugins. + +[para] + +The result of the command is the list of options retrieved. + +[call [cmd ::page::pluginmgr::reader] [arg name]] + +This command loads the named reader plugin and initializes it. The +result of the command is a list of options the plugin understands. + +[para] + +Only a single reader plugin can be loaded. Loading another reader +plugin causes the previously loaded reader plugin to be de-initialized +and unloaded. + +[para] + +See section [sectref {READER PLUGIN API}] for the API expected of +reader plugins. + +[call [cmd ::page::pluginmgr::rconfigure] [arg dict]] + +This commands configures the loaded reader plugin. The options and +their values are provided as a Tcl dictionary. The result of the +command is the empty string. + +[call [cmd ::page::pluginmgr::rtimeable]] + +This commands checks if the loaded reader plugin is able to collect +timing statistics. The result of the command is a boolean flag. The +result is [const true] if the plugin can be timed, and [const false] +otherwise. + +[call [cmd ::page::pluginmgr::rtime]] + +This command activates the collection of timing statistics in the +loaded reader plugin. + +[call [cmd ::page::pluginmgr::rgettime]] + +This command retrieves the collected timing statistics of the loaded +reader plugin after it was executed. + +[call [cmd ::page::pluginmgr::rhelp]] + +This command retrieves the help string of the loaded reader +plugin. This is expected to be in [term doctools] format. + +[call [cmd ::page::pluginmgr::rlabel]] + +This command retrieves the human-readable name of the loaded reader +plugin. + +[call [cmd ::page::pluginmgr::read] [arg read] [arg eof] [opt [arg complete]]] + +This command invokes the loaded reader plugin to process the input, +and returns the results of the plugin as its own result. The input is +accessible through the callback commands [arg read], and [arg eof]. The +optional [arg done] can be used to intrecept when the plugin has +completed its processing. All arguments are command prefixes. + +[para] + +The plugin will invoke the various callbacks in the following +situations: + +[list_begin definitions] +[call [arg read] [arg num]] +is invoked whenever input to process is needed, with the number of +characters/bytes it asks for. The result is expected to be the input +the plugin is in need of. + +[call [arg eof]] +is invoked by the plugin to check if the input has reached the of the +stream. The result is expected to be a boolean flag, [const true] when +the input has hit EOF, and [const false] otherwise. + +[call [arg done]] +is invoked when the plugin has completed the processing of the input. + +[list_end] + +[call [cmd ::page::pluginmgr::writer] [arg name]] + +This command loads the named writer plugin and initializes it. The +result of the command is a list of options the plugin understands. + +[para] + +Only a single reader plugin can be loaded. Loading another reader +plugin causes the previously loaded reader plugin to be de-initialized +and unloaded. + +[para] + +See section [sectref {WRITER PLUGIN API}] for the API expected of +writer plugins. + +[call [cmd ::page::pluginmgr::wconfigure] [arg dict]] + +This commands configures the loaded writer plugin. The options and +their values are provided as a Tcl dictionary. The result of the +command is the empty string. + +[call [cmd ::page::pluginmgr::wtimeable]] + +This commands checks if the loaded writer plugin is able to measure +execution times. The result of the command is a boolean flag. The +result is [const true] if the plugin can be timed, and [const false] +otherwise. + +[call [cmd ::page::pluginmgr::wtime]] + +This command activates the collection of timing statistics in the +loaded writer plugin. + +[call [cmd ::page::pluginmgr::wgettime]] + +This command retrieves the collected timing statistics of the loaded +writer plugin after it was executed. + +[call [cmd ::page::pluginmgr::whelp]] + +This command retrieves the help string of the loaded writer +plugin. This is expected to be in [term doctools] format. + +[call [cmd ::page::pluginmgr::wlabel]] + +This command retrieves the human-readable name of the loaded writer +plugin. + +[call [cmd ::page::pluginmgr::write] [arg chan] [arg data]] + +The loaded writer plugin is invoked to generate the output. It is +given the [arg data] to generate the outpout from, and the Tcl handle +[arg chan] of the channel to write the generated output to. The +command returns th empty string as its result. + +[call [cmd ::page::pluginmgr::transform] [arg name]] + +This command loads the named transformation plugin and initializes +it. The result of the command is a 2-element list containing the +plugin id and a list of options the plugin understands, in this order. + +[para] + +Multiple transformations plugins can be loaded and are identified by +handles. + +[para] + +See section [sectref {TRANSFORM PLUGIN API}] for the API expected of +transformation plugins. + +[call [cmd ::page::pluginmgr::tconfigure] [arg id] [arg dict]] + +This commands configures the identified transformation plugin. The +options and their values are provided as a Tcl dictionary. The result +of the command is the empty string. + +[call [cmd ::page::pluginmgr::ttimeable] [arg id]] + +This commands checks if the identified transformation plugin is able +to collect timing statistics. The result of the command is a boolean +flag. The result is [const true] if the plugin can be timed, and +[const false] otherwise. + +[call [cmd ::page::pluginmgr::ttime] [arg id]] + +This command activates the collection of timing statistics in the +identified transformation plugin. + +[call [cmd ::page::pluginmgr::tgettime] [arg id]] + +This command retrieves the collected timing statistics of the +identified transformation plugin after it was executed. + +[call [cmd ::page::pluginmgr::thelp] [arg id]] + +This command retrieves the help string of the identified +transformation plugin. This is expected to be in [term doctools] +format. + +[call [cmd ::page::pluginmgr::tlabel] [arg id]] + +This command retrieves the human-readable name of the identified +transformation plugin. + +[call [cmd ::page::pluginmgr::transform_do] [arg id] [arg data]] + +The identified transformation plugin is invoked to process the +specified [arg data]. The result of the plugin is returned as the +result of the command. + +[list_end] + +[section {CONFIG PLUGIN API}] + +Configuration plugins are expected to provide a single command, +described below. + +[para] + +[list_begin definitions] +[call [cmd page_cdefinition]] + +This command of a configuration plugin is called by the plugin manager +to execute it. Its result has to be a list of options and values to +process. + +[list_end] +[para] + +Configuration plugins do not expect the environment to provide any +special commands. + +[para] + +It is expected that a configuration plugin [const FOO] is implemented +by the package [package page::config::[const FOO]]. + +[para] + +Configuration plugins are loaded, executed, and unloaded in one step, +they are not kept in memory. The command for doing this is +[cmd ::page::pluginmgr::configuration]. + +[section {READER PLUGIN API}] + +Reader plugins are expected to provide the following commands, +described below. + +[para] + +[list_begin definitions] + +[call [cmd page_rfeature] [arg name]] + +This command takes a feature [arg name] and returns a boolean flag +indicating whether the feature is supported by the plugin, or not. + +The result has to be [const true] if the feature is supported, and +[const false] otherwise. + +[para] + +See section [sectref FEATURES] for the possible features the plugin +manager will ask for. + +[call [cmd page_rtime]] + +This command is invoked to activate the collection of timing +statistics. + +[call [cmd page_rgettime]] + +This command is invoked to retrieve the collected timing statistics. + +[call [cmd page_rlabel]] + +This command is invoked to retrieve a human-readable label for the +plugin. + +[call [cmd page_rhelp]] + +This command is invoked to retrieve a help text for plugin. The text +is expected to be in [term doctools] format. + +[call [cmd page_roptions]] + +This command is invoked to retrieve the options understood by the +plugin. + +[call [cmd page_rconfigure] [arg option] [arg value]] + +This command is invoked to reconfigure the plugin, specifically the +given [arg option] is set to the new [arg value]. + +[call [cmd page_rrun]] + +This command is invoked to process the input stream per the current +plugin configuration. The result of the command is the result of the +processing. + +[list_end] +[para] + +Reader plugins expect the environment to provide the following special +commands. + +[list_begin definitions] + +[call [cmd page_read] [arg num]] + +This command is invoked to read [arg num] characters/bytes from the +input. Its result has to be read characters/bytes. + +[call [cmd page_read_done]] + +This command is invoked to signal that the plugin has completed the +processing of the input. + +[call [cmd page_eof]] + +This command is invoked to check if the input stream has reached its +end. Its result has to be a boolean flag, [const true] when the input +has reached the end, [const false] otherwise. + +[call [cmd page_info] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report some information to the user. May indicate a +location or range in the input. Each piece of location data, if +provided, is a 2-element list containing line and column numbers. + +[call [cmd page_warning] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report a warning to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_error] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report an error to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_log_info] [arg text]] + +Invoked to report some internal information. + +[call [cmd page_log_warning] [arg text]] + +Invoked to report an internal warning. + +[call [cmd page_log_error] [arg text]] + +Invoked to report an internal error. + +[list_end] +[para] + +It is expected that a reader plugin [const FOO] is implemented +by the package [package page::reader::[const FOO]]. + +[para] + +Reader plugins are loaded by the command +[cmd ::page::pluginmgr::reader]. At most one reader plugin can be kept +in memory. + +[section {WRITER PLUGIN API}] + +Writer plugins are expected to provide the following commands, +described below. + +[para] + +[list_begin definitions] + +[call [cmd page_wfeature]] + +This command takes a feature [arg name] and returns a boolean flag +indicating whether the feature is supported by the plugin, or not. + +The result has to be [const true] if the feature is supported, and +[const false] otherwise. + +[para] + +See section [sectref FEATURES] for the possible features the plugin +manager will ask for. + +[call [cmd page_wtime]] + +This command is invoked to activate the collection of timing +statistics. + +[call [cmd page_wgettime]] + +This command is invoked to retrieve the collected timing statistics. + +[call [cmd page_wlabel]] + +This command is invoked to retrieve a human-readable label for the +plugin. + +[call [cmd page_whelp]] + +This command is invoked to retrieve a help text for plugin. The text +is expected to be in [term doctools] format. + +[call [cmd page_woptions]] + +This command is invoked to retrieve the options understood by the +plugin. + +[call [cmd page_wconfigure] [arg option] [arg value]] + +This command is invoked to reconfigure the plugin, specifically the +given [arg option] is set to the new [arg value]. + +[call [cmd page_wrun] [arg chan] [arg data]] + +This command is invoked to process the specified [arg data] and write +it to the output stream [arg chan]. The latter is a Tcl channel handle +opened for writing. The result of the command is the empty string. + +[list_end] +[para] + +Writer plugins expect the environment to provide the following special +commands. + +[list_begin definitions] + +[call [cmd page_info] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report some information to the user. May indicate a +location or range in the input. Each piece of location data, if +provided, is a 2-element list containing line and column numbers. + +[call [cmd page_warning] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report a warning to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_error] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report an error to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_log_info] [arg text]] + +Invoked to report some internal information. + +[call [cmd page_log_warning] [arg text]] + +Invoked to report an internal warning. + +[call [cmd page_log_error] [arg text]] + +Invoked to report an internal error. + +[list_end] +[para] + +It is expected that a writer plugin [const FOO] is implemented +by the package [package page::writer::[const FOO]]. + +[para] + +Writer plugins are loaded by the command +[cmd ::page::pluginmgr::writer]. At most one writer plugin can be kept +in memory. + +[section {TRANSFORM PLUGIN API}] page::transform::* + +Transformation plugins are expected to provide the following commands, +described below. + +[para] + +[list_begin definitions] + +[call [cmd page_tfeature]] + +This command takes a feature [arg name] and returns a boolean flag +indicating whether the feature is supported by the plugin, or not. + +The result has to be [const true] if the feature is supported, and +[const false] otherwise. + +[para] + +See section [sectref FEATURES] for the possible features the plugin +manager will ask for. + +[call [cmd page_ttime]] + +This command is invoked to activate the collection of timing +statistics. + +[call [cmd page_tgettime]] + +This command is invoked to retrieve the collected timing statistics. + +[call [cmd page_tlabel]] + +This command is invoked to retrieve a human-readable label for the +plugin. + +[call [cmd page_thelp]] + +This command is invoked to retrieve a help text for plugin. The text +is expected to be in [term doctools] format. + +[call [cmd page_toptions]] + +This command is invoked to retrieve the options understood by the +plugin. + +[call [cmd page_tconfigure] [arg option] [arg value]] + +This command is invoked to reconfigure the plugin, specifically the +given [arg option] is set to the new [arg value]. + +[call [cmd page_trun] [arg chan] [arg data]] + +This command is invoked to process the specified [arg data] and write +it to the output stream [arg chan]. The latter is a Tcl channel handle +opened for writing. The result of the command is the empty string. + +[list_end] +[para] + +Transformation plugins expect the environment to provide the following +special commands. + +[list_begin definitions] + +[call [cmd page_info] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report some information to the user. May indicate a +location or range in the input. Each piece of location data, if +provided, is a 2-element list containing line and column numbers. + +[call [cmd page_warning] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report a warning to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_error] [arg text] [opt "[arg from] [opt [arg to]]"]] + +Invoked to report an error to the user. May indicate a location or +range in the input. Each piece of location data, if provided, is a +2-element list containing line and column numbers. + +[call [cmd page_log_info] [arg text]] + +Invoked to report some internal information. + +[call [cmd page_log_warning] [arg text]] + +Invoked to report an internal warning. + +[call [cmd page_log_error] [arg text]] + +Invoked to report an internal error. + +[list_end] +[para] + +It is expected that a transformation plugin [const FOO] is implemented +by the package [package page::transform::[const FOO]]. + +[para] + +Transformation plugins are loaded by the command + +[cmd ::page::pluginmgr::transform]. More than one transformation +plugin can be kept in memory. + +[section {PREDEFINED PLUGINS}] + +The following predefined plugins are known, i.e. provided by the page +module. + +[list_begin definitions] + +[def Configuration] +[list_begin definitions] +[def peg] + +Returns a set of options to configure the [syscmd page] application +for the processing of a PEG grammar and the generation of ME code. See +the packages [package grammar_peg], [package grammar_me] and relations +for more details. + +[list_end] + +[def Reader] +[list_begin definitions] +[def hb] +Expects a so-called [term {half-baked PEG container}] as input and +returns the equivalent abstract syntax tree. See the writer plugin +[term hb] for the plugin generating this type of input. + +[def lemon] +Expects a grammar specification as understood by Richar Hipp's LEMON +parser generator and returns an abstract syntax tree for it. + +[def peg] +Expects a grammar specification in the form of a parsing expression +grammar (PEG) and returns an abstract syntax tree for it. + +[def ser] +Expect the serialized form of a parsing expression grammar as +generated by the package [package grammar::peg] as input, converts it +into an equivalent abstract syntax tree and returns that. + +[def treeser] +Expects the serialized form of a tree as generated by the package +[package struct::tree] as input and returns it, after validation. + +[list_end] + +[def Writer] +[list_begin definitions] +[def hb] +Expects an abstract syntax tree for a parsing expression grammar as +input and writes it out in the form of a so-called +[term {half-baked PEG container}]. + +[def identity] +Takes any input and writes it as is. + +[def mecpu] +Expects symbolic assembler code for the MatchEngine CPU (See the +package [package grammar::me::cpu] and relatives) and writes it out as +Tcl code for a parser. + +[def me] +Expects an abstract syntax tree for a parsing expression grammar as +input and writes it out as Tcl code for the MatchEngine (See the +package [package grammar::me] and relatives) which parses input in +that grammar. + +[def null] +Takes any input and writes nothing. The logical equivalent of +/dev/null. + +[def peg] +Expects an abstract syntax tree for a parsing expression grammar as +input and writes it out in the form of a canonical PEG which can be +read by the reader plugin [term peg]. + +[def ser] +Expects an abstract syntax tree for a parsing expression grammar as +input and writes it out as a serialized PEG container which can be +read by the reader plugin [term ser]. + +[def tpc] +Expects an abstract syntax tree for a parsing expression grammar as +input and writes it out as Tcl code initializing a PEG container as +provided by the package [package grammar::peg]. + +[def tree] +Takes any serialized tree (per package [package struct::tree]) as +input and writes it out in a generic indented format. + +[list_end] + +[def Transformation] +[list_begin definitions] + +[def mecpu] +Takes an abstract syntax tree for a parsing expression grammer as +input, generates symbolic assembler code for the MatchEngine CPU, and +returns that as its result (See the package [package grammar::me::cpu] +and relatives). + +[def reachable] +Takes an abstract syntax tree for a parsing expression grammer as +input, performs a reachability analysis, and returns the modified and +annotated tree. + +[def realizable] +Takes an abstract syntax tree for a parsing expression grammer as +input, performs an analysis of realizability, and returns the modified +and annotated tree. + +[list_end] +[list_end] + +[comment { + Make it clear that all data between plugins is shuffled around + in serialized form, as objects cannot be transfered/accessed + across the interpreter boundaries (safety concerns). + + Describe the commands expected by plugins to be available in + the environment. + + Describe the predefined features. +}] + +[section FEATURES] + +The plugin manager currently checks the plugins for only one feature, +[const timeable]. A plugin supporting this feature is assumed to be +able to collect timing statistics on request. + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_util_flow.man b/tcllib/modules/page/page_util_flow.man new file mode 100644 index 0000000..d6535cb --- /dev/null +++ b/tcllib/modules/page/page_util_flow.man @@ -0,0 +1,96 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_util_flow n 1.0] +[keywords dataflow] +[keywords {graph walking}] +[keywords page] +[keywords {parser generator}] +[keywords {text processing}] +[keywords {tree walking}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page dataflow/treewalker utility}] +[category {Page Parser Generator}] +[require page::util::flow [opt 0.1]] +[require snit] +[description] +[para] + +This package provides a single utility command for easy dataflow based +manipulation of arbitrary data structures, especially abstract syntax +trees. + +[section API] + +[list_begin definitions] +[call [cmd ::page::util::flow] [arg start] [arg flowvar] [arg nodevar] [arg script]] + +This command contains the core logic to drive the walking of an +arbitrary data structure which can partitioned into separate +parts. Examples of such structures are trees and graphs. + +[para] + +The command makes no assumptions at all about the API of the structure +to be walked, except that that its parts, here called [term nodes], +are identified by strings. These strings are taken as is, from the +arguments, and the body, and handed back to the body, without +modification. + +[para] + +Access to the actual data structure, and all decisions regarding which +nodes to visit in what order are delegated to the body of the loop, +i.e. the [arg script]. + +[para] + +The body is invoked first for the nodes in the start-set specified via +[arg start]), and from then on for the nodes the body has requested to +be visited. The command stops when the set of nodes to visit becomes +empty. Note that a node can be visited more than once. The body has +complete control about this. + +[para] + +The body is invoked in the context of the caller. The variable named +by [arg nodevar] will be set to the current node, and the variable +named by [arg flowvar] will be set to the command of the flow object +through which the body can request the nodes to visit next. The API +provided by this object is described in the next section, +[sectref {FLOW API}]. + +[para] + +Note that the command makes no promises regarding the order in which +nodes are visited, excpt that the nodes requested to be visited by the +current iteration will be visited afterward, in some order. + +[list_end] + +[section {FLOW API}] + +This section describes the API provided by the flow object made +accessible to the body script of [cmd ::page::util::flow]. + +[list_begin definitions] + +[call [arg flow] [method visit] [arg node]] + +Invoking this method requests that the node [arg n] is visited after +the current iteration. + +[call [arg flow] [method visitl] [arg nodelist]] + +Invoking this method requests that all the nodes found in the list +[arg nodelist] are visited after the current iteration. + +[call [arg flow] [method visita] [arg node]...] + +This is the variadic arguments form of the method [method visitl], see +above. + +[list_end] + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_util_norm_lemon.man b/tcllib/modules/page/page_util_norm_lemon.man new file mode 100644 index 0000000..5859575 --- /dev/null +++ b/tcllib/modules/page/page_util_norm_lemon.man @@ -0,0 +1,51 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_util_norm_lemon n 1.0] +[keywords {graph walking}] +[keywords lemon] +[keywords normalization] +[keywords page] +[keywords {parser generator}] +[keywords {text processing}] +[keywords {tree walking}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page AST normalization, LEMON}] +[category {Page Parser Generator}] +[require page::util::norm_lemon [opt 0.1]] +[require snit] +[description] +[para] + +This package provides a single utility command which takes an AST for a +lemon grammar and normalizes it in various ways. The result +is called a [term {Normalized Lemon Grammar Tree}]. + +[para] + +[emph Note] that this package can only be used from within a plugin +managed by the package [package page::pluginmgr]. + +[comment { + TODO: Document the structure of a LEMON AST, + and then of a Normalized LEMON Tree. Which + is not a true AST any longer. +}] + +[section API] + +[list_begin definitions] +[call [cmd ::page::util::norm::lemon] [arg tree]] + +This command assumes the [arg tree] object contains for a lemon +grammar. It normalizes this tree in place. The result is called a +[term {Normalized Lemon Grammar Tree}]. + +[para] + +The exact operations performed are left undocumented for the moment. + +[list_end] + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_util_norm_peg.man b/tcllib/modules/page/page_util_norm_peg.man new file mode 100644 index 0000000..d5e98c3 --- /dev/null +++ b/tcllib/modules/page/page_util_norm_peg.man @@ -0,0 +1,105 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_util_norm_peg n 1.0] +[keywords {graph walking}] +[keywords normalization] +[keywords page] +[keywords {parser generator}] +[keywords PEG] +[keywords {text processing}] +[keywords {tree walking}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page AST normalization, PEG}] +[category {Page Parser Generator}] +[require page::util::norm_peg [opt 0.1]] +[require snit] +[description] +[para] + +This package provides a single utility command which takes an AST for a +parsing expression grammar and normalizes it in various ways. The result +is called a [term {Normalized PE Grammar Tree}]. + +[para] + +[emph Note] that this package can only be used from within a plugin +managed by the package [package page::pluginmgr]. + +[comment { + TODO: Document the structure of a PEG AST, + and then of a Normalized PEG Tree. Which + is not a true AST any longer. +}] + +[section API] + +[list_begin definitions] +[call [cmd ::page::util::norm::peg] [arg tree]] + +This command assumes the [arg tree] object contains for a +parsing expression grammar. It normalizes this tree in place. +The result is called a [term {Normalized PE Grammar Tree}]. + +[para] + +The following operations are performd + +[list_begin enum] +[enum] +The data for all terminals is stored in their grandparental +nodes. The terminal nodes and their parents are removed. Type +information is dropped. + +[enum] +All nodes which have exactly one child are irrelevant and are +removed, with the exception of the root node. The immediate +child of the root is irrelevant as well, and removed as well. + +[enum] +The name of the grammar is moved from the tree node it is stored +in to an attribute of the root node, and the tree node removed. +[para] +The node keeping the start expression separate is removed as +irrelevant and the root node of the start expression tagged with +a marker attribute, and its handle saved in an attribute of the +root node for quick access. + +[enum] +Nonterminal hint information is moved from nodes into attributes, +and the now irrelevant nodes are deleted. +[para] +[emph Note:] This transformation is dependent on the removal of all +nodes with exactly one child, as it removes the all 'Attribute' +nodes already. Otherwise this transformation would have to put +the information into the grandparental node. +[para] +The default mode given to the nonterminals is [const value]. +[para] +Like with the global metadata definition specific information +is moved out out of nodes into attributes, the now irrelevant +nodes are deleted, and the root nodes of all definitions are +tagged with marker attributes. This provides us with a mapping +from nonterminal names to their defining nodes as well, which +is saved in an attribute of the root node for quick reference. +[para] +At last the range in the input covered by a definition is +computed. The left extent comes from the terminal for the +nonterminal symbol it defines. The right extent comes from +the rightmost child under the definition. While this not an +expression tree yet the location data is sound already. + +[enum] +The remaining nodes under all definitions are transformed +into proper expression trees. First character ranges, followed +by unary operations, characters, and nonterminals. At last the +tree is flattened by the removal of superfluous inner nodes. +[para] +The order matters, to shed as much nodes as possible early, and +to avoid unnecessary work later. + +[list_end] +[list_end] + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_util_peg.man b/tcllib/modules/page/page_util_peg.man new file mode 100644 index 0000000..5e75be5 --- /dev/null +++ b/tcllib/modules/page/page_util_peg.man @@ -0,0 +1,108 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_util_peg n 1.0] +[keywords page] +[keywords {parser generator}] +[keywords {parsing expression grammar}] +[keywords PEG] +[keywords {text processing}] +[keywords transformation] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page PEG transformation utilities}] +[category {Page Parser Generator}] +[require page::util::peg [opt 0.1]] +[require snit] +[description] +[para] + +This package provides a few common operations to PEG transformations. +They assume a [term {Normalized PE Grammar Tree}] as input, see the +package [package page::util::norm::peg], possibly augmented with +attributes coming from transformations not in conflict with the base +definition. + +[para] + +[section API] + +[list_begin definitions] +[call [cmd ::page::util::peg::symbolNodeOf] [arg tree] [arg node]] + +Given an arbitrary expression [arg node] in the AST [arg tree] it +determines the node (itself or an ancestor) containing the name of the +nonterminal symbol the node belongs to, and returns its id. The result +is either the root of the tree (for the start expression), or a +definition node. + +[call [cmd ::page::util::peg::symbolOf] [arg tree] [arg node]] + +As [cmd ::page::util::peg::symbolNodeOf], but returns the symbol name +instead of the node. + +[call [cmd ::page::util::peg::updateUndefinedDueRemoval] [arg tree]] + +The removal of nodes in the AST [arg tree] can cause symbols to lose +one or more users. + +[example { + A used by B and C, + B is reachable, + C is not, + + so A now loses the node in the expression for C calling it, + or rather, not calling it anymore. +}] + +This command updates the cross-references and which nonterminals are +now undefined. + +[call [cmd ::page::util::peg::flatten] [arg treequery] [arg tree]] + +This commands flattens nested sequence and choice operators in the AST +[arg tree], re-using the [package treeql] object [arg treequery] to +run the query determining which nodes to cut. + +[call [cmd ::page::util::peg::getWarnings] [arg tree]] + +This command looks at the attributes of the AST [arg tree] for +problems with the grammar and issues warnings. They do not prevent us +from writing the grammar, but still represent problems with it the +user should be made aware of. + +[para] + +The result of the command is a dictionary mapping nonterminal names to +their associated warnings. + +[call [cmd ::page::util::peg::printWarnings] [arg msg]] + +The argument of the command is a dictionary mapping nonterminal names +to their associated warnings, as generated by, for example, the +command [cmd ::page::util::peg::getWarnings]. + +[para] + +The warnings contained therein are formatted and then printed via the +log command [cmd page_info]. This means that this command can be used +only from within a plugin managed by the package +[package page::pluginmgr]. + +[call [cmd ::page::util::peg::peOf] [arg tree] [arg eroot]] + +This command converts the parsing expression starting at the node +[arg eroot] in the AST [arg tree] into a nested list. The exact syntax +of this list specified by the package [package grammar::peg]. + +[call [cmd ::page::util::peg::printTclExpr] [arg pe]] + +This command converts the parsing expression contained in the nested +list [arg pe] into a Tcl string which can be placed into a Tcl script. + +See the package [package grammar::peg] for the exact syntax of +[arg pe]. + +[list_end] + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/page_util_quote.man b/tcllib/modules/page/page_util_quote.man new file mode 100644 index 0000000..c1a32f3 --- /dev/null +++ b/tcllib/modules/page/page_util_quote.man @@ -0,0 +1,62 @@ +[comment {-*- tcl -*- doctools manpage}] +[manpage_begin page_util_quote n 1.0] +[keywords page] +[keywords {parser generator}] +[keywords quoting] +[keywords {text processing}] +[copyright {2007 Andreas Kupries <andreas_kupries@users.sourceforge.net>}] +[moddesc {Parser generator tools}] +[titledesc {page character quoting utilities}] +[category {Page Parser Generator}] +[require page::util::quote [opt 0.1]] +[require snit] +[description] +[para] + +This package provides a few utility commands to convert characters +into various forms. + +[section API] + +[list_begin definitions] +[call [cmd ::page::util::quote::unquote] [arg char]] + +A character, as stored in an abstract syntax tree by a PEG processor +(See the packages [package grammar::peg::interpreter], +[package grammar::me], and their relations), i.e. in some quoted form, +is converted into the equivalent Tcl character. The character is returned +as the result of the command. + +[call [cmd ::page::util::quote::quote'tcl] [arg char]] + +This command takes a Tcl character (internal representation) and +converts it into a string which is accepted by the Tcl parser, will +regenerate the character in question and is 7bit ASCII. The string is +returned as the result of this command. + +[call [cmd ::page::util::quote::quote'tclstr] [arg char]] + +This command takes a Tcl character (internal representation) and +converts it into a string which is accepted by the Tcl parser and will +generate a human readable representation of the character in question. +The string is returned as the result of this command. + +[para] + +The string does not use any unprintable characters. It may use +backslash-quoting. High UTF characters are quoted to avoid problems +with the still prevalent ascii terminals. It is assumed that the +string will be used in a double-quoted environment. + +[call [cmd ::page::util::quote::quote'tclcom] [arg char]] + +This command takes a Tcl character (internal representation) and +converts it into a string which is accepted by the Tcl parser when +used within a Tcl comment. The string is returned as the result of +this command. + +[list_end] + +[vset CATEGORY page] +[include ../doctools2base/include/feedback.inc] +[manpage_end] diff --git a/tcllib/modules/page/parse_lemon.tcl b/tcllib/modules/page/parse_lemon.tcl new file mode 100644 index 0000000..e3b2447 --- /dev/null +++ b/tcllib/modules/page/parse_lemon.tcl @@ -0,0 +1,7420 @@ +# -*- tcl -*- +## Parsing Expression Grammar 'page::parse::lemon'. +## RD parser by the PG backend 'MEwriter'. + +# ### ### ### ######### ######### ######### +## Package description + +# The commands provided here match an input provided through a buffer +# command to the PE grammar 'page::parse::lemon'. The parser is based on the package +# 'grammar::mengine' (recursive-descent, packrat, pulling chars, +# pushing the generated AST). + +# ### ### ### ######### ######### ######### +## Requisites + +package require grammar::me::tcl + +# ### ### ### ######### ######### ######### +## Implementation + +namespace eval ::page::parse::lemon { + # Import the virtual machine for matching. + + namespace import ::grammar::me::tcl::* + upvar #0 ::grammar::me::tcl::ok ok +} + +# ### ### ### ######### ######### ######### +## API Implementation. + +proc ::page::parse::lemon::parse {nxcmd emvar astvar} { + variable ok + variable se + + upvar 1 $emvar emsg $astvar ast + + init $nxcmd + + matchSymbol_LemonGrammar ; # (n LemonGrammar) + + isv_nonterminal_reduce ALL -1 + set ast [sv] + if {!$ok} { + foreach {l m} [ier_get] break + lappend l [lc $l] + set mx {} + foreach x $m {lappend mx "Expected $x"} + set emsg [list $l $mx] + } + + return $ok +} + +# ### ### ### ######### ######### ######### +## Internal helper methods + +# Grammar 'page::parse::lemon' +# +# ASSIGN = (x (t :) +# (t :) +# (t =) +# (n SPACE)) +# +# C_COMMENT = (x (n CCOM_OPEN) +# (* (x (! (n CCOM_CLOSE)) +# (dot))) +# (n CCOM_CLOSE)) +# +# CCOM_CLOSE = (x (t *) +# (t /)) +# +# CCOM_OPEN = (x (t /) +# (t *)) +# +# Code = (x (n DCODE) +# (n Codeblock)) +# +# Codeblock = (x (n LBRACE) +# (* (/ (n Codeblock) +# (n C_COMMENT) +# (n Cplusplus_COMMENT) +# (x (! (n RBRACE)) +# (dot)))) +# (n RBRACE)) +# +# Cplusplus_COMMENT = (x (t /) +# (t /) +# (* (x (! (n EOL)) +# (dot))) +# (n EOL)) +# +# DCODE = (x (t c) +# (t o) +# (t d) +# (t e) +# (n SPACE)) +# +# DDEFDEST = (x (t d) +# (t e) +# (t f) +# (t a) +# (t u) +# (t l) +# (t t) +# (t _) +# (t d) +# (t e) +# (t s) +# (t t) +# (t r) +# (t u) +# (t c) +# (t t) +# (t o) +# (t r) +# (n SPACE)) +# +# DDEFTYPE = (x (t d) +# (t e) +# (t f) +# (t a) +# (t u) +# (t l) +# (t t) +# (t _) +# (t t) +# (t y) +# (t p) +# (t e) +# (n SPACE)) +# +# DDEST = (x (t d) +# (t e) +# (t s) +# (t t) +# (t r) +# (t u) +# (t c) +# (t t) +# (t o) +# (t r) +# (n SPACE)) +# +# DefaultDestructor = (x (n DDEFDEST) +# (n Identifier) +# (n Codeblock)) +# +# DefaultType = (x (n DDEFTYPE) +# (n Codeblock)) +# +# Definition = (* (x (n Identifier) +# (? (n Label)))) +# +# DENDIF = (x (t %) +# (t e) +# (t n) +# (t d) +# (t i) +# (t f) +# (n SPACE)) +# +# Destructor = (x (n DDEST) +# (n Identifier) +# (n Codeblock)) +# +# DEXTRA = (x (t e) +# (t x) +# (t t) +# (t r) +# (t a) +# (t _) +# (t a) +# (t r) +# (t g) +# (t u) +# (t m) +# (t e) +# (t n) +# (t t) +# (n SPACE)) +# +# DFALLBK = (x (t f) +# (t a) +# (t l) +# (t l) +# (t b) +# (t a) +# (t c) +# (t k) +# (n SPACE)) +# +# DIFDEF = (x (t %) +# (t i) +# (t f) +# (t d) +# (t e) +# (t f) +# (n SPACE)) +# +# DIFNDEF = (x (t %) +# (t i) +# (t f) +# (t n) +# (t d) +# (t e) +# (t f) +# (n SPACE)) +# +# DINCL = (x (t i) +# (t n) +# (t c) +# (t l) +# (t u) +# (t d) +# (t e) +# (n SPACE)) +# +# DINTRO = (t %) +# +# Directive = (x (n DINTRO) +# (/ (n Code) +# (n DefaultDestructor) +# (n DefaultType) +# (n Destructor) +# (n ExtraArgument) +# (n Include) +# (n Left) +# (n Name) +# (n Nonassoc) +# (n ParseAccept) +# (n ParseFailure) +# (n Right) +# (n StackOverflow) +# (n Stacksize) +# (n StartSymbol) +# (n SyntaxError) +# (n TokenDestructor) +# (n TokenPrefix) +# (n TokenType) +# (n Type) +# (n Fallback))) +# +# DLEFT = (x (t l) +# (t e) +# (t f) +# (t t) +# (n SPACE)) +# +# DNAME = (x (t n) +# (t a) +# (t m) +# (t e) +# (n SPACE)) +# +# DNON = (x (t n) +# (t o) +# (t n) +# (t a) +# (t s) +# (t s) +# (t o) +# (t c) +# (n SPACE)) +# +# DOT = (x (t .) +# (n SPACE)) +# +# DPACC = (x (t p) +# (t a) +# (t r) +# (t s) +# (t e) +# (t _) +# (t a) +# (t c) +# (t c) +# (t e) +# (t p) +# (t t) +# (n SPACE)) +# +# DPFAIL = (x (t p) +# (t a) +# (t r) +# (t s) +# (t e) +# (t _) +# (t f) +# (t a) +# (t i) +# (t l) +# (t u) +# (t r) +# (t e) +# (n SPACE)) +# +# DRIGHT = (x (t r) +# (t i) +# (t g) +# (t h) +# (t t) +# (n SPACE)) +# +# DSTART = (x (t s) +# (t t) +# (t a) +# (t r) +# (t t) +# (t _) +# (t s) +# (t y) +# (t m) +# (t b) +# (t o) +# (t l) +# (n SPACE)) +# +# DSTKOVER = (x (t s) +# (t t) +# (t a) +# (t c) +# (t k) +# (t _) +# (t o) +# (t v) +# (t e) +# (t r) +# (t f) +# (t l) +# (t o) +# (t w) +# (n SPACE)) +# +# DSTKSZ = (x (t s) +# (t t) +# (t a) +# (t c) +# (t k) +# (t _) +# (t s) +# (t i) +# (t z) +# (t e) +# (n SPACE)) +# +# DSYNERR = (x (t s) +# (t y) +# (t n) +# (t t) +# (t a) +# (t x) +# (t _) +# (t e) +# (t r) +# (t r) +# (t o) +# (t r) +# (n SPACE)) +# +# DTOKDEST = (x (t t) +# (t o) +# (t k) +# (t e) +# (t n) +# (t _) +# (t d) +# (t e) +# (t s) +# (t t) +# (t r) +# (t u) +# (t c) +# (t t) +# (t o) +# (t r) +# (n SPACE)) +# +# DTOKPFX = (x (t t) +# (t o) +# (t k) +# (t e) +# (t n) +# (t _) +# (t p) +# (t r) +# (t e) +# (t f) +# (t i) +# (t x) +# (n SPACE)) +# +# DTOKTYPE = (x (t t) +# (t o) +# (t k) +# (t e) +# (t n) +# (t _) +# (t t) +# (t y) +# (t p) +# (t e) +# (n SPACE)) +# +# DTYPE = (x (t t) +# (t y) +# (t p) +# (t e) +# (n SPACE)) +# +# Endif = (n DENDIF) +# +# EOF = (! (dot)) +# +# EOL = (/ (x (t \r) +# (t \n)) +# (t \r) +# (t \n)) +# +# ExtraArgument = (x (n DEXTRA) +# (n Codeblock)) +# +# Fallback = (x (n DFALLBK) +# (+ (n Identifier)) +# (n DOT)) +# +# Ident = (x (/ (alpha) +# (t _)) +# (* (/ (alnum) +# (t _)))) +# +# Identifier = (x (n Ident) +# (n SPACE)) +# +# Ifdef = (x (n DIFDEF) +# (n Identifier)) +# +# Ifndef = (x (n DIFNDEF) +# (n Identifier)) +# +# Include = (x (n DINCL) +# (n Codeblock)) +# +# Label = (x (n LPAREN) +# (n Identifier) +# (n RPAREN)) +# +# LBRACE = (t \{) +# +# LBRACKET = (x (t [) +# (n SPACE)) +# +# Left = (x (n DLEFT) +# (+ (n Identifier)) +# (n DOT)) +# +# LemonGrammar = (x (n SPACE) +# (+ (n Statement)) +# (n EOF)) +# +# LPAREN = (x (t \() +# (n SPACE)) +# +# Name = (x (n DNAME) +# (n Identifier)) +# +# NatNum = (+ (.. 0 9)) +# +# NaturalNumber = (x (n NatNum) +# (n SPACE)) +# +# Nonassoc = (x (n DNON) +# (+ (n Identifier)) +# (n DOT)) +# +# ParseAccept = (x (n DPACC) +# (n Codeblock)) +# +# ParseFailure = (x (n DPFAIL) +# (n Codeblock)) +# +# Precedence = (x (n LBRACKET) +# (n Identifier) +# (n RBRACKET)) +# +# RBRACE = (t \}) +# +# RBRACKET = (x (t ]) +# (n SPACE)) +# +# Right = (x (n DRIGHT) +# (+ (n Identifier)) +# (n DOT)) +# +# RPAREN = (x (t \)) +# (n SPACE)) +# +# Rule = (x (n Identifier) +# (? (n Label)) +# (n ASSIGN) +# (n Definition) +# (n DOT) +# (? (n Precedence)) +# (? (n Codeblock))) +# +# SPACE = (* (/ (t <blank>) +# (t \t) +# (t \n) +# (t \r) +# (n C_COMMENT) +# (n Cplusplus_COMMENT) +# (n Ifndef) +# (n Ifdef) +# (n Endif))) +# +# StackOverflow = (x (n DSTKOVER) +# (n Codeblock)) +# +# Stacksize = (x (n DSTKSZ) +# (n NaturalNumber)) +# +# StartSymbol = (x (n DSTART) +# (n Identifier)) +# +# Statement = (x (/ (n Directive) +# (n Rule)) +# (n SPACE)) +# +# SyntaxError = (x (n DSYNERR) +# (n Codeblock)) +# +# TokenDestructor = (x (n DTOKDEST) +# (n Identifier) +# (n Codeblock)) +# +# TokenPrefix = (x (n DTOKPFX) +# (n Identifier)) +# +# TokenType = (x (n DTOKTYPE) +# (n Codeblock)) +# +# Type = (x (n DTYPE) +# (n Identifier) +# (n Codeblock)) +# + +proc ::page::parse::lemon::matchSymbol_ASSIGN {} { + # ASSIGN = (x (t :) + # (t :) + # (t =) + # (n SPACE)) + + if {[inc_restore ASSIGN]} return + + set pos [icl_get] + + eseq53 ; # (x (t :) + # (t :) + # (t =) + # (n SPACE)) + + isv_clear + inc_save ASSIGN $pos + ier_nonterminal ASSIGN $pos + return +} + +proc ::page::parse::lemon::eseq53 {} { + + # (x (t :) + # (t :) + # (t =) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance : + if {$ok} {ict_match_token :} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance : + if {$ok} {ict_match_token :} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance = + if {$ok} {ict_match_token =} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_C_COMMENT {} { + # C_COMMENT = (x (n CCOM_OPEN) + # (* (x (! (n CCOM_CLOSE)) + # (dot))) + # (n CCOM_CLOSE)) + + if {[inc_restore C_COMMENT]} return + + set pos [icl_get] + + eseq90 ; # (x (n CCOM_OPEN) + # (* (x (! (n CCOM_CLOSE)) + # (dot))) + # (n CCOM_CLOSE)) + + isv_clear + inc_save C_COMMENT $pos + ier_nonterminal C_COMMENT $pos + return +} + +proc ::page::parse::lemon::eseq90 {} { + + # (x (n CCOM_OPEN) + # (* (x (! (n CCOM_CLOSE)) + # (dot))) + # (n CCOM_CLOSE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_CCOM_OPEN ; # (n CCOM_OPEN) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene89 ; # (* (x (! (n CCOM_CLOSE)) + # (dot))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_CCOM_CLOSE ; # (n CCOM_CLOSE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ekleene89 {} { + + # (* (x (! (n CCOM_CLOSE)) + # (dot))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq88 ; # (x (! (n CCOM_CLOSE)) + # (dot)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::eseq88 {} { + + # (x (! (n CCOM_CLOSE)) + # (dot)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang87 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "any character" + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ebang87 {} { + set pos [icl_get] + + matchSymbol_CCOM_CLOSE ; # (n CCOM_CLOSE) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::lemon::matchSymbol_CCOM_CLOSE {} { + # CCOM_CLOSE = (x (t *) + # (t /)) + + if {[inc_restore CCOM_CLOSE]} return + + set pos [icl_get] + + eseq92 ; # (x (t *) + # (t /)) + + isv_clear + inc_save CCOM_CLOSE $pos + ier_nonterminal CCOM_CLOSE $pos + return +} + +proc ::page::parse::lemon::eseq92 {} { + + # (x (t *) + # (t /)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance * + if {$ok} {ict_match_token *} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance / + if {$ok} {ict_match_token /} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_CCOM_OPEN {} { + # CCOM_OPEN = (x (t /) + # (t *)) + + if {[inc_restore CCOM_OPEN]} return + + set pos [icl_get] + + eseq91 ; # (x (t /) + # (t *)) + + isv_clear + inc_save CCOM_OPEN $pos + ier_nonterminal CCOM_OPEN $pos + return +} + +proc ::page::parse::lemon::eseq91 {} { + + # (x (t /) + # (t *)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance / + if {$ok} {ict_match_token /} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance * + if {$ok} {ict_match_token *} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Code {} { + # Code = (x (n DCODE) + # (n Codeblock)) + + variable ok + if {[inc_restore Code]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq16 ; # (x (n DCODE) + # (n Codeblock)) + + isv_nonterminal_reduce Code $pos $mrk + inc_save Code $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Code $pos + return +} + +proc ::page::parse::lemon::eseq16 {} { + + # (x (n DCODE) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DCODE ; # (n DCODE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Codeblock {} { + # Codeblock = (x (n LBRACE) + # (* (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot)))) + # (n RBRACE)) + + variable ok + if {[inc_restore Codeblock]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq45 ; # (x (n LBRACE) + # (* (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot)))) + # (n RBRACE)) + + isv_nonterminal_range Codeblock $pos + inc_save Codeblock $pos + if {$ok} ias_push + ier_nonterminal Codeblock $pos + return +} + +proc ::page::parse::lemon::eseq45 {} { + + # (x (n LBRACE) + # (* (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot)))) + # (n RBRACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_LBRACE ; # (n LBRACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene44 ; # (* (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot)))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_RBRACE ; # (n RBRACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ekleene44 {} { + + # (* (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot)))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + ebra43 ; # (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot))) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::ebra43 {} { + + # (/ (n Codeblock) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (x (! (n RBRACE)) + # (dot))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + set pCodeblock [ias_mark] + matchSymbol_Codeblock + ias_pop2mark $pCodeblock ; # (n Codeblock) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_C_COMMENT ; # (n C_COMMENT) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_Cplusplus_COMMENT ; # (n Cplusplus_COMMENT) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + eseq42 ; # (x (! (n RBRACE)) + # (dot)) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::eseq42 {} { + + # (x (! (n RBRACE)) + # (dot)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang41 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "any character" + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ebang41 {} { + set pos [icl_get] + + matchSymbol_RBRACE ; # (n RBRACE) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::lemon::matchSymbol_Cplusplus_COMMENT {} { + # Cplusplus_COMMENT = (x (t /) + # (t /) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + if {[inc_restore Cplusplus_COMMENT]} return + + set pos [icl_get] + + eseq96 ; # (x (t /) + # (t /) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + isv_clear + inc_save Cplusplus_COMMENT $pos + ier_nonterminal Cplusplus_COMMENT $pos + return +} + +proc ::page::parse::lemon::eseq96 {} { + + # (x (t /) + # (t /) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance / + if {$ok} {ict_match_token /} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance / + if {$ok} {ict_match_token /} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene95 ; # (* (x (! (n EOL)) + # (dot))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_EOL ; # (n EOL) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ekleene95 {} { + + # (* (x (! (n EOL)) + # (dot))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq94 ; # (x (! (n EOL)) + # (dot)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::eseq94 {} { + + # (x (! (n EOL)) + # (dot)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang93 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "any character" + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ebang93 {} { + set pos [icl_get] + + matchSymbol_EOL ; # (n EOL) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::lemon::matchSymbol_DCODE {} { + # DCODE = (x (t c) + # (t o) + # (t d) + # (t e) + # (n SPACE)) + + if {[inc_restore DCODE]} return + + set pos [icl_get] + + eseq59 ; # (x (t c) + # (t o) + # (t d) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DCODE $pos + ier_nonterminal DCODE $pos + return +} + +proc ::page::parse::lemon::eseq59 {} { + + # (x (t c) + # (t o) + # (t d) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DDEFDEST {} { + # DDEFDEST = (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + if {[inc_restore DDEFDEST]} return + + set pos [icl_get] + + eseq60 ; # (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + isv_clear + inc_save DDEFDEST $pos + ier_nonterminal DDEFDEST $pos + return +} + +proc ::page::parse::lemon::eseq60 {} { + + # (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DDEFTYPE {} { + # DDEFTYPE = (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + if {[inc_restore DDEFTYPE]} return + + set pos [icl_get] + + eseq61 ; # (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DDEFTYPE $pos + ier_nonterminal DDEFTYPE $pos + return +} + +proc ::page::parse::lemon::eseq61 {} { + + # (x (t d) + # (t e) + # (t f) + # (t a) + # (t u) + # (t l) + # (t t) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance y + if {$ok} {ict_match_token y} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DDEST {} { + # DDEST = (x (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + if {[inc_restore DDEST]} return + + set pos [icl_get] + + eseq62 ; # (x (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + isv_clear + inc_save DDEST $pos + ier_nonterminal DDEST $pos + return +} + +proc ::page::parse::lemon::eseq62 {} { + + # (x (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DefaultDestructor {} { + # DefaultDestructor = (x (n DDEFDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + if {[inc_restore DefaultDestructor]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq17 ; # (x (n DDEFDEST) + # (n Identifier) + # (n Codeblock)) + + isv_nonterminal_reduce DefaultDestructor $pos $mrk + inc_save DefaultDestructor $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal DefaultDestructor $pos + return +} + +proc ::page::parse::lemon::eseq17 {} { + + # (x (n DDEFDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DDEFDEST ; # (n DDEFDEST) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_DefaultType {} { + # DefaultType = (x (n DDEFTYPE) + # (n Codeblock)) + + variable ok + if {[inc_restore DefaultType]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq18 ; # (x (n DDEFTYPE) + # (n Codeblock)) + + isv_nonterminal_reduce DefaultType $pos $mrk + inc_save DefaultType $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal DefaultType $pos + return +} + +proc ::page::parse::lemon::eseq18 {} { + + # (x (n DDEFTYPE) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DDEFTYPE ; # (n DDEFTYPE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Definition {} { + # Definition = (* (x (n Identifier) + # (? (n Label)))) + + variable ok + if {[inc_restore Definition]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + ekleene11 ; # (* (x (n Identifier) + # (? (n Label)))) + + isv_nonterminal_reduce Definition $pos $mrk + inc_save Definition $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Definition $pos + return +} + +proc ::page::parse::lemon::ekleene11 {} { + + # (* (x (n Identifier) + # (? (n Label)))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq10 ; # (x (n Identifier) + # (? (n Label))) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::eseq10 {} { + + # (x (n Identifier) + # (? (n Label))) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + eopt9 ; # (? (n Label)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::eopt9 {} { + + # (? (n Label)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Label ; # (n Label) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_DENDIF {} { + # DENDIF = (x (t %) + # (t e) + # (t n) + # (t d) + # (t i) + # (t f) + # (n SPACE)) + + if {[inc_restore DENDIF]} return + + set pos [icl_get] + + eseq82 ; # (x (t %) + # (t e) + # (t n) + # (t d) + # (t i) + # (t f) + # (n SPACE)) + + isv_clear + inc_save DENDIF $pos + ier_nonterminal DENDIF $pos + return +} + +proc ::page::parse::lemon::eseq82 {} { + + # (x (t %) + # (t e) + # (t n) + # (t d) + # (t i) + # (t f) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance % + if {$ok} {ict_match_token %} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Destructor {} { + # Destructor = (x (n DDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + if {[inc_restore Destructor]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq19 ; # (x (n DDEST) + # (n Identifier) + # (n Codeblock)) + + isv_nonterminal_reduce Destructor $pos $mrk + inc_save Destructor $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Destructor $pos + return +} + +proc ::page::parse::lemon::eseq19 {} { + + # (x (n DDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DDEST ; # (n DDEST) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_DEXTRA {} { + # DEXTRA = (x (t e) + # (t x) + # (t t) + # (t r) + # (t a) + # (t _) + # (t a) + # (t r) + # (t g) + # (t u) + # (t m) + # (t e) + # (t n) + # (t t) + # (n SPACE)) + + if {[inc_restore DEXTRA]} return + + set pos [icl_get] + + eseq63 ; # (x (t e) + # (t x) + # (t t) + # (t r) + # (t a) + # (t _) + # (t a) + # (t r) + # (t g) + # (t u) + # (t m) + # (t e) + # (t n) + # (t t) + # (n SPACE)) + + isv_clear + inc_save DEXTRA $pos + ier_nonterminal DEXTRA $pos + return +} + +proc ::page::parse::lemon::eseq63 {} { + + # (x (t e) + # (t x) + # (t t) + # (t r) + # (t a) + # (t _) + # (t a) + # (t r) + # (t g) + # (t u) + # (t m) + # (t e) + # (t n) + # (t t) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance x + if {$ok} {ict_match_token x} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance g + if {$ok} {ict_match_token g} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance m + if {$ok} {ict_match_token m} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DFALLBK {} { + # DFALLBK = (x (t f) + # (t a) + # (t l) + # (t l) + # (t b) + # (t a) + # (t c) + # (t k) + # (n SPACE)) + + if {[inc_restore DFALLBK]} return + + set pos [icl_get] + + eseq79 ; # (x (t f) + # (t a) + # (t l) + # (t l) + # (t b) + # (t a) + # (t c) + # (t k) + # (n SPACE)) + + isv_clear + inc_save DFALLBK $pos + ier_nonterminal DFALLBK $pos + return +} + +proc ::page::parse::lemon::eseq79 {} { + + # (x (t f) + # (t a) + # (t l) + # (t l) + # (t b) + # (t a) + # (t c) + # (t k) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance b + if {$ok} {ict_match_token b} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DIFDEF {} { + # DIFDEF = (x (t %) + # (t i) + # (t f) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + if {[inc_restore DIFDEF]} return + + set pos [icl_get] + + eseq80 ; # (x (t %) + # (t i) + # (t f) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + isv_clear + inc_save DIFDEF $pos + ier_nonterminal DIFDEF $pos + return +} + +proc ::page::parse::lemon::eseq80 {} { + + # (x (t %) + # (t i) + # (t f) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance % + if {$ok} {ict_match_token %} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DIFNDEF {} { + # DIFNDEF = (x (t %) + # (t i) + # (t f) + # (t n) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + if {[inc_restore DIFNDEF]} return + + set pos [icl_get] + + eseq81 ; # (x (t %) + # (t i) + # (t f) + # (t n) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + isv_clear + inc_save DIFNDEF $pos + ier_nonterminal DIFNDEF $pos + return +} + +proc ::page::parse::lemon::eseq81 {} { + + # (x (t %) + # (t i) + # (t f) + # (t n) + # (t d) + # (t e) + # (t f) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance % + if {$ok} {ict_match_token %} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DINCL {} { + # DINCL = (x (t i) + # (t n) + # (t c) + # (t l) + # (t u) + # (t d) + # (t e) + # (n SPACE)) + + if {[inc_restore DINCL]} return + + set pos [icl_get] + + eseq64 ; # (x (t i) + # (t n) + # (t c) + # (t l) + # (t u) + # (t d) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DINCL $pos + ier_nonterminal DINCL $pos + return +} + +proc ::page::parse::lemon::eseq64 {} { + + # (x (t i) + # (t n) + # (t c) + # (t l) + # (t u) + # (t d) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DINTRO {} { + # DINTRO = (t %) + + variable ok + if {[inc_restore DINTRO]} return + + set pos [icl_get] + + ict_advance % + if {$ok} {ict_match_token %} + + isv_clear + inc_save DINTRO $pos + ier_nonterminal DINTRO $pos + return +} + +proc ::page::parse::lemon::matchSymbol_Directive {} { + # Directive = (x (n DINTRO) + # (/ (n Code) + # (n DefaultDestructor) + # (n DefaultType) + # (n Destructor) + # (n ExtraArgument) + # (n Include) + # (n Left) + # (n Name) + # (n Nonassoc) + # (n ParseAccept) + # (n ParseFailure) + # (n Right) + # (n StackOverflow) + # (n Stacksize) + # (n StartSymbol) + # (n SyntaxError) + # (n TokenDestructor) + # (n TokenPrefix) + # (n TokenType) + # (n Type) + # (n Fallback))) + + variable ok + if {[inc_restore Directive]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq15 ; # (x (n DINTRO) + # (/ (n Code) + # (n DefaultDestructor) + # (n DefaultType) + # (n Destructor) + # (n ExtraArgument) + # (n Include) + # (n Left) + # (n Name) + # (n Nonassoc) + # (n ParseAccept) + # (n ParseFailure) + # (n Right) + # (n StackOverflow) + # (n Stacksize) + # (n StartSymbol) + # (n SyntaxError) + # (n TokenDestructor) + # (n TokenPrefix) + # (n TokenType) + # (n Type) + # (n Fallback))) + + isv_nonterminal_reduce Directive $pos $mrk + inc_save Directive $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Directive $pos + return +} + +proc ::page::parse::lemon::eseq15 {} { + + # (x (n DINTRO) + # (/ (n Code) + # (n DefaultDestructor) + # (n DefaultType) + # (n Destructor) + # (n ExtraArgument) + # (n Include) + # (n Left) + # (n Name) + # (n Nonassoc) + # (n ParseAccept) + # (n ParseFailure) + # (n Right) + # (n StackOverflow) + # (n Stacksize) + # (n StartSymbol) + # (n SyntaxError) + # (n TokenDestructor) + # (n TokenPrefix) + # (n TokenType) + # (n Type) + # (n Fallback))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DINTRO ; # (n DINTRO) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + ebra14 ; # (/ (n Code) + # (n DefaultDestructor) + # (n DefaultType) + # (n Destructor) + # (n ExtraArgument) + # (n Include) + # (n Left) + # (n Name) + # (n Nonassoc) + # (n ParseAccept) + # (n ParseFailure) + # (n Right) + # (n StackOverflow) + # (n Stacksize) + # (n StartSymbol) + # (n SyntaxError) + # (n TokenDestructor) + # (n TokenPrefix) + # (n TokenType) + # (n Type) + # (n Fallback)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::ebra14 {} { + + # (/ (n Code) + # (n DefaultDestructor) + # (n DefaultType) + # (n Destructor) + # (n ExtraArgument) + # (n Include) + # (n Left) + # (n Name) + # (n Nonassoc) + # (n ParseAccept) + # (n ParseFailure) + # (n Right) + # (n StackOverflow) + # (n Stacksize) + # (n StartSymbol) + # (n SyntaxError) + # (n TokenDestructor) + # (n TokenPrefix) + # (n TokenType) + # (n Type) + # (n Fallback)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Code ; # (n Code) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_DefaultDestructor ; # (n DefaultDestructor) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_DefaultType ; # (n DefaultType) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Destructor ; # (n Destructor) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_ExtraArgument ; # (n ExtraArgument) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Include ; # (n Include) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Left ; # (n Left) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Name ; # (n Name) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Nonassoc ; # (n Nonassoc) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_ParseAccept ; # (n ParseAccept) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_ParseFailure ; # (n ParseFailure) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Right ; # (n Right) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_StackOverflow ; # (n StackOverflow) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Stacksize ; # (n Stacksize) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_StartSymbol ; # (n StartSymbol) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_SyntaxError ; # (n SyntaxError) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_TokenDestructor ; # (n TokenDestructor) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_TokenPrefix ; # (n TokenPrefix) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_TokenType ; # (n TokenType) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Type ; # (n Type) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Fallback ; # (n Fallback) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::matchSymbol_DLEFT {} { + # DLEFT = (x (t l) + # (t e) + # (t f) + # (t t) + # (n SPACE)) + + if {[inc_restore DLEFT]} return + + set pos [icl_get] + + eseq65 ; # (x (t l) + # (t e) + # (t f) + # (t t) + # (n SPACE)) + + isv_clear + inc_save DLEFT $pos + ier_nonterminal DLEFT $pos + return +} + +proc ::page::parse::lemon::eseq65 {} { + + # (x (t l) + # (t e) + # (t f) + # (t t) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DNAME {} { + # DNAME = (x (t n) + # (t a) + # (t m) + # (t e) + # (n SPACE)) + + if {[inc_restore DNAME]} return + + set pos [icl_get] + + eseq66 ; # (x (t n) + # (t a) + # (t m) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DNAME $pos + ier_nonterminal DNAME $pos + return +} + +proc ::page::parse::lemon::eseq66 {} { + + # (x (t n) + # (t a) + # (t m) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance m + if {$ok} {ict_match_token m} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DNON {} { + # DNON = (x (t n) + # (t o) + # (t n) + # (t a) + # (t s) + # (t s) + # (t o) + # (t c) + # (n SPACE)) + + if {[inc_restore DNON]} return + + set pos [icl_get] + + eseq67 ; # (x (t n) + # (t o) + # (t n) + # (t a) + # (t s) + # (t s) + # (t o) + # (t c) + # (n SPACE)) + + isv_clear + inc_save DNON $pos + ier_nonterminal DNON $pos + return +} + +proc ::page::parse::lemon::eseq67 {} { + + # (x (t n) + # (t o) + # (t n) + # (t a) + # (t s) + # (t s) + # (t o) + # (t c) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DOT {} { + # DOT = (x (t .) + # (n SPACE)) + + if {[inc_restore DOT]} return + + set pos [icl_get] + + eseq54 ; # (x (t .) + # (n SPACE)) + + isv_clear + inc_save DOT $pos + ier_nonterminal DOT $pos + return +} + +proc ::page::parse::lemon::eseq54 {} { + + # (x (t .) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance . + if {$ok} {ict_match_token .} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DPACC {} { + # DPACC = (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t a) + # (t c) + # (t c) + # (t e) + # (t p) + # (t t) + # (n SPACE)) + + if {[inc_restore DPACC]} return + + set pos [icl_get] + + eseq68 ; # (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t a) + # (t c) + # (t c) + # (t e) + # (t p) + # (t t) + # (n SPACE)) + + isv_clear + inc_save DPACC $pos + ier_nonterminal DPACC $pos + return +} + +proc ::page::parse::lemon::eseq68 {} { + + # (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t a) + # (t c) + # (t c) + # (t e) + # (t p) + # (t t) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DPFAIL {} { + # DPFAIL = (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t f) + # (t a) + # (t i) + # (t l) + # (t u) + # (t r) + # (t e) + # (n SPACE)) + + if {[inc_restore DPFAIL]} return + + set pos [icl_get] + + eseq69 ; # (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t f) + # (t a) + # (t i) + # (t l) + # (t u) + # (t r) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DPFAIL $pos + ier_nonterminal DPFAIL $pos + return +} + +proc ::page::parse::lemon::eseq69 {} { + + # (x (t p) + # (t a) + # (t r) + # (t s) + # (t e) + # (t _) + # (t f) + # (t a) + # (t i) + # (t l) + # (t u) + # (t r) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DRIGHT {} { + # DRIGHT = (x (t r) + # (t i) + # (t g) + # (t h) + # (t t) + # (n SPACE)) + + if {[inc_restore DRIGHT]} return + + set pos [icl_get] + + eseq70 ; # (x (t r) + # (t i) + # (t g) + # (t h) + # (t t) + # (n SPACE)) + + isv_clear + inc_save DRIGHT $pos + ier_nonterminal DRIGHT $pos + return +} + +proc ::page::parse::lemon::eseq70 {} { + + # (x (t r) + # (t i) + # (t g) + # (t h) + # (t t) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance g + if {$ok} {ict_match_token g} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance h + if {$ok} {ict_match_token h} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DSTART {} { + # DSTART = (x (t s) + # (t t) + # (t a) + # (t r) + # (t t) + # (t _) + # (t s) + # (t y) + # (t m) + # (t b) + # (t o) + # (t l) + # (n SPACE)) + + if {[inc_restore DSTART]} return + + set pos [icl_get] + + eseq73 ; # (x (t s) + # (t t) + # (t a) + # (t r) + # (t t) + # (t _) + # (t s) + # (t y) + # (t m) + # (t b) + # (t o) + # (t l) + # (n SPACE)) + + isv_clear + inc_save DSTART $pos + ier_nonterminal DSTART $pos + return +} + +proc ::page::parse::lemon::eseq73 {} { + + # (x (t s) + # (t t) + # (t a) + # (t r) + # (t t) + # (t _) + # (t s) + # (t y) + # (t m) + # (t b) + # (t o) + # (t l) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance y + if {$ok} {ict_match_token y} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance m + if {$ok} {ict_match_token m} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance b + if {$ok} {ict_match_token b} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DSTKOVER {} { + # DSTKOVER = (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t o) + # (t v) + # (t e) + # (t r) + # (t f) + # (t l) + # (t o) + # (t w) + # (n SPACE)) + + if {[inc_restore DSTKOVER]} return + + set pos [icl_get] + + eseq71 ; # (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t o) + # (t v) + # (t e) + # (t r) + # (t f) + # (t l) + # (t o) + # (t w) + # (n SPACE)) + + isv_clear + inc_save DSTKOVER $pos + ier_nonterminal DSTKOVER $pos + return +} + +proc ::page::parse::lemon::eseq71 {} { + + # (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t o) + # (t v) + # (t e) + # (t r) + # (t f) + # (t l) + # (t o) + # (t w) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance v + if {$ok} {ict_match_token v} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance l + if {$ok} {ict_match_token l} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance w + if {$ok} {ict_match_token w} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DSTKSZ {} { + # DSTKSZ = (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t s) + # (t i) + # (t z) + # (t e) + # (n SPACE)) + + if {[inc_restore DSTKSZ]} return + + set pos [icl_get] + + eseq72 ; # (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t s) + # (t i) + # (t z) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DSTKSZ $pos + ier_nonterminal DSTKSZ $pos + return +} + +proc ::page::parse::lemon::eseq72 {} { + + # (x (t s) + # (t t) + # (t a) + # (t c) + # (t k) + # (t _) + # (t s) + # (t i) + # (t z) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance z + if {$ok} {ict_match_token z} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DSYNERR {} { + # DSYNERR = (x (t s) + # (t y) + # (t n) + # (t t) + # (t a) + # (t x) + # (t _) + # (t e) + # (t r) + # (t r) + # (t o) + # (t r) + # (n SPACE)) + + if {[inc_restore DSYNERR]} return + + set pos [icl_get] + + eseq74 ; # (x (t s) + # (t y) + # (t n) + # (t t) + # (t a) + # (t x) + # (t _) + # (t e) + # (t r) + # (t r) + # (t o) + # (t r) + # (n SPACE)) + + isv_clear + inc_save DSYNERR $pos + ier_nonterminal DSYNERR $pos + return +} + +proc ::page::parse::lemon::eseq74 {} { + + # (x (t s) + # (t y) + # (t n) + # (t t) + # (t a) + # (t x) + # (t _) + # (t e) + # (t r) + # (t r) + # (t o) + # (t r) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance y + if {$ok} {ict_match_token y} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance a + if {$ok} {ict_match_token a} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance x + if {$ok} {ict_match_token x} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DTOKDEST {} { + # DTOKDEST = (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + if {[inc_restore DTOKDEST]} return + + set pos [icl_get] + + eseq75 ; # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + isv_clear + inc_save DTOKDEST $pos + ier_nonterminal DTOKDEST $pos + return +} + +proc ::page::parse::lemon::eseq75 {} { + + # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t d) + # (t e) + # (t s) + # (t t) + # (t r) + # (t u) + # (t c) + # (t t) + # (t o) + # (t r) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance d + if {$ok} {ict_match_token d} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance s + if {$ok} {ict_match_token s} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance u + if {$ok} {ict_match_token u} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance c + if {$ok} {ict_match_token c} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DTOKPFX {} { + # DTOKPFX = (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t p) + # (t r) + # (t e) + # (t f) + # (t i) + # (t x) + # (n SPACE)) + + if {[inc_restore DTOKPFX]} return + + set pos [icl_get] + + eseq76 ; # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t p) + # (t r) + # (t e) + # (t f) + # (t i) + # (t x) + # (n SPACE)) + + isv_clear + inc_save DTOKPFX $pos + ier_nonterminal DTOKPFX $pos + return +} + +proc ::page::parse::lemon::eseq76 {} { + + # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t p) + # (t r) + # (t e) + # (t f) + # (t i) + # (t x) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance r + if {$ok} {ict_match_token r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance f + if {$ok} {ict_match_token f} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance i + if {$ok} {ict_match_token i} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance x + if {$ok} {ict_match_token x} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DTOKTYPE {} { + # DTOKTYPE = (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + if {[inc_restore DTOKTYPE]} return + + set pos [icl_get] + + eseq77 ; # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DTOKTYPE $pos + ier_nonterminal DTOKTYPE $pos + return +} + +proc ::page::parse::lemon::eseq77 {} { + + # (x (t t) + # (t o) + # (t k) + # (t e) + # (t n) + # (t _) + # (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance o + if {$ok} {ict_match_token o} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance k + if {$ok} {ict_match_token k} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance n + if {$ok} {ict_match_token n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance y + if {$ok} {ict_match_token y} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_DTYPE {} { + # DTYPE = (x (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + if {[inc_restore DTYPE]} return + + set pos [icl_get] + + eseq78 ; # (x (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + isv_clear + inc_save DTYPE $pos + ier_nonterminal DTYPE $pos + return +} + +proc ::page::parse::lemon::eseq78 {} { + + # (x (t t) + # (t y) + # (t p) + # (t e) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance t + if {$ok} {ict_match_token t} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance y + if {$ok} {ict_match_token y} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance p + if {$ok} {ict_match_token p} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance e + if {$ok} {ict_match_token e} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Endif {} { + # Endif = (n DENDIF) + + if {[inc_restore Endif]} return + + set pos [icl_get] + + matchSymbol_DENDIF ; # (n DENDIF) + + isv_clear + inc_save Endif $pos + ier_nonterminal Endif $pos + return +} + +proc ::page::parse::lemon::matchSymbol_EOF {} { + # EOF = (! (dot)) + + if {[inc_restore EOF]} return + + set pos [icl_get] + + ebang99 + + isv_clear + inc_save EOF $pos + ier_nonterminal EOF $pos + return +} + +proc ::page::parse::lemon::ebang99 {} { + set pos [icl_get] + + ict_advance "any character" + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::lemon::matchSymbol_EOL {} { + # EOL = (/ (x (t \r) + # (t \n)) + # (t \r) + # (t \n)) + + if {[inc_restore EOL]} return + + set pos [icl_get] + + ebra98 ; # (/ (x (t \r) + # (t \n)) + # (t \r) + # (t \n)) + + isv_clear + inc_save EOL $pos + ier_nonterminal EOL $pos + return +} + +proc ::page::parse::lemon::ebra98 {} { + + # (/ (x (t \r) + # (t \n)) + # (t \r) + # (t \n)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + eseq97 ; # (x (t \r) + # (t \n)) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance \\r + if {$ok} {ict_match_token \r} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance \\n + if {$ok} {ict_match_token \n} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::eseq97 {} { + + # (x (t \r) + # (t \n)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance \\r + if {$ok} {ict_match_token \r} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance \\n + if {$ok} {ict_match_token \n} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_ExtraArgument {} { + # ExtraArgument = (x (n DEXTRA) + # (n Codeblock)) + + variable ok + if {[inc_restore ExtraArgument]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq20 ; # (x (n DEXTRA) + # (n Codeblock)) + + isv_nonterminal_reduce ExtraArgument $pos $mrk + inc_save ExtraArgument $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal ExtraArgument $pos + return +} + +proc ::page::parse::lemon::eseq20 {} { + + # (x (n DEXTRA) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DEXTRA ; # (n DEXTRA) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Fallback {} { + # Fallback = (x (n DFALLBK) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + if {[inc_restore Fallback]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq40 ; # (x (n DFALLBK) + # (+ (n Identifier)) + # (n DOT)) + + isv_nonterminal_reduce Fallback $pos $mrk + inc_save Fallback $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Fallback $pos + return +} + +proc ::page::parse::lemon::eseq40 {} { + + # (x (n DFALLBK) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DFALLBK ; # (n DFALLBK) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + epkleene39 ; # (+ (n Identifier)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::epkleene39 {} { + + # (+ (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_Ident {} { + # Ident = (x (/ (alpha) + # (t _)) + # (* (/ (alnum) + # (t _)))) + + variable ok + if {[inc_restore Ident]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq50 ; # (x (/ (alpha) + # (t _)) + # (* (/ (alnum) + # (t _)))) + + isv_nonterminal_range Ident $pos + inc_save Ident $pos + if {$ok} ias_push + ier_nonterminal Ident $pos + return +} + +proc ::page::parse::lemon::eseq50 {} { + + # (x (/ (alpha) + # (t _)) + # (* (/ (alnum) + # (t _)))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebra47 ; # (/ (alpha) + # (t _)) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene49 ; # (* (/ (alnum) + # (t _))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::ebra47 {} { + + # (/ (alpha) + # (t _)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance alpha + if {$ok} {ict_match_tokclass alpha} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::ekleene49 {} { + + # (* (/ (alnum) + # (t _))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + ebra48 ; # (/ (alnum) + # (t _)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::ebra48 {} { + + # (/ (alnum) + # (t _)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance alnum + if {$ok} {ict_match_tokclass alnum} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance _ + if {$ok} {ict_match_token _} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::matchSymbol_Identifier {} { + # Identifier = (x (n Ident) + # (n SPACE)) + + variable ok + if {[inc_restore Identifier]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq46 ; # (x (n Ident) + # (n SPACE)) + + isv_nonterminal_reduce Identifier $pos $mrk + inc_save Identifier $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Identifier $pos + return +} + +proc ::page::parse::lemon::eseq46 {} { + + # (x (n Ident) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Ident ; # (n Ident) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Ifdef {} { + # Ifdef = (x (n DIFDEF) + # (n Identifier)) + + if {[inc_restore Ifdef]} return + + set pos [icl_get] + + eseq83 ; # (x (n DIFDEF) + # (n Identifier)) + + isv_clear + inc_save Ifdef $pos + ier_nonterminal Ifdef $pos + return +} + +proc ::page::parse::lemon::eseq83 {} { + + # (x (n DIFDEF) + # (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DIFDEF ; # (n DIFDEF) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + set pIdentifier [ias_mark] + matchSymbol_Identifier + ias_pop2mark $pIdentifier ; # (n Identifier) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Ifndef {} { + # Ifndef = (x (n DIFNDEF) + # (n Identifier)) + + if {[inc_restore Ifndef]} return + + set pos [icl_get] + + eseq84 ; # (x (n DIFNDEF) + # (n Identifier)) + + isv_clear + inc_save Ifndef $pos + ier_nonterminal Ifndef $pos + return +} + +proc ::page::parse::lemon::eseq84 {} { + + # (x (n DIFNDEF) + # (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DIFNDEF ; # (n DIFNDEF) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + set pIdentifier [ias_mark] + matchSymbol_Identifier + ias_pop2mark $pIdentifier ; # (n Identifier) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Include {} { + # Include = (x (n DINCL) + # (n Codeblock)) + + variable ok + if {[inc_restore Include]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq21 ; # (x (n DINCL) + # (n Codeblock)) + + isv_nonterminal_reduce Include $pos $mrk + inc_save Include $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Include $pos + return +} + +proc ::page::parse::lemon::eseq21 {} { + + # (x (n DINCL) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DINCL ; # (n DINCL) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Label {} { + # Label = (x (n LPAREN) + # (n Identifier) + # (n RPAREN)) + + variable ok + if {[inc_restore Label]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq12 ; # (x (n LPAREN) + # (n Identifier) + # (n RPAREN)) + + isv_nonterminal_reduce Label $pos $mrk + inc_save Label $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Label $pos + return +} + +proc ::page::parse::lemon::eseq12 {} { + + # (x (n LPAREN) + # (n Identifier) + # (n RPAREN)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_LPAREN ; # (n LPAREN) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_RPAREN ; # (n RPAREN) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_LBRACE {} { + # LBRACE = (t \{) + + variable ok + if {[inc_restore LBRACE]} return + + set pos [icl_get] + + ict_advance \{ + if {$ok} {ict_match_token \173} + + isv_clear + inc_save LBRACE $pos + ier_nonterminal LBRACE $pos + return +} + +proc ::page::parse::lemon::matchSymbol_LBRACKET {} { + # LBRACKET = (x (t [) + # (n SPACE)) + + if {[inc_restore LBRACKET]} return + + set pos [icl_get] + + eseq57 ; # (x (t [) + # (n SPACE)) + + isv_clear + inc_save LBRACKET $pos + ier_nonterminal LBRACKET $pos + return +} + +proc ::page::parse::lemon::eseq57 {} { + + # (x (t [) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance \[ + if {$ok} {ict_match_token \133} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Left {} { + # Left = (x (n DLEFT) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + if {[inc_restore Left]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq23 ; # (x (n DLEFT) + # (+ (n Identifier)) + # (n DOT)) + + isv_nonterminal_reduce Left $pos $mrk + inc_save Left $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Left $pos + return +} + +proc ::page::parse::lemon::eseq23 {} { + + # (x (n DLEFT) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DLEFT ; # (n DLEFT) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + epkleene22 ; # (+ (n Identifier)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::epkleene22 {} { + + # (+ (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_LemonGrammar {} { + # LemonGrammar = (x (n SPACE) + # (+ (n Statement)) + # (n EOF)) + + variable ok + if {[inc_restore LemonGrammar]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq2 ; # (x (n SPACE) + # (+ (n Statement)) + # (n EOF)) + + isv_nonterminal_reduce LemonGrammar $pos $mrk + inc_save LemonGrammar $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal LemonGrammar $pos + return +} + +proc ::page::parse::lemon::eseq2 {} { + + # (x (n SPACE) + # (+ (n Statement)) + # (n EOF)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + epkleene1 ; # (+ (n Statement)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_EOF ; # (n EOF) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::epkleene1 {} { + + # (+ (n Statement)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Statement ; # (n Statement) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Statement ; # (n Statement) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_LPAREN {} { + # LPAREN = (x (t \() + # (n SPACE)) + + if {[inc_restore LPAREN]} return + + set pos [icl_get] + + eseq55 ; # (x (t \() + # (n SPACE)) + + isv_clear + inc_save LPAREN $pos + ier_nonterminal LPAREN $pos + return +} + +proc ::page::parse::lemon::eseq55 {} { + + # (x (t \() + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance \( + if {$ok} {ict_match_token \50} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Name {} { + # Name = (x (n DNAME) + # (n Identifier)) + + variable ok + if {[inc_restore Name]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq24 ; # (x (n DNAME) + # (n Identifier)) + + isv_nonterminal_reduce Name $pos $mrk + inc_save Name $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Name $pos + return +} + +proc ::page::parse::lemon::eseq24 {} { + + # (x (n DNAME) + # (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DNAME ; # (n DNAME) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_NatNum {} { + # NatNum = (+ (.. 0 9)) + + variable ok + if {[inc_restore NatNum]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + epkleene52 ; # (+ (.. 0 9)) + + isv_nonterminal_range NatNum $pos + inc_save NatNum $pos + if {$ok} ias_push + ier_nonterminal NatNum $pos + return +} + +proc ::page::parse::lemon::epkleene52 {} { + + # (+ (.. 0 9)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "any in 0..9" + if {$ok} {ict_match_tokrange 0 9} + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + ict_advance "any in 0..9" + if {$ok} {ict_match_tokrange 0 9} + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_NaturalNumber {} { + # NaturalNumber = (x (n NatNum) + # (n SPACE)) + + variable ok + if {[inc_restore NaturalNumber]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq51 ; # (x (n NatNum) + # (n SPACE)) + + isv_nonterminal_reduce NaturalNumber $pos $mrk + inc_save NaturalNumber $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal NaturalNumber $pos + return +} + +proc ::page::parse::lemon::eseq51 {} { + + # (x (n NatNum) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_NatNum ; # (n NatNum) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Nonassoc {} { + # Nonassoc = (x (n DNON) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + if {[inc_restore Nonassoc]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq26 ; # (x (n DNON) + # (+ (n Identifier)) + # (n DOT)) + + isv_nonterminal_reduce Nonassoc $pos $mrk + inc_save Nonassoc $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Nonassoc $pos + return +} + +proc ::page::parse::lemon::eseq26 {} { + + # (x (n DNON) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DNON ; # (n DNON) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + epkleene25 ; # (+ (n Identifier)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::epkleene25 {} { + + # (+ (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_ParseAccept {} { + # ParseAccept = (x (n DPACC) + # (n Codeblock)) + + variable ok + if {[inc_restore ParseAccept]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq27 ; # (x (n DPACC) + # (n Codeblock)) + + isv_nonterminal_reduce ParseAccept $pos $mrk + inc_save ParseAccept $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal ParseAccept $pos + return +} + +proc ::page::parse::lemon::eseq27 {} { + + # (x (n DPACC) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DPACC ; # (n DPACC) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_ParseFailure {} { + # ParseFailure = (x (n DPFAIL) + # (n Codeblock)) + + variable ok + if {[inc_restore ParseFailure]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq28 ; # (x (n DPFAIL) + # (n Codeblock)) + + isv_nonterminal_reduce ParseFailure $pos $mrk + inc_save ParseFailure $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal ParseFailure $pos + return +} + +proc ::page::parse::lemon::eseq28 {} { + + # (x (n DPFAIL) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DPFAIL ; # (n DPFAIL) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Precedence {} { + # Precedence = (x (n LBRACKET) + # (n Identifier) + # (n RBRACKET)) + + variable ok + if {[inc_restore Precedence]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq13 ; # (x (n LBRACKET) + # (n Identifier) + # (n RBRACKET)) + + isv_nonterminal_reduce Precedence $pos $mrk + inc_save Precedence $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Precedence $pos + return +} + +proc ::page::parse::lemon::eseq13 {} { + + # (x (n LBRACKET) + # (n Identifier) + # (n RBRACKET)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_LBRACKET ; # (n LBRACKET) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_RBRACKET ; # (n RBRACKET) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_RBRACE {} { + # RBRACE = (t \}) + + variable ok + if {[inc_restore RBRACE]} return + + set pos [icl_get] + + ict_advance \} + if {$ok} {ict_match_token \175} + + isv_clear + inc_save RBRACE $pos + ier_nonterminal RBRACE $pos + return +} + +proc ::page::parse::lemon::matchSymbol_RBRACKET {} { + # RBRACKET = (x (t ]) + # (n SPACE)) + + if {[inc_restore RBRACKET]} return + + set pos [icl_get] + + eseq58 ; # (x (t ]) + # (n SPACE)) + + isv_clear + inc_save RBRACKET $pos + ier_nonterminal RBRACKET $pos + return +} + +proc ::page::parse::lemon::eseq58 {} { + + # (x (t ]) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance \] + if {$ok} {ict_match_token \135} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Right {} { + # Right = (x (n DRIGHT) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + if {[inc_restore Right]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq30 ; # (x (n DRIGHT) + # (+ (n Identifier)) + # (n DOT)) + + isv_nonterminal_reduce Right $pos $mrk + inc_save Right $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Right $pos + return +} + +proc ::page::parse::lemon::eseq30 {} { + + # (x (n DRIGHT) + # (+ (n Identifier)) + # (n DOT)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DRIGHT ; # (n DRIGHT) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + epkleene29 ; # (+ (n Identifier)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::epkleene29 {} { + + # (+ (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_RPAREN {} { + # RPAREN = (x (t \)) + # (n SPACE)) + + if {[inc_restore RPAREN]} return + + set pos [icl_get] + + eseq56 ; # (x (t \)) + # (n SPACE)) + + isv_clear + inc_save RPAREN $pos + ier_nonterminal RPAREN $pos + return +} + +proc ::page::parse::lemon::eseq56 {} { + + # (x (t \)) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance \) + if {$ok} {ict_match_token \51} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::lemon::matchSymbol_Rule {} { + # Rule = (x (n Identifier) + # (? (n Label)) + # (n ASSIGN) + # (n Definition) + # (n DOT) + # (? (n Precedence)) + # (? (n Codeblock))) + + variable ok + if {[inc_restore Rule]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq8 ; # (x (n Identifier) + # (? (n Label)) + # (n ASSIGN) + # (n Definition) + # (n DOT) + # (? (n Precedence)) + # (? (n Codeblock))) + + isv_nonterminal_reduce Rule $pos $mrk + inc_save Rule $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Rule $pos + return +} + +proc ::page::parse::lemon::eseq8 {} { + + # (x (n Identifier) + # (? (n Label)) + # (n ASSIGN) + # (n Definition) + # (n DOT) + # (? (n Precedence)) + # (? (n Codeblock))) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + eopt5 ; # (? (n Label)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_ASSIGN ; # (n ASSIGN) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Definition ; # (n Definition) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + eopt6 ; # (? (n Precedence)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + eopt7 ; # (? (n Codeblock)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::eopt5 {} { + + # (? (n Label)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Label ; # (n Label) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::eopt6 {} { + + # (? (n Precedence)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Precedence ; # (n Precedence) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::eopt7 {} { + + # (? (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::matchSymbol_SPACE {} { + # SPACE = (* (/ (t <blank>) + # (t \t) + # (t \n) + # (t \r) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (n Ifndef) + # (n Ifdef) + # (n Endif))) + + if {[inc_restore SPACE]} return + + set pos [icl_get] + + ekleene86 ; # (* (/ (t <blank>) + # (t \t) + # (t \n) + # (t \r) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (n Ifndef) + # (n Ifdef) + # (n Endif))) + + isv_clear + inc_save SPACE $pos + ier_nonterminal SPACE $pos + return +} + +proc ::page::parse::lemon::ekleene86 {} { + + # (* (/ (t <blank>) + # (t \t) + # (t \n) + # (t \r) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (n Ifndef) + # (n Ifdef) + # (n Endif))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + ebra85 ; # (/ (t <blank>) + # (t \t) + # (t \n) + # (t \r) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (n Ifndef) + # (n Ifdef) + # (n Endif)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::lemon::ebra85 {} { + + # (/ (t <blank>) + # (t \t) + # (t \n) + # (t \r) + # (n C_COMMENT) + # (n Cplusplus_COMMENT) + # (n Ifndef) + # (n Ifdef) + # (n Endif)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance <blank> + if {$ok} {ict_match_token \40} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance \\t + if {$ok} {ict_match_token \t} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance \\n + if {$ok} {ict_match_token \n} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance \\r + if {$ok} {ict_match_token \r} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_C_COMMENT ; # (n C_COMMENT) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_Cplusplus_COMMENT ; # (n Cplusplus_COMMENT) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_Ifndef ; # (n Ifndef) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_Ifdef ; # (n Ifdef) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_Endif ; # (n Endif) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::matchSymbol_StackOverflow {} { + # StackOverflow = (x (n DSTKOVER) + # (n Codeblock)) + + variable ok + if {[inc_restore StackOverflow]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq31 ; # (x (n DSTKOVER) + # (n Codeblock)) + + isv_nonterminal_reduce StackOverflow $pos $mrk + inc_save StackOverflow $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal StackOverflow $pos + return +} + +proc ::page::parse::lemon::eseq31 {} { + + # (x (n DSTKOVER) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DSTKOVER ; # (n DSTKOVER) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Stacksize {} { + # Stacksize = (x (n DSTKSZ) + # (n NaturalNumber)) + + variable ok + if {[inc_restore Stacksize]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq32 ; # (x (n DSTKSZ) + # (n NaturalNumber)) + + isv_nonterminal_reduce Stacksize $pos $mrk + inc_save Stacksize $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Stacksize $pos + return +} + +proc ::page::parse::lemon::eseq32 {} { + + # (x (n DSTKSZ) + # (n NaturalNumber)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DSTKSZ ; # (n DSTKSZ) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_NaturalNumber ; # (n NaturalNumber) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_StartSymbol {} { + # StartSymbol = (x (n DSTART) + # (n Identifier)) + + variable ok + if {[inc_restore StartSymbol]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq33 ; # (x (n DSTART) + # (n Identifier)) + + isv_nonterminal_reduce StartSymbol $pos $mrk + inc_save StartSymbol $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal StartSymbol $pos + return +} + +proc ::page::parse::lemon::eseq33 {} { + + # (x (n DSTART) + # (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DSTART ; # (n DSTART) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Statement {} { + # Statement = (x (/ (n Directive) + # (n Rule)) + # (n SPACE)) + + variable ok + if {[inc_restore Statement]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq4 ; # (x (/ (n Directive) + # (n Rule)) + # (n SPACE)) + + isv_nonterminal_reduce Statement $pos $mrk + inc_save Statement $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Statement $pos + return +} + +proc ::page::parse::lemon::eseq4 {} { + + # (x (/ (n Directive) + # (n Rule)) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + ebra3 ; # (/ (n Directive) + # (n Rule)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::ebra3 {} { + + # (/ (n Directive) + # (n Rule)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Directive ; # (n Directive) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Rule ; # (n Rule) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::lemon::matchSymbol_SyntaxError {} { + # SyntaxError = (x (n DSYNERR) + # (n Codeblock)) + + variable ok + if {[inc_restore SyntaxError]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq34 ; # (x (n DSYNERR) + # (n Codeblock)) + + isv_nonterminal_reduce SyntaxError $pos $mrk + inc_save SyntaxError $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal SyntaxError $pos + return +} + +proc ::page::parse::lemon::eseq34 {} { + + # (x (n DSYNERR) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DSYNERR ; # (n DSYNERR) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_TokenDestructor {} { + # TokenDestructor = (x (n DTOKDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + if {[inc_restore TokenDestructor]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq35 ; # (x (n DTOKDEST) + # (n Identifier) + # (n Codeblock)) + + isv_nonterminal_reduce TokenDestructor $pos $mrk + inc_save TokenDestructor $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal TokenDestructor $pos + return +} + +proc ::page::parse::lemon::eseq35 {} { + + # (x (n DTOKDEST) + # (n Identifier) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DTOKDEST ; # (n DTOKDEST) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_TokenPrefix {} { + # TokenPrefix = (x (n DTOKPFX) + # (n Identifier)) + + variable ok + if {[inc_restore TokenPrefix]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq36 ; # (x (n DTOKPFX) + # (n Identifier)) + + isv_nonterminal_reduce TokenPrefix $pos $mrk + inc_save TokenPrefix $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal TokenPrefix $pos + return +} + +proc ::page::parse::lemon::eseq36 {} { + + # (x (n DTOKPFX) + # (n Identifier)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DTOKPFX ; # (n DTOKPFX) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_TokenType {} { + # TokenType = (x (n DTOKTYPE) + # (n Codeblock)) + + variable ok + if {[inc_restore TokenType]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq37 ; # (x (n DTOKTYPE) + # (n Codeblock)) + + isv_nonterminal_reduce TokenType $pos $mrk + inc_save TokenType $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal TokenType $pos + return +} + +proc ::page::parse::lemon::eseq37 {} { + + # (x (n DTOKTYPE) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DTOKTYPE ; # (n DTOKTYPE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::lemon::matchSymbol_Type {} { + # Type = (x (n DTYPE) + # (n Identifier) + # (n Codeblock)) + + variable ok + if {[inc_restore Type]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq38 ; # (x (n DTYPE) + # (n Identifier) + # (n Codeblock)) + + isv_nonterminal_reduce Type $pos $mrk + inc_save Type $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal Type $pos + return +} + +proc ::page::parse::lemon::eseq38 {} { + + # (x (n DTYPE) + # (n Identifier) + # (n Codeblock)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DTYPE ; # (n DTYPE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Codeblock ; # (n Codeblock) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +# ### ### ### ######### ######### ######### +## Package Management + +package provide page::parse::lemon 0.1 diff --git a/tcllib/modules/page/parse_peg.tcl b/tcllib/modules/page/parse_peg.tcl new file mode 100644 index 0000000..13f7265 --- /dev/null +++ b/tcllib/modules/page/parse_peg.tcl @@ -0,0 +1,4415 @@ +# -*- tcl -*- +## Parsing Expression Grammar 'pg::peg::grammar'. +## Recursive Descent Packrat parser generated +## by the PAGE writer plugin 'me'. +## (C) 2005 Andreas Kupries <andreas_kupries@users.sourceforge.net> + +# ### ### ### ######### ######### ######### +## Package description + +# The commands provided here match an input provided through a buffer +# command to the PE grammar 'pg::peg::grammar'. The parser is based on the package +# 'grammar::me::tcl' (recursive-descent, packrat, pulling chars, +# pushing the generated AST). + +# ### ### ### ######### ######### ######### +## Requisites + +package require grammar::me::tcl + +# ### ### ### ######### ######### ######### +## Implementation + +namespace eval ::page::parse::peg { + # Import the virtual machine for matching. + + namespace import ::grammar::me::tcl::* + upvar #0 ::grammar::me::tcl::ok ok +} + +# ### ### ### ######### ######### ######### +## API Implementation. + +proc ::page::parse::peg::parse {nxcmd emvar astvar} { + variable ok + variable se + + upvar 1 $emvar emsg $astvar ast + + init $nxcmd + + matchSymbol_Grammar ; # (n Grammar) + + isv_nonterminal_reduce ALL -1 + set ast [sv] + if {!$ok} { + foreach {l m} [ier_get] break + lappend l [lc $l] + set emsg [list $l $m] + } + + return $ok +} + +# ### ### ### ######### ######### ######### +## Internal helper methods + +# Grammar 'pg::peg::grammar' +# +# ALNUM = (x (t <) +# (t a) +# (t l) +# (t n) +# (t u) +# (t m) +# (t >) +# (n SPACE)) +# +# ALPHA = (x (t <) +# (t a) +# (t l) +# (t p) +# (t h) +# (t a) +# (t >) +# (n SPACE)) +# +# AND = (x (t &) +# (n SPACE)) +# +# APOSTROPH = (t ') +# +# Attribute = (x (/ (n VOID) +# (n LEAF) +# (n MATCH)) +# (n COLON)) +# +# Char = (/ (n CharSpecial) +# (n CharOctalFull) +# (n CharOctalPart) +# (n CharUnicode) +# (n CharUnescaped)) +# +# CharOctalFull = (x (t \) +# (.. 0 2) +# (.. 0 7) +# (.. 0 7)) +# +# CharOctalPart = (x (t \) +# (.. 0 7) +# (? (.. 0 7))) +# +# CharSpecial = (x (t \) +# (/ (t n) +# (t r) +# (t t) +# (t ') +# (t \") +# (t [) +# (t ]) +# (t \))) +# +# CharUnescaped = (x (! (t \)) +# (dot)) +# +# CharUnicode = (x (t \) +# (t u) +# (n HexDigit) +# (? (x (n HexDigit) +# (? (x (n HexDigit) +# (? (n HexDigit))))))) +# +# Class = (x (n OPENB) +# (* (x (! (n CLOSEB)) +# (n Range))) +# (n CLOSEB) +# (n SPACE)) +# +# CLOSE = (x (t \)) +# (n SPACE)) +# +# CLOSEB = (t ]) +# +# COLON = (x (t :) +# (n SPACE)) +# +# COMMENT = (x (t #) +# (* (x (! (n EOL)) +# (dot))) +# (n EOL)) +# +# DAPOSTROPH = (t \") +# +# Definition = (x (? (n Attribute)) +# (n Identifier) +# (n IS) +# (n Expression) +# (n SEMICOLON)) +# +# DOT = (x (t .) +# (n SPACE)) +# +# END = (x (t E) +# (t N) +# (t D) +# (n SPACE)) +# +# EOF = (! (dot)) +# +# EOL = (/ (x (t \n) +# (t \r)) +# (t \n) +# (t \r)) +# +# Expression = (x (n Sequence) +# (* (x (n SLASH) +# (n Sequence)))) +# +# Final = (x (n END) +# (n SEMICOLON) +# (n SPACE)) +# +# Grammar = (x (n SPACE) +# (n Header) +# (+ (n Definition)) +# (n Final) +# (n EOF)) +# +# Header = (x (n PEG) +# (n Identifier) +# (n StartExpr)) +# +# HexDigit = (/ (.. 0 9) +# (.. a f) +# (.. A F)) +# +# Ident = (x (/ (t _) +# (t :) +# (alpha)) +# (* (/ (t _) +# (t :) +# (alnum)))) +# +# Identifier = (x (n Ident) +# (n SPACE)) +# +# IS = (x (t <) +# (t -) +# (n SPACE)) +# +# LEAF = (x (t l) +# (t e) +# (t a) +# (t f) +# (n SPACE)) +# +# Literal = (/ (x (n APOSTROPH) +# (* (x (! (n APOSTROPH)) +# (n Char))) +# (n APOSTROPH) +# (n SPACE)) +# (x (n DAPOSTROPH) +# (* (x (! (n DAPOSTROPH)) +# (n Char))) +# (n DAPOSTROPH) +# (n SPACE))) +# +# MATCH = (x (t m) +# (t a) +# (t t) +# (t c) +# (t h) +# (n SPACE)) +# +# NOT = (x (t !) +# (n SPACE)) +# +# OPEN = (x (t \() +# (n SPACE)) +# +# OPENB = (t [) +# +# PEG = (x (t P) +# (t E) +# (t G) +# (n SPACE)) +# +# PLUS = (x (t +) +# (n SPACE)) +# +# Prefix = (x (? (/ (n AND) +# (n NOT))) +# (n Suffix)) +# +# Primary = (/ (n ALNUM) +# (n ALPHA) +# (n Identifier) +# (x (n OPEN) +# (n Expression) +# (n CLOSE)) +# (n Literal) +# (n Class) +# (n DOT)) +# +# QUESTION = (x (t ?) +# (n SPACE)) +# +# Range = (/ (x (n Char) +# (n TO) +# (n Char)) +# (n Char)) +# +# SEMICOLON = (x (t ;) +# (n SPACE)) +# +# Sequence = (+ (n Prefix)) +# +# SLASH = (x (t /) +# (n SPACE)) +# +# SPACE = (* (/ (t <blank>) +# (t \t) +# (n EOL) +# (n COMMENT))) +# +# STAR = (x (t *) +# (n SPACE)) +# +# StartExpr = (x (n OPEN) +# (n Expression) +# (n CLOSE)) +# +# Suffix = (x (n Primary) +# (? (/ (n QUESTION) +# (n STAR) +# (n PLUS)))) +# +# TO = (t -) +# +# VOID = (x (t v) +# (t o) +# (t i) +# (t d) +# (n SPACE)) +# + +proc ::page::parse::peg::matchSymbol_ALNUM {} { + # ALNUM = (x (t <) + # (t a) + # (t l) + # (t n) + # (t u) + # (t m) + # (t >) + # (n SPACE)) + + variable ok + if {[inc_restore ALNUM]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq75 ; # (x (t <) + # (t a) + # (t l) + # (t n) + # (t u) + # (t m) + # (t >) + # (n SPACE)) + + isv_nonterminal_leaf ALNUM $pos + inc_save ALNUM $pos + if {$ok} ias_push + ier_nonterminal "Expected ALNUM" $pos + return +} + +proc ::page::parse::peg::eseq75 {} { + + # (x (t <) + # (t a) + # (t l) + # (t n) + # (t u) + # (t m) + # (t >) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected < (got EOF)" + if {$ok} {ict_match_token < "Expected <"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected a (got EOF)" + if {$ok} {ict_match_token a "Expected a"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected l (got EOF)" + if {$ok} {ict_match_token l "Expected l"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected n (got EOF)" + if {$ok} {ict_match_token n "Expected n"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected u (got EOF)" + if {$ok} {ict_match_token u "Expected u"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected m (got EOF)" + if {$ok} {ict_match_token m "Expected m"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected > (got EOF)" + if {$ok} {ict_match_token > "Expected >"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_ALPHA {} { + # ALPHA = (x (t <) + # (t a) + # (t l) + # (t p) + # (t h) + # (t a) + # (t >) + # (n SPACE)) + + variable ok + if {[inc_restore ALPHA]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq74 ; # (x (t <) + # (t a) + # (t l) + # (t p) + # (t h) + # (t a) + # (t >) + # (n SPACE)) + + isv_nonterminal_leaf ALPHA $pos + inc_save ALPHA $pos + if {$ok} ias_push + ier_nonterminal "Expected ALPHA" $pos + return +} + +proc ::page::parse::peg::eseq74 {} { + + # (x (t <) + # (t a) + # (t l) + # (t p) + # (t h) + # (t a) + # (t >) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected < (got EOF)" + if {$ok} {ict_match_token < "Expected <"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected a (got EOF)" + if {$ok} {ict_match_token a "Expected a"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected l (got EOF)" + if {$ok} {ict_match_token l "Expected l"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected p (got EOF)" + if {$ok} {ict_match_token p "Expected p"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected h (got EOF)" + if {$ok} {ict_match_token h "Expected h"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected a (got EOF)" + if {$ok} {ict_match_token a "Expected a"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected > (got EOF)" + if {$ok} {ict_match_token > "Expected >"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_AND {} { + # AND = (x (t &) + # (n SPACE)) + + variable ok + if {[inc_restore AND]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq66 ; # (x (t &) + # (n SPACE)) + + isv_nonterminal_leaf AND $pos + inc_save AND $pos + if {$ok} ias_push + ier_nonterminal "Expected AND" $pos + return +} + +proc ::page::parse::peg::eseq66 {} { + + # (x (t &) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected & (got EOF)" + if {$ok} {ict_match_token & "Expected &"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_APOSTROPH {} { + # APOSTROPH = (t ') + + variable ok + if {[inc_restore APOSTROPH]} return + + set pos [icl_get] + + ict_advance "Expected ' (got EOF)" + if {$ok} {ict_match_token ' "Expected '"} + + isv_clear + inc_save APOSTROPH $pos + ier_nonterminal "Expected APOSTROPH" $pos + return +} + +proc ::page::parse::peg::matchSymbol_Attribute {} { + # Attribute = (x (/ (n VOID) + # (n LEAF) + # (n MATCH)) + # (n COLON)) + + variable ok + if {[inc_restore Attribute]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq7 ; # (x (/ (n VOID) + # (n LEAF) + # (n MATCH)) + # (n COLON)) + + isv_nonterminal_reduce Attribute $pos $mrk + inc_save Attribute $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Attribute" $pos + return +} + +proc ::page::parse::peg::eseq7 {} { + + # (x (/ (n VOID) + # (n LEAF) + # (n MATCH)) + # (n COLON)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + ebra6 ; # (/ (n VOID) + # (n LEAF) + # (n MATCH)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_COLON ; # (n COLON) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ebra6 {} { + + # (/ (n VOID) + # (n LEAF) + # (n MATCH)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_VOID ; # (n VOID) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_LEAF ; # (n LEAF) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_MATCH ; # (n MATCH) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_Char {} { + # Char = (/ (n CharSpecial) + # (n CharOctalFull) + # (n CharOctalPart) + # (n CharUnicode) + # (n CharUnescaped)) + + variable ok + if {[inc_restore Char]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + ebra42 ; # (/ (n CharSpecial) + # (n CharOctalFull) + # (n CharOctalPart) + # (n CharUnicode) + # (n CharUnescaped)) + + isv_nonterminal_reduce Char $pos $mrk + inc_save Char $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Char" $pos + return +} + +proc ::page::parse::peg::ebra42 {} { + + # (/ (n CharSpecial) + # (n CharOctalFull) + # (n CharOctalPart) + # (n CharUnicode) + # (n CharUnescaped)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_CharSpecial ; # (n CharSpecial) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_CharOctalFull ; # (n CharOctalFull) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_CharOctalPart ; # (n CharOctalPart) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_CharUnicode ; # (n CharUnicode) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_CharUnescaped ; # (n CharUnescaped) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_CharOctalFull {} { + # CharOctalFull = (x (t \) + # (.. 0 2) + # (.. 0 7) + # (.. 0 7)) + + variable ok + if {[inc_restore CharOctalFull]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq45 ; # (x (t \) + # (.. 0 2) + # (.. 0 7) + # (.. 0 7)) + + isv_nonterminal_range CharOctalFull $pos + inc_save CharOctalFull $pos + if {$ok} ias_push + ier_nonterminal "Expected CharOctalFull" $pos + return +} + +proc ::page::parse::peg::eseq45 {} { + + # (x (t \) + # (.. 0 2) + # (.. 0 7) + # (.. 0 7)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected \[0..2\] (got EOF)" + if {$ok} {ict_match_tokrange 0 2 "Expected \[0..2\]"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected \[0..7\] (got EOF)" + if {$ok} {ict_match_tokrange 0 7 "Expected \[0..7\]"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected \[0..7\] (got EOF)" + if {$ok} {ict_match_tokrange 0 7 "Expected \[0..7\]"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_CharOctalPart {} { + # CharOctalPart = (x (t \) + # (.. 0 7) + # (? (.. 0 7))) + + variable ok + if {[inc_restore CharOctalPart]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq47 ; # (x (t \) + # (.. 0 7) + # (? (.. 0 7))) + + isv_nonterminal_range CharOctalPart $pos + inc_save CharOctalPart $pos + if {$ok} ias_push + ier_nonterminal "Expected CharOctalPart" $pos + return +} + +proc ::page::parse::peg::eseq47 {} { + + # (x (t \) + # (.. 0 7) + # (? (.. 0 7))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected \[0..7\] (got EOF)" + if {$ok} {ict_match_tokrange 0 7 "Expected \[0..7\]"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + eopt46 ; # (? (.. 0 7)) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::eopt46 {} { + + # (? (.. 0 7)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \[0..7\] (got EOF)" + if {$ok} {ict_match_tokrange 0 7 "Expected \[0..7\]"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::matchSymbol_CharSpecial {} { + # CharSpecial = (x (t \) + # (/ (t n) + # (t r) + # (t t) + # (t ') + # (t \") + # (t [) + # (t ]) + # (t \))) + + variable ok + if {[inc_restore CharSpecial]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq44 ; # (x (t \) + # (/ (t n) + # (t r) + # (t t) + # (t ') + # (t \") + # (t [) + # (t ]) + # (t \))) + + isv_nonterminal_range CharSpecial $pos + inc_save CharSpecial $pos + if {$ok} ias_push + ier_nonterminal "Expected CharSpecial" $pos + return +} + +proc ::page::parse::peg::eseq44 {} { + + # (x (t \) + # (/ (t n) + # (t r) + # (t t) + # (t ') + # (t \") + # (t [) + # (t ]) + # (t \))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ebra43 ; # (/ (t n) + # (t r) + # (t t) + # (t ') + # (t \") + # (t [) + # (t ]) + # (t \)) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::ebra43 {} { + + # (/ (t n) + # (t r) + # (t t) + # (t ') + # (t \") + # (t [) + # (t ]) + # (t \)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected n (got EOF)" + if {$ok} {ict_match_token n "Expected n"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected r (got EOF)" + if {$ok} {ict_match_token r "Expected r"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected t (got EOF)" + if {$ok} {ict_match_token t "Expected t"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected ' (got EOF)" + if {$ok} {ict_match_token ' "Expected '"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \" (got EOF)" + if {$ok} {ict_match_token \42 "Expected \""} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \[ (got EOF)" + if {$ok} {ict_match_token \133 "Expected \["} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \] (got EOF)" + if {$ok} {ict_match_token \135 "Expected \]"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_CharUnescaped {} { + # CharUnescaped = (x (! (t \)) + # (dot)) + + variable ok + if {[inc_restore CharUnescaped]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq55 ; # (x (! (t \)) + # (dot)) + + isv_nonterminal_range CharUnescaped $pos + inc_save CharUnescaped $pos + if {$ok} ias_push + ier_nonterminal "Expected CharUnescaped" $pos + return +} + +proc ::page::parse::peg::eseq55 {} { + + # (x (! (t \)) + # (dot)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang54 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected any character (got EOF)" + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::ebang54 {} { + variable ok + + set pos [icl_get] + + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::matchSymbol_CharUnicode {} { + # CharUnicode = (x (t \) + # (t u) + # (n HexDigit) + # (? (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit))))))) + + variable ok + if {[inc_restore CharUnicode]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq53 ; # (x (t \) + # (t u) + # (n HexDigit) + # (? (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit))))))) + + isv_nonterminal_range CharUnicode $pos + inc_save CharUnicode $pos + if {$ok} ias_push + ier_nonterminal "Expected CharUnicode" $pos + return +} + +proc ::page::parse::peg::eseq53 {} { + + # (x (t \) + # (t u) + # (n HexDigit) + # (? (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit))))))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \\ (got EOF)" + if {$ok} {ict_match_token \134 "Expected \\"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected u (got EOF)" + if {$ok} {ict_match_token u "Expected u"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_HexDigit ; # (n HexDigit) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + eopt52 ; # (? (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit)))))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::eopt52 {} { + + # (? (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit)))))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + eseq51 ; # (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit))))) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq51 {} { + + # (x (n HexDigit) + # (? (x (n HexDigit) + # (? (n HexDigit))))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_HexDigit ; # (n HexDigit) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + eopt50 ; # (? (x (n HexDigit) + # (? (n HexDigit)))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::eopt50 {} { + + # (? (x (n HexDigit) + # (? (n HexDigit)))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + eseq49 ; # (x (n HexDigit) + # (? (n HexDigit))) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq49 {} { + + # (x (n HexDigit) + # (? (n HexDigit))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_HexDigit ; # (n HexDigit) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + eopt48 ; # (? (n HexDigit)) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::eopt48 {} { + + # (? (n HexDigit)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_HexDigit ; # (n HexDigit) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::matchSymbol_Class {} { + # Class = (x (n OPENB) + # (* (x (! (n CLOSEB)) + # (n Range))) + # (n CLOSEB) + # (n SPACE)) + + variable ok + if {[inc_restore Class]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq32 ; # (x (n OPENB) + # (* (x (! (n CLOSEB)) + # (n Range))) + # (n CLOSEB) + # (n SPACE)) + + isv_nonterminal_reduce Class $pos $mrk + inc_save Class $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Class" $pos + return +} + +proc ::page::parse::peg::eseq32 {} { + + # (x (n OPENB) + # (* (x (! (n CLOSEB)) + # (n Range))) + # (n CLOSEB) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_OPENB ; # (n OPENB) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + ekleene31 ; # (* (x (! (n CLOSEB)) + # (n Range))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_CLOSEB ; # (n CLOSEB) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ekleene31 {} { + + # (* (x (! (n CLOSEB)) + # (n Range))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq30 ; # (x (! (n CLOSEB)) + # (n Range)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq30 {} { + + # (x (! (n CLOSEB)) + # (n Range)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang29 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Range ; # (n Range) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ebang29 {} { + set pos [icl_get] + + matchSymbol_CLOSEB ; # (n CLOSEB) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::matchSymbol_CLOSE {} { + # CLOSE = (x (t \)) + # (n SPACE)) + + if {[inc_restore CLOSE]} return + + set pos [icl_get] + + eseq72 ; # (x (t \)) + # (n SPACE)) + + isv_clear + inc_save CLOSE $pos + ier_nonterminal "Expected CLOSE" $pos + return +} + +proc ::page::parse::peg::eseq72 {} { + + # (x (t \)) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \) (got EOF)" + if {$ok} {ict_match_token \51 "Expected \)"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_CLOSEB {} { + # CLOSEB = (t ]) + + variable ok + if {[inc_restore CLOSEB]} return + + set pos [icl_get] + + ict_advance "Expected \] (got EOF)" + if {$ok} {ict_match_token \135 "Expected \]"} + + isv_clear + inc_save CLOSEB $pos + ier_nonterminal "Expected CLOSEB" $pos + return +} + +proc ::page::parse::peg::matchSymbol_COLON {} { + # COLON = (x (t :) + # (n SPACE)) + + if {[inc_restore COLON]} return + + set pos [icl_get] + + eseq64 ; # (x (t :) + # (n SPACE)) + + isv_clear + inc_save COLON $pos + ier_nonterminal "Expected COLON" $pos + return +} + +proc ::page::parse::peg::eseq64 {} { + + # (x (t :) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected : (got EOF)" + if {$ok} {ict_match_token : "Expected :"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_COMMENT {} { + # COMMENT = (x (t #) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + if {[inc_restore COMMENT]} return + + set pos [icl_get] + + eseq81 ; # (x (t #) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + isv_clear + inc_save COMMENT $pos + ier_nonterminal "Expected COMMENT" $pos + return +} + +proc ::page::parse::peg::eseq81 {} { + + # (x (t #) + # (* (x (! (n EOL)) + # (dot))) + # (n EOL)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected # (got EOF)" + if {$ok} {ict_match_token # "Expected #"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene80 ; # (* (x (! (n EOL)) + # (dot))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_EOL ; # (n EOL) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::ekleene80 {} { + + # (* (x (! (n EOL)) + # (dot))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq79 ; # (x (! (n EOL)) + # (dot)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq79 {} { + + # (x (! (n EOL)) + # (dot)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang78 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected any character (got EOF)" + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::ebang78 {} { + set pos [icl_get] + + matchSymbol_EOL ; # (n EOL) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::matchSymbol_DAPOSTROPH {} { + # DAPOSTROPH = (t \") + + variable ok + if {[inc_restore DAPOSTROPH]} return + + set pos [icl_get] + + ict_advance "Expected \" (got EOF)" + if {$ok} {ict_match_token \42 "Expected \""} + + isv_clear + inc_save DAPOSTROPH $pos + ier_nonterminal "Expected DAPOSTROPH" $pos + return +} + +proc ::page::parse::peg::matchSymbol_Definition {} { + # Definition = (x (? (n Attribute)) + # (n Identifier) + # (n IS) + # (n Expression) + # (n SEMICOLON)) + + variable ok + if {[inc_restore Definition]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq5 ; # (x (? (n Attribute)) + # (n Identifier) + # (n IS) + # (n Expression) + # (n SEMICOLON)) + + isv_nonterminal_reduce Definition $pos $mrk + inc_save Definition $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Definition" $pos + return +} + +proc ::page::parse::peg::eseq5 {} { + + # (x (? (n Attribute)) + # (n Identifier) + # (n IS) + # (n Expression) + # (n SEMICOLON)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + eopt4 ; # (? (n Attribute)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_IS ; # (n IS) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Expression ; # (n Expression) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SEMICOLON ; # (n SEMICOLON) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::eopt4 {} { + + # (? (n Attribute)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Attribute ; # (n Attribute) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::matchSymbol_DOT {} { + # DOT = (x (t .) + # (n SPACE)) + + variable ok + if {[inc_restore DOT]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq73 ; # (x (t .) + # (n SPACE)) + + isv_nonterminal_leaf DOT $pos + inc_save DOT $pos + if {$ok} ias_push + ier_nonterminal "Expected DOT" $pos + return +} + +proc ::page::parse::peg::eseq73 {} { + + # (x (t .) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected . (got EOF)" + if {$ok} {ict_match_token . "Expected ."} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_END {} { + # END = (x (t E) + # (t N) + # (t D) + # (n SPACE)) + + if {[inc_restore END]} return + + set pos [icl_get] + + eseq62 ; # (x (t E) + # (t N) + # (t D) + # (n SPACE)) + + isv_clear + inc_save END $pos + ier_nonterminal "Expected END" $pos + return +} + +proc ::page::parse::peg::eseq62 {} { + + # (x (t E) + # (t N) + # (t D) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected E (got EOF)" + if {$ok} {ict_match_token E "Expected E"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected N (got EOF)" + if {$ok} {ict_match_token N "Expected N"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected D (got EOF)" + if {$ok} {ict_match_token D "Expected D"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_EOF {} { + # EOF = (! (dot)) + + if {[inc_restore EOF]} return + + set pos [icl_get] + + ebang84 + + isv_clear + inc_save EOF $pos + ier_nonterminal "Expected EOF" $pos + return +} + +proc ::page::parse::peg::ebang84 {} { + set pos [icl_get] + + ict_advance "Expected any character (got EOF)" + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::matchSymbol_EOL {} { + # EOL = (/ (x (t \n) + # (t \r)) + # (t \n) + # (t \r)) + + if {[inc_restore EOL]} return + + set pos [icl_get] + + ebra83 ; # (/ (x (t \n) + # (t \r)) + # (t \n) + # (t \r)) + + isv_clear + inc_save EOL $pos + ier_nonterminal "Expected EOL" $pos + return +} + +proc ::page::parse::peg::ebra83 {} { + + # (/ (x (t \n) + # (t \r)) + # (t \n) + # (t \r)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + eseq82 ; # (x (t \n) + # (t \r)) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \\n (got EOF)" + if {$ok} {ict_match_token \n "Expected \\n"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \\r (got EOF)" + if {$ok} {ict_match_token \r "Expected \\r"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::eseq82 {} { + + # (x (t \n) + # (t \r)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \\n (got EOF)" + if {$ok} {ict_match_token \n "Expected \\n"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected \\r (got EOF)" + if {$ok} {ict_match_token \r "Expected \\r"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Expression {} { + # Expression = (x (n Sequence) + # (* (x (n SLASH) + # (n Sequence)))) + + variable ok + if {[inc_restore Expression]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq10 ; # (x (n Sequence) + # (* (x (n SLASH) + # (n Sequence)))) + + isv_nonterminal_reduce Expression $pos $mrk + inc_save Expression $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Expression" $pos + return +} + +proc ::page::parse::peg::eseq10 {} { + + # (x (n Sequence) + # (* (x (n SLASH) + # (n Sequence)))) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Sequence ; # (n Sequence) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + ekleene9 ; # (* (x (n SLASH) + # (n Sequence))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ekleene9 {} { + + # (* (x (n SLASH) + # (n Sequence))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq8 ; # (x (n SLASH) + # (n Sequence)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq8 {} { + + # (x (n SLASH) + # (n Sequence)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_SLASH ; # (n SLASH) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Sequence ; # (n Sequence) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_Final {} { + # Final = (x (n END) + # (n SEMICOLON) + # (n SPACE)) + + if {[inc_restore Final]} return + + set pos [icl_get] + + eseq36 ; # (x (n END) + # (n SEMICOLON) + # (n SPACE)) + + isv_clear + inc_save Final $pos + ier_nonterminal "Expected Final" $pos + return +} + +proc ::page::parse::peg::eseq36 {} { + + # (x (n END) + # (n SEMICOLON) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_END ; # (n END) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SEMICOLON ; # (n SEMICOLON) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Grammar {} { + # Grammar = (x (n SPACE) + # (n Header) + # (+ (n Definition)) + # (n Final) + # (n EOF)) + + variable ok + if {[inc_restore Grammar]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq2 ; # (x (n SPACE) + # (n Header) + # (+ (n Definition)) + # (n Final) + # (n EOF)) + + isv_nonterminal_reduce Grammar $pos $mrk + inc_save Grammar $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Grammar" $pos + return +} + +proc ::page::parse::peg::eseq2 {} { + + # (x (n SPACE) + # (n Header) + # (+ (n Definition)) + # (n Final) + # (n EOF)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Header ; # (n Header) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + epkleene1 ; # (+ (n Definition)) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Final ; # (n Final) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_EOF ; # (n EOF) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::epkleene1 {} { + + # (+ (n Definition)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Definition ; # (n Definition) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Definition ; # (n Definition) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::matchSymbol_Header {} { + # Header = (x (n PEG) + # (n Identifier) + # (n StartExpr)) + + variable ok + if {[inc_restore Header]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq3 ; # (x (n PEG) + # (n Identifier) + # (n StartExpr)) + + isv_nonterminal_reduce Header $pos $mrk + inc_save Header $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Header" $pos + return +} + +proc ::page::parse::peg::eseq3 {} { + + # (x (n PEG) + # (n Identifier) + # (n StartExpr)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_PEG ; # (n PEG) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_StartExpr ; # (n StartExpr) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_HexDigit {} { + # HexDigit = (/ (.. 0 9) + # (.. a f) + # (.. A F)) + + if {[inc_restore HexDigit]} return + + set pos [icl_get] + + ebra56 ; # (/ (.. 0 9) + # (.. a f) + # (.. A F)) + + isv_clear + inc_save HexDigit $pos + ier_nonterminal "Expected HexDigit" $pos + return +} + +proc ::page::parse::peg::ebra56 {} { + + # (/ (.. 0 9) + # (.. a f) + # (.. A F)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \[0..9\] (got EOF)" + if {$ok} {ict_match_tokrange 0 9 "Expected \[0..9\]"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \[a..f\] (got EOF)" + if {$ok} {ict_match_tokrange a f "Expected \[a..f\]"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \[A..F\] (got EOF)" + if {$ok} {ict_match_tokrange A F "Expected \[A..F\]"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_Ident {} { + # Ident = (x (/ (t _) + # (t :) + # (alpha)) + # (* (/ (t _) + # (t :) + # (alnum)))) + + variable ok + if {[inc_restore Ident]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq41 ; # (x (/ (t _) + # (t :) + # (alpha)) + # (* (/ (t _) + # (t :) + # (alnum)))) + + isv_nonterminal_range Ident $pos + inc_save Ident $pos + if {$ok} ias_push + ier_nonterminal "Expected Ident" $pos + return +} + +proc ::page::parse::peg::eseq41 {} { + + # (x (/ (t _) + # (t :) + # (alpha)) + # (* (/ (t _) + # (t :) + # (alnum)))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebra38 ; # (/ (t _) + # (t :) + # (alpha)) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ekleene40 ; # (* (/ (t _) + # (t :) + # (alnum))) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::ebra38 {} { + + # (/ (t _) + # (t :) + # (alpha)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected _ (got EOF)" + if {$ok} {ict_match_token _ "Expected _"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected : (got EOF)" + if {$ok} {ict_match_token : "Expected :"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected <alpha> (got EOF)" + if {$ok} {ict_match_tokclass alpha "Expected <alpha>"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::ekleene40 {} { + + # (* (/ (t _) + # (t :) + # (alnum))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + ebra39 ; # (/ (t _) + # (t :) + # (alnum)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::ebra39 {} { + + # (/ (t _) + # (t :) + # (alnum)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected _ (got EOF)" + if {$ok} {ict_match_token _ "Expected _"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected : (got EOF)" + if {$ok} {ict_match_token : "Expected :"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected <alnum> (got EOF)" + if {$ok} {ict_match_tokclass alnum "Expected <alnum>"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_Identifier {} { + # Identifier = (x (n Ident) + # (n SPACE)) + + variable ok + if {[inc_restore Identifier]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq37 ; # (x (n Ident) + # (n SPACE)) + + isv_nonterminal_reduce Identifier $pos $mrk + inc_save Identifier $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Identifier" $pos + return +} + +proc ::page::parse::peg::eseq37 {} { + + # (x (n Ident) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Ident ; # (n Ident) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_IS {} { + # IS = (x (t <) + # (t -) + # (n SPACE)) + + if {[inc_restore IS]} return + + set pos [icl_get] + + eseq58 ; # (x (t <) + # (t -) + # (n SPACE)) + + isv_clear + inc_save IS $pos + ier_nonterminal "Expected IS" $pos + return +} + +proc ::page::parse::peg::eseq58 {} { + + # (x (t <) + # (t -) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected < (got EOF)" + if {$ok} {ict_match_token < "Expected <"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected - (got EOF)" + if {$ok} {ict_match_token - "Expected -"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_LEAF {} { + # LEAF = (x (t l) + # (t e) + # (t a) + # (t f) + # (n SPACE)) + + variable ok + if {[inc_restore LEAF]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq60 ; # (x (t l) + # (t e) + # (t a) + # (t f) + # (n SPACE)) + + isv_nonterminal_leaf LEAF $pos + inc_save LEAF $pos + if {$ok} ias_push + ier_nonterminal "Expected LEAF" $pos + return +} + +proc ::page::parse::peg::eseq60 {} { + + # (x (t l) + # (t e) + # (t a) + # (t f) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected l (got EOF)" + if {$ok} {ict_match_token l "Expected l"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected e (got EOF)" + if {$ok} {ict_match_token e "Expected e"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected a (got EOF)" + if {$ok} {ict_match_token a "Expected a"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected f (got EOF)" + if {$ok} {ict_match_token f "Expected f"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Literal {} { + # Literal = (/ (x (n APOSTROPH) + # (* (x (! (n APOSTROPH)) + # (n Char))) + # (n APOSTROPH) + # (n SPACE)) + # (x (n DAPOSTROPH) + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + # (n DAPOSTROPH) + # (n SPACE))) + + variable ok + if {[inc_restore Literal]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + ebra28 ; # (/ (x (n APOSTROPH) + # (* (x (! (n APOSTROPH)) + # (n Char))) + # (n APOSTROPH) + # (n SPACE)) + # (x (n DAPOSTROPH) + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + # (n DAPOSTROPH) + # (n SPACE))) + + isv_nonterminal_reduce Literal $pos $mrk + inc_save Literal $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Literal" $pos + return +} + +proc ::page::parse::peg::ebra28 {} { + + # (/ (x (n APOSTROPH) + # (* (x (! (n APOSTROPH)) + # (n Char))) + # (n APOSTROPH) + # (n SPACE)) + # (x (n DAPOSTROPH) + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + # (n DAPOSTROPH) + # (n SPACE))) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + eseq23 ; # (x (n APOSTROPH) + # (* (x (! (n APOSTROPH)) + # (n Char))) + # (n APOSTROPH) + # (n SPACE)) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + eseq27 ; # (x (n DAPOSTROPH) + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + # (n DAPOSTROPH) + # (n SPACE)) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::eseq23 {} { + + # (x (n APOSTROPH) + # (* (x (! (n APOSTROPH)) + # (n Char))) + # (n APOSTROPH) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_APOSTROPH ; # (n APOSTROPH) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + ekleene22 ; # (* (x (! (n APOSTROPH)) + # (n Char))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_APOSTROPH ; # (n APOSTROPH) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ekleene22 {} { + + # (* (x (! (n APOSTROPH)) + # (n Char))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq21 ; # (x (! (n APOSTROPH)) + # (n Char)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq21 {} { + + # (x (! (n APOSTROPH)) + # (n Char)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang20 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Char ; # (n Char) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ebang20 {} { + set pos [icl_get] + + matchSymbol_APOSTROPH ; # (n APOSTROPH) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::eseq27 {} { + + # (x (n DAPOSTROPH) + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + # (n DAPOSTROPH) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_DAPOSTROPH ; # (n DAPOSTROPH) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + ekleene26 ; # (* (x (! (n DAPOSTROPH)) + # (n Char))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_DAPOSTROPH ; # (n DAPOSTROPH) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ekleene26 {} { + + # (* (x (! (n DAPOSTROPH)) + # (n Char))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + eseq25 ; # (x (! (n DAPOSTROPH)) + # (n Char)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::eseq25 {} { + + # (x (! (n DAPOSTROPH)) + # (n Char)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebang24 + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Char ; # (n Char) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::ebang24 {} { + set pos [icl_get] + + matchSymbol_DAPOSTROPH ; # (n DAPOSTROPH) + + icl_rewind $pos + iok_negate + return +} + +proc ::page::parse::peg::matchSymbol_MATCH {} { + # MATCH = (x (t m) + # (t a) + # (t t) + # (t c) + # (t h) + # (n SPACE)) + + variable ok + if {[inc_restore MATCH]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq61 ; # (x (t m) + # (t a) + # (t t) + # (t c) + # (t h) + # (n SPACE)) + + isv_nonterminal_leaf MATCH $pos + inc_save MATCH $pos + if {$ok} ias_push + ier_nonterminal "Expected MATCH" $pos + return +} + +proc ::page::parse::peg::eseq61 {} { + + # (x (t m) + # (t a) + # (t t) + # (t c) + # (t h) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected m (got EOF)" + if {$ok} {ict_match_token m "Expected m"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected a (got EOF)" + if {$ok} {ict_match_token a "Expected a"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected t (got EOF)" + if {$ok} {ict_match_token t "Expected t"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected c (got EOF)" + if {$ok} {ict_match_token c "Expected c"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected h (got EOF)" + if {$ok} {ict_match_token h "Expected h"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_NOT {} { + # NOT = (x (t !) + # (n SPACE)) + + variable ok + if {[inc_restore NOT]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq67 ; # (x (t !) + # (n SPACE)) + + isv_nonterminal_leaf NOT $pos + inc_save NOT $pos + if {$ok} ias_push + ier_nonterminal "Expected NOT" $pos + return +} + +proc ::page::parse::peg::eseq67 {} { + + # (x (t !) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected ! (got EOF)" + if {$ok} {ict_match_token ! "Expected !"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_OPEN {} { + # OPEN = (x (t \() + # (n SPACE)) + + if {[inc_restore OPEN]} return + + set pos [icl_get] + + eseq71 ; # (x (t \() + # (n SPACE)) + + isv_clear + inc_save OPEN $pos + ier_nonterminal "Expected OPEN" $pos + return +} + +proc ::page::parse::peg::eseq71 {} { + + # (x (t \() + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \( (got EOF)" + if {$ok} {ict_match_token \50 "Expected \("} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_OPENB {} { + # OPENB = (t [) + + variable ok + if {[inc_restore OPENB]} return + + set pos [icl_get] + + ict_advance "Expected \[ (got EOF)" + if {$ok} {ict_match_token \133 "Expected \["} + + isv_clear + inc_save OPENB $pos + ier_nonterminal "Expected OPENB" $pos + return +} + +proc ::page::parse::peg::matchSymbol_PEG {} { + # PEG = (x (t P) + # (t E) + # (t G) + # (n SPACE)) + + if {[inc_restore PEG]} return + + set pos [icl_get] + + eseq57 ; # (x (t P) + # (t E) + # (t G) + # (n SPACE)) + + isv_clear + inc_save PEG $pos + ier_nonterminal "Expected PEG" $pos + return +} + +proc ::page::parse::peg::eseq57 {} { + + # (x (t P) + # (t E) + # (t G) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected P (got EOF)" + if {$ok} {ict_match_token P "Expected P"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected E (got EOF)" + if {$ok} {ict_match_token E "Expected E"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected G (got EOF)" + if {$ok} {ict_match_token G "Expected G"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_PLUS {} { + # PLUS = (x (t +) + # (n SPACE)) + + variable ok + if {[inc_restore PLUS]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq70 ; # (x (t +) + # (n SPACE)) + + isv_nonterminal_leaf PLUS $pos + inc_save PLUS $pos + if {$ok} ias_push + ier_nonterminal "Expected PLUS" $pos + return +} + +proc ::page::parse::peg::eseq70 {} { + + # (x (t +) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected + (got EOF)" + if {$ok} {ict_match_token + "Expected +"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Prefix {} { + # Prefix = (x (? (/ (n AND) + # (n NOT))) + # (n Suffix)) + + variable ok + if {[inc_restore Prefix]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq14 ; # (x (? (/ (n AND) + # (n NOT))) + # (n Suffix)) + + isv_nonterminal_reduce Prefix $pos $mrk + inc_save Prefix $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Prefix" $pos + return +} + +proc ::page::parse::peg::eseq14 {} { + + # (x (? (/ (n AND) + # (n NOT))) + # (n Suffix)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + eopt13 ; # (? (/ (n AND) + # (n NOT))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Suffix ; # (n Suffix) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::eopt13 {} { + + # (? (/ (n AND) + # (n NOT))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebra12 ; # (/ (n AND) + # (n NOT)) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::ebra12 {} { + + # (/ (n AND) + # (n NOT)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_AND ; # (n AND) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_NOT ; # (n NOT) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_Primary {} { + # Primary = (/ (n ALNUM) + # (n ALPHA) + # (n Identifier) + # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + # (n Literal) + # (n Class) + # (n DOT)) + + variable ok + if {[inc_restore Primary]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + ebra19 ; # (/ (n ALNUM) + # (n ALPHA) + # (n Identifier) + # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + # (n Literal) + # (n Class) + # (n DOT)) + + isv_nonterminal_reduce Primary $pos $mrk + inc_save Primary $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Primary" $pos + return +} + +proc ::page::parse::peg::ebra19 {} { + + # (/ (n ALNUM) + # (n ALPHA) + # (n Identifier) + # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + # (n Literal) + # (n Class) + # (n DOT)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_ALNUM ; # (n ALNUM) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_ALPHA ; # (n ALPHA) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Identifier ; # (n Identifier) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + eseq18 ; # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Literal ; # (n Literal) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Class ; # (n Class) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_DOT ; # (n DOT) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::eseq18 {} { + + # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_OPEN ; # (n OPEN) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Expression ; # (n Expression) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_CLOSE ; # (n CLOSE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_QUESTION {} { + # QUESTION = (x (t ?) + # (n SPACE)) + + variable ok + if {[inc_restore QUESTION]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq68 ; # (x (t ?) + # (n SPACE)) + + isv_nonterminal_leaf QUESTION $pos + inc_save QUESTION $pos + if {$ok} ias_push + ier_nonterminal "Expected QUESTION" $pos + return +} + +proc ::page::parse::peg::eseq68 {} { + + # (x (t ?) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected ? (got EOF)" + if {$ok} {ict_match_token ? "Expected ?"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Range {} { + # Range = (/ (x (n Char) + # (n TO) + # (n Char)) + # (n Char)) + + variable ok + if {[inc_restore Range]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + ebra34 ; # (/ (x (n Char) + # (n TO) + # (n Char)) + # (n Char)) + + isv_nonterminal_reduce Range $pos $mrk + inc_save Range $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Range" $pos + return +} + +proc ::page::parse::peg::ebra34 {} { + + # (/ (x (n Char) + # (n TO) + # (n Char)) + # (n Char)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + eseq33 ; # (x (n Char) + # (n TO) + # (n Char)) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_Char ; # (n Char) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::eseq33 {} { + + # (x (n Char) + # (n TO) + # (n Char)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Char ; # (n Char) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_TO ; # (n TO) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_Char ; # (n Char) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_SEMICOLON {} { + # SEMICOLON = (x (t ;) + # (n SPACE)) + + if {[inc_restore SEMICOLON]} return + + set pos [icl_get] + + eseq63 ; # (x (t ;) + # (n SPACE)) + + isv_clear + inc_save SEMICOLON $pos + ier_nonterminal "Expected SEMICOLON" $pos + return +} + +proc ::page::parse::peg::eseq63 {} { + + # (x (t ;) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected \; (got EOF)" + if {$ok} {ict_match_token \73 "Expected \;"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_Sequence {} { + # Sequence = (+ (n Prefix)) + + variable ok + if {[inc_restore Sequence]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + epkleene11 ; # (+ (n Prefix)) + + isv_nonterminal_reduce Sequence $pos $mrk + inc_save Sequence $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Sequence" $pos + return +} + +proc ::page::parse::peg::epkleene11 {} { + + # (+ (n Prefix)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_Prefix ; # (n Prefix) + ier_merge $old + + if {!$ok} { + icl_rewind $pos + return + } + + while {1} { + set pos [icl_get] + + set old [ier_get] + matchSymbol_Prefix ; # (n Prefix) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::matchSymbol_SLASH {} { + # SLASH = (x (t /) + # (n SPACE)) + + if {[inc_restore SLASH]} return + + set pos [icl_get] + + eseq65 ; # (x (t /) + # (n SPACE)) + + isv_clear + inc_save SLASH $pos + ier_nonterminal "Expected SLASH" $pos + return +} + +proc ::page::parse::peg::eseq65 {} { + + # (x (t /) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected / (got EOF)" + if {$ok} {ict_match_token / "Expected /"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_SPACE {} { + # SPACE = (* (/ (t <blank>) + # (t \t) + # (n EOL) + # (n COMMENT))) + + if {[inc_restore SPACE]} return + + set pos [icl_get] + + ekleene77 ; # (* (/ (t <blank>) + # (t \t) + # (n EOL) + # (n COMMENT))) + + isv_clear + inc_save SPACE $pos + ier_nonterminal "Expected SPACE" $pos + return +} + +proc ::page::parse::peg::ekleene77 {} { + + # (* (/ (t <blank>) + # (t \t) + # (n EOL) + # (n COMMENT))) + + variable ok + + while {1} { + set pos [icl_get] + + set old [ier_get] + ebra76 ; # (/ (t <blank>) + # (t \t) + # (n EOL) + # (n COMMENT)) + ier_merge $old + + if {$ok} continue + break + } + + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::ebra76 {} { + + # (/ (t <blank>) + # (t \t) + # (n EOL) + # (n COMMENT)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected <blank> (got EOF)" + if {$ok} {ict_match_token \40 "Expected <blank>"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + ict_advance "Expected \\t (got EOF)" + if {$ok} {ict_match_token \t "Expected \\t"} + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_EOL ; # (n EOL) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + set old [ier_get] + matchSymbol_COMMENT ; # (n COMMENT) + ier_merge $old + + if {$ok} return + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_STAR {} { + # STAR = (x (t *) + # (n SPACE)) + + variable ok + if {[inc_restore STAR]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq69 ; # (x (t *) + # (n SPACE)) + + isv_nonterminal_leaf STAR $pos + inc_save STAR $pos + if {$ok} ias_push + ier_nonterminal "Expected STAR" $pos + return +} + +proc ::page::parse::peg::eseq69 {} { + + # (x (t *) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected * (got EOF)" + if {$ok} {ict_match_token * "Expected *"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +proc ::page::parse::peg::matchSymbol_StartExpr {} { + # StartExpr = (x (n OPEN) + # (n Expression) + # (n CLOSE)) + + variable ok + if {[inc_restore StartExpr]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq35 ; # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + + isv_nonterminal_reduce StartExpr $pos $mrk + inc_save StartExpr $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected StartExpr" $pos + return +} + +proc ::page::parse::peg::eseq35 {} { + + # (x (n OPEN) + # (n Expression) + # (n CLOSE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + matchSymbol_OPEN ; # (n OPEN) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Expression ; # (n Expression) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + matchSymbol_CLOSE ; # (n CLOSE) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::matchSymbol_Suffix {} { + # Suffix = (x (n Primary) + # (? (/ (n QUESTION) + # (n STAR) + # (n PLUS)))) + + variable ok + if {[inc_restore Suffix]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + set mrk [ias_mark] + + eseq17 ; # (x (n Primary) + # (? (/ (n QUESTION) + # (n STAR) + # (n PLUS)))) + + isv_nonterminal_reduce Suffix $pos $mrk + inc_save Suffix $pos + ias_pop2mark $mrk + if {$ok} ias_push + ier_nonterminal "Expected Suffix" $pos + return +} + +proc ::page::parse::peg::eseq17 {} { + + # (x (n Primary) + # (? (/ (n QUESTION) + # (n STAR) + # (n PLUS)))) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + + set old [ier_get] + matchSymbol_Primary ; # (n Primary) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + set old [ier_get] + eopt16 ; # (? (/ (n QUESTION) + # (n STAR) + # (n PLUS))) + ier_merge $old + + if {!$ok} { + ias_pop2mark $mrk + icl_rewind $pos + return + } + + return +} + +proc ::page::parse::peg::eopt16 {} { + + # (? (/ (n QUESTION) + # (n STAR) + # (n PLUS))) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ebra15 ; # (/ (n QUESTION) + # (n STAR) + # (n PLUS)) + ier_merge $old + + if {$ok} return + icl_rewind $pos + iok_ok + return +} + +proc ::page::parse::peg::ebra15 {} { + + # (/ (n QUESTION) + # (n STAR) + # (n PLUS)) + + variable ok + + set pos [icl_get] + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_QUESTION ; # (n QUESTION) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_STAR ; # (n STAR) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + set mrk [ias_mark] + set old [ier_get] + matchSymbol_PLUS ; # (n PLUS) + ier_merge $old + + if {$ok} return + ias_pop2mark $mrk + icl_rewind $pos + + return +} + +proc ::page::parse::peg::matchSymbol_TO {} { + # TO = (t -) + + variable ok + if {[inc_restore TO]} return + + set pos [icl_get] + + ict_advance "Expected - (got EOF)" + if {$ok} {ict_match_token - "Expected -"} + + isv_clear + inc_save TO $pos + ier_nonterminal "Expected TO" $pos + return +} + +proc ::page::parse::peg::matchSymbol_VOID {} { + # VOID = (x (t v) + # (t o) + # (t i) + # (t d) + # (n SPACE)) + + variable ok + if {[inc_restore VOID]} { + if {$ok} ias_push + return + } + + set pos [icl_get] + + eseq59 ; # (x (t v) + # (t o) + # (t i) + # (t d) + # (n SPACE)) + + isv_nonterminal_leaf VOID $pos + inc_save VOID $pos + if {$ok} ias_push + ier_nonterminal "Expected VOID" $pos + return +} + +proc ::page::parse::peg::eseq59 {} { + + # (x (t v) + # (t o) + # (t i) + # (t d) + # (n SPACE)) + + variable ok + + set pos [icl_get] + + set old [ier_get] + ict_advance "Expected v (got EOF)" + if {$ok} {ict_match_token v "Expected v"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected o (got EOF)" + if {$ok} {ict_match_token o "Expected o"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected i (got EOF)" + if {$ok} {ict_match_token i "Expected i"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + ict_advance "Expected d (got EOF)" + if {$ok} {ict_match_token d "Expected d"} + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + set old [ier_get] + matchSymbol_SPACE ; # (n SPACE) + ier_merge $old + + if {!$ok} {icl_rewind $pos ; return} + + return +} + +# ### ### ### ######### ######### ######### +## Package Management + +package provide page::parse::peg 0.1 diff --git a/tcllib/modules/page/parse_peghb.tcl b/tcllib/modules/page/parse_peghb.tcl new file mode 100644 index 0000000..ba9fab4 --- /dev/null +++ b/tcllib/modules/page/parse_peghb.tcl @@ -0,0 +1,118 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Frontend - Read halfbaked PEG container. + +# ### ### ### ######### ######### ######### +## Requisites + +namespace eval ::page::parse::peghb { + variable fixup {} + variable definitions +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::parse::peghb {halfbaked t} { + variable peghb::fixup + variable peghb::definitions + array set definitions {} + + set fixup {} + + interp create -safe sb + # Should remove everything. + interp alias sb Start {} ::page::parse::peghb::Start $t + interp alias sb Define {} ::page::parse::peghb::Define $t + interp eval sb $halfbaked + interp delete sb + + array set undefined {} + array set users {} + foreach {n sym} $fixup { + if {[info exists definitions($sym)]} { + set def $definitions($sym) + $t set $n def $def + lappend users($def) $n + } else { + lappend undefined($sym) $n + } + } + + foreach def [array names users] { + $t set $def users $users($def) + } + + $t set root definitions [array get definitions] + $t set root undefined [array get undefined] + $t set root symbol <StartExpression> + $t set root name <HalfBaked> + + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::parse::peghb::Start {t pe} { + variable fixup + $t set root start [treeOf $t root $pe fixup] + return +} + +proc ::page::parse::peghb::Define {t mode sym pe} { + variable fixup + variable definitions + + set def [$t insert root end] + + $t set $def users {} + $t set $def symbol $sym + $t set $def label $sym + $t set $def mode $mode + + treeOf $t $def $pe fixup + + set definitions($sym) $def + return +} + +proc ::page::parse::peghb::treeOf {t root pe fv} { + upvar 1 $fv fixup + + set n [$t insert $root end] + set op [lindex $pe 0] + $t set $n op $op + + if {$op eq "t"} { + $t set $n char [lindex $pe 1] + + } elseif {$op eq ".."} { + $t set $n begin [lindex $pe 1] + $t set $n end [lindex $pe 2] + + } elseif {$op eq "n"} { + + set sym [lindex $pe 1] + $t set $n sym $sym + $t set $n def "" + + lappend fixup $n $sym + } else { + foreach sub [lrange $pe 1 end] { + treeOf $t $n $sub fixup + } + } + return $n +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::parse::peghb {} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::parse::peghb 0.1 diff --git a/tcllib/modules/page/parse_pegser.tcl b/tcllib/modules/page/parse_pegser.tcl new file mode 100644 index 0000000..b3814a7 --- /dev/null +++ b/tcllib/modules/page/parse_pegser.tcl @@ -0,0 +1,99 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Frontend - Read serialized PEG container. + +# ### ### ### ######### ######### ######### +## Requisites + +package require grammar::peg + +namespace eval ::page::parse::pegser {} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::parse::pegser {serial t} { + + ::grammar::peg gr deserialize $serial + + $t set root start [pegser::treeOf $t root [gr start] fixup] + + array set definitions {} + foreach sym [gr nonterminals] { + set def [$t insert root end] + + $t set $def users {} + $t set $def symbol $sym + $t set $def label $sym + $t set $def mode [gr nonterminal mode $sym] + pegser::treeOf $t $def [gr nonterminal rule $sym] fixup + + set definitions($sym) $def + } + + array set undefined {} + array set users {} + foreach {n sym} $fixup { + if {[info exists definitions($sym)]} { + set def $definitions($sym) + $t set $n def $def + lappend users($def) $n + } else { + lappend undefined($sym) $n + } + } + + foreach def [array names users] { + $t set $def users $users($def) + } + + $t set root definitions [array get definitions] + $t set root undefined [array get undefined] + $t set root symbol <StartExpression> + $t set root name <Serialization> + + return +} + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::parse::pegser::treeOf {t root pe fv} { + upvar 1 $fv fixup + + set n [$t insert $root end] + set op [lindex $pe 0] + $t set $n op $op + + if {$op eq "t"} { + $t set $n char [lindex $pe 1] + + } elseif {$op eq ".."} { + $t set $n begin [lindex $pe 1] + $t set $n end [lindex $pe 2] + + } elseif {$op eq "n"} { + + set sym [lindex $pe 1] + $t set $n sym $sym + $t set $n def "" + + lappend fixup $n $sym + } else { + foreach sub [lrange $pe 1 end] { + treeOf $t $n $sub fixup + } + } + return $n +} + +# ### ### ### ######### ######### ######### +## Internal. Strings. + +namespace eval ::page::parse::pegser {} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::parse::pegser 0.1 diff --git a/tcllib/modules/page/peg_grammar.peg b/tcllib/modules/page/peg_grammar.peg new file mode 100644 index 0000000..8ae4068 --- /dev/null +++ b/tcllib/modules/page/peg_grammar.peg @@ -0,0 +1,86 @@ +# -*- text -*- +# +# Parsing Expression Grammar declaring a syntax for Parsing Expression +# Grammars, to use in a PEG-based parser generator. This specification +# is self-referential, it uses the grammar described by to describe +# said grammar. + +PEG pg::peg::grammar (Grammar) + + # -------------------------------------------------------------------- + # Syntactical constructs + + Grammar <- SPACE Header Definition+ Final EOF ; + + Header <- PEG Identifier StartExpr ; + Definition <- Attribute? Identifier IS Expression SEMICOLON ; + Attribute <- (VOID / LEAF / MATCH) COLON ; + Expression <- Sequence (SLASH Sequence)* ; + Sequence <- Prefix+ ; + Prefix <- (AND / NOT)? Suffix ; + Suffix <- Primary (QUESTION / STAR / PLUS)? ; + Primary <- ALNUM / ALPHA / Identifier + / OPEN Expression CLOSE + / Literal + / Class + / DOT + ; + Literal <- APOSTROPH (!APOSTROPH Char)* APOSTROPH SPACE + / DAPOSTROPH (!DAPOSTROPH Char)* DAPOSTROPH SPACE ; + Class <- OPENB (!CLOSEB Range)* CLOSEB SPACE ; + Range <- Char TO Char / Char ; + + StartExpr <- OPEN Expression CLOSE ; +void: Final <- END SEMICOLON SPACE ; + + # -------------------------------------------------------------------- + # Lexing constructs + + Identifier <- Ident SPACE ; +match: Ident <- ('_' / ':' / <alpha>) ('_' / ':' / <alnum>)* ; + Char <- CharSpecial / CharOctalFull / CharOctalPart + / CharUnicode / CharUnescaped + ; + +match: CharSpecial <- "\\" [nrt'"\[\]\\] ; +match: CharOctalFull <- "\\" [0-2][0-7][0-7] ; +match: CharOctalPart <- "\\" [0-7][0-7]? ; +match: CharUnicode <- "\\" 'u' HexDigit (HexDigit (HexDigit HexDigit?)?)? ; +match: CharUnescaped <- !"\\" . ; + +void: HexDigit <- [0-9a-fA-F] ; + +void: TO <- '-' ; +void: OPENB <- "[" ; +void: CLOSEB <- "]" ; +void: APOSTROPH <- "'" ; +void: DAPOSTROPH <- '"' ; +void: PEG <- "PEG" SPACE ; +void: IS <- "<-" SPACE ; +leaf: VOID <- "void" SPACE ; # Implies that definition has no semantic value. +leaf: LEAF <- "leaf" SPACE ; # Implies that definition has no terminals. +leaf: MATCH <- "match" SPACE ; # Implies that semantic value is the matched string, + # not the parse tree from the symbol. +void: END <- "END" SPACE ; +void: SEMICOLON <- ";" SPACE ; +void: COLON <- ":" SPACE ; +void: SLASH <- "/" SPACE ; +leaf: AND <- "&" SPACE ; +leaf: NOT <- "!" SPACE ; +leaf: QUESTION <- "?" SPACE ; +leaf: STAR <- "*" SPACE ; +leaf: PLUS <- "+" SPACE ; +void: OPEN <- "(" SPACE ; +void: CLOSE <- ")" SPACE ; +leaf: DOT <- "." SPACE ; +leaf: ALPHA <- "<alpha>" SPACE ; +leaf: ALNUM <- "<alnum>" SPACE ; + +void: SPACE <- (" " / "\t" / EOL / COMMENT)* ; +void: COMMENT <- '#' (!EOL .)* EOL ; +void: EOL <- "\n\r" / "\n" / "\r" ; +void: EOF <- !. ; + + # -------------------------------------------------------------------- +END; + diff --git a/tcllib/modules/page/peg_grammar.tcl b/tcllib/modules/page/peg_grammar.tcl new file mode 100644 index 0000000..b585125 --- /dev/null +++ b/tcllib/modules/page/peg_grammar.tcl @@ -0,0 +1,117 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Grammar for reading of PE grammars + +## Parsing Expression Grammar 'pg::peg::grammar'. + +# ### ### ### ######### ######### ######### +## Package description + +## It provides a single command returning the handle of a +## grammar container in which the grammar 'pg::peg::grammar' +## is stored. The container is usable by a PEG interpreter +## or other packages taking PE grammars. + +# ### ### ### ######### ######### ######### +## Requisites. +## - PEG container type + +package require grammar::peg + +namespace eval ::pg::peg::grammar {} + +# ### ### ### ######### ######### ######### +## API + +proc ::pg::peg::grammar {} { + return $grammar::gr +} + +# ### ### ### ######### ######### ######### +# ### ### ### ######### ######### ######### +## Data and helpers. + +namespace eval ::pg::peg::grammar { + # Grammar container + variable gr [::grammar::peg gr] +} + +proc ::pg::peg::grammar::Start {pe} { + variable gr + $gr start $pe + return +} + +proc ::pg::peg::grammar::Define {mode nt pe} { + variable gr + $gr nonterminal add $nt $pe + $gr nonterminal mode $nt $mode + return +} + +# ### ### ### ######### ######### ######### +## Initialization = Grammar definition + +namespace eval ::pg::peg::grammar { + Start {n Grammar} + + Define leaf ALNUM {x {t <} {t a} {t l} {t n} {t u} {t m} {t >} {n SPACE}} + Define leaf ALPHA {x {t <} {t a} {t l} {t p} {t h} {t a} {t >} {n SPACE}} + Define leaf AND {x {t &} {n SPACE}} + Define discard APOSTROPH {t '} + Define value Attribute {x {/ {n VOID} {n LEAF} {n MATCH}} {n COLON}} + Define value Char {/ {n CharSpecial} {n CharOctalFull} {n CharOctalPart} {n CharUnicode} {n CharUnescaped}} + Define match CharOctalFull {x {t \134} {.. 0 2} {.. 0 7} {.. 0 7}} + Define match CharOctalPart {x {t \134} {.. 0 7} {? {.. 0 7}}} + Define match CharSpecial {x {t \134} {/ {t n} {t r} {t t} {t '} {t \42} {t \133} {t \135} {t \134}}} + Define match CharUnescaped {x {! {t \134}} dot} + Define match CharUnicode {x {t \134} {t u} {n HexDigit} {? {x {n HexDigit} {? {x {n HexDigit} {? {n HexDigit}}}}}}} + Define value Class {x {n OPENB} {* {x {! {n CLOSEB}} {n Range}}} {n CLOSEB} {n SPACE}} + Define discard CLOSE {x {t \51} {n SPACE}} + Define discard CLOSEB {t \135} + Define discard COLON {x {t :} {n SPACE}} + Define discard COMMENT {x {t #} {* {x {! {n EOL}} dot}} {n EOL}} + Define discard DAPOSTROPH {t \42} + Define value Definition {x {? {n Attribute}} {n Identifier} {n IS} {n Expression} {n SEMICOLON}} + Define leaf DOT {x {t .} {n SPACE}} + Define discard END {x {t E} {t N} {t D} {n SPACE}} + Define discard EOF {! dot} + Define discard EOL {/ {x {t \n} {t \r}} {t \n} {t \r}} + Define value Expression {x {n Sequence} {* {x {n SLASH} {n Sequence}}}} + Define discard Final {x {n END} {n SEMICOLON} {n SPACE}} + Define value Grammar {x {n SPACE} {n Header} {+ {n Definition}} {n Final} {n EOF}} + Define value Header {x {n PEG} {n Identifier} {n StartExpr}} + Define discard HexDigit {/ {.. 0 9} {.. a f} {.. A F}} + Define match Ident {x {/ {t _} {t :} alpha} {* {/ {t _} {t :} alnum}}} + Define value Identifier {x {n Ident} {n SPACE}} + Define discard IS {x {t <} {t -} {n SPACE}} + Define leaf LEAF {x {t l} {t e} {t a} {t f} {n SPACE}} + Define value Literal {/ {x {n APOSTROPH} {* {x {! {n APOSTROPH}} {n Char}}} {n APOSTROPH} {n SPACE}} {x {n DAPOSTROPH} {* {x {! {n DAPOSTROPH}} {n Char}}} {n DAPOSTROPH} {n SPACE}}} + Define leaf MATCH {x {t m} {t a} {t t} {t c} {t h} {n SPACE}} + Define leaf NOT {x {t !} {n SPACE}} + Define discard OPEN {x {t \50} {n SPACE}} + Define discard OPENB {t \133} + Define discard PEG {x {t P} {t E} {t G} {n SPACE}} + Define leaf PLUS {x {t +} {n SPACE}} + Define value Prefix {x {? {/ {n AND} {n NOT}}} {n Suffix}} + Define value Primary {/ {n ALNUM} {n ALPHA} {n Identifier} {x {n OPEN} {n Expression} {n CLOSE}} {n Literal} {n Class} {n DOT}} + Define leaf QUESTION {x {t ?} {n SPACE}} + Define value Range {/ {x {n Char} {n TO} {n Char}} {n Char}} + Define discard SEMICOLON {x {t \73} {n SPACE}} + Define value Sequence {+ {n Prefix}} + Define discard SLASH {x {t /} {n SPACE}} + Define discard SPACE {* {/ {t \40} {t \t} {n EOL} {n COMMENT}}} + Define leaf STAR {x {t *} {n SPACE}} + Define value StartExpr {x {n OPEN} {n Expression} {n CLOSE}} + Define value Suffix {x {n Primary} {? {/ {n QUESTION} {n STAR} {n PLUS}}}} + Define discard TO {t -} + Define leaf VOID {x {t v} {t o} {t i} {t d} {n SPACE}} +} + +# ### ### ### ######### ######### ######### +## Package Management - Ready + +# @sak notprovided pg::peg::grammar +package provide pg::peg::grammar 0.1 + diff --git a/tcllib/modules/page/pkgIndex.tcl b/tcllib/modules/page/pkgIndex.tcl new file mode 100644 index 0000000..71cb403 --- /dev/null +++ b/tcllib/modules/page/pkgIndex.tcl @@ -0,0 +1,80 @@ +# -- PAGE application packages -- +# -- ---- plugin management + +package ifneeded page::pluginmgr 0.2 [list source [file join $dir pluginmgr.tcl]] + +# -- PAGE plugin packages +# -- ---- Canned configurations + +package ifneeded page::config::peg 0.1 [list source [file join $dir plugins/config_peg.tcl]] + +# -- PAGE plugin packages +# -- ---- Readers + +package ifneeded page::reader::peg 0.1 [list source [file join $dir plugins/reader_peg.tcl]] +package ifneeded page::reader::lemon 0.1 [list source [file join $dir plugins/reader_lemon.tcl]] +package ifneeded page::reader::hb 0.1 [list source [file join $dir plugins/reader_hb.tcl]] +package ifneeded page::reader::ser 0.1 [list source [file join $dir plugins/reader_ser.tcl]] +package ifneeded page::reader::treeser 0.1 [list source [file join $dir plugins/reader_treeser.tcl]] + +# -- PAGE plugin packages +# -- ---- Writers + +package ifneeded page::writer::null 0.1 [list source [file join $dir plugins/writer_null.tcl]] +package ifneeded page::writer::me 0.1 [list source [file join $dir plugins/writer_me.tcl]] +package ifneeded page::writer::mecpu 0.1.1 [list source [file join $dir plugins/writer_mecpu.tcl]] +package ifneeded page::writer::tree 0.1 [list source [file join $dir plugins/writer_tree.tcl]] +package ifneeded page::writer::tpc 0.1 [list source [file join $dir plugins/writer_tpc.tcl]] +package ifneeded page::writer::hb 0.1 [list source [file join $dir plugins/writer_hb.tcl]] +package ifneeded page::writer::ser 0.1 [list source [file join $dir plugins/writer_ser.tcl]] +package ifneeded page::writer::peg 0.1 [list source [file join $dir plugins/writer_peg.tcl]] +package ifneeded page::writer::identity 0.1 [list source [file join $dir plugins/writer_identity.tcl]] + +# -- PAGE plugin packages +# -- ---- Transformations + +package ifneeded page::transform::reachable 0.1 \ + [list source [file join $dir plugins/transform_reachable.tcl]] +package ifneeded page::transform::realizable 0.1 \ + [list source [file join $dir plugins/transform_realizable.tcl]] +package ifneeded page::transform::mecpu 0.1 \ + [list source [file join $dir plugins/transform_mecpu.tcl]] + +# -- PAGE packages -- +# -- --- Parsing and normalization packages used by the reader plugins. + +package ifneeded page::parse::peg 0.1 [list source [file join $dir parse_peg.tcl]] +package ifneeded page::parse::lemon 0.1 [list source [file join $dir parse_lemon.tcl]] +package ifneeded page::parse::pegser 0.1 [list source [file join $dir parse_pegser.tcl]] +package ifneeded page::parse::peghb 0.1 [list source [file join $dir parse_peghb.tcl]] + +package ifneeded page::util::norm::peg 0.1 [list source [file join $dir util_norm_peg.tcl]] +package ifneeded page::util::norm::lemon 0.1 [list source [file join $dir util_norm_lemon.tcl]] + +# @mdgen EXCLUDE: peg_grammar.tcl +### package ifneeded pg::peg::grammar 0.1 [list source [file join $dir peg_grammar.tcl]] + +# -- PAGE packages -- +# -- --- Code generation packages used by the writer plugins. + +package ifneeded page::gen::tree::text 0.1 [list source [file join $dir gen_tree_text.tcl]] +package ifneeded page::gen::peg::cpkg 0.1 [list source [file join $dir gen_peg_cpkg.tcl]] +package ifneeded page::gen::peg::hb 0.1 [list source [file join $dir gen_peg_hb.tcl]] +package ifneeded page::gen::peg::ser 0.1 [list source [file join $dir gen_peg_ser.tcl]] +package ifneeded page::gen::peg::canon 0.1 [list source [file join $dir gen_peg_canon.tcl]] +package ifneeded page::gen::peg::me 0.1 [list source [file join $dir gen_peg_me.tcl]] +package ifneeded page::gen::peg::mecpu 0.1 [list source [file join $dir gen_peg_mecpu.tcl]] + +# -- Transformation Helper Packages -- + +package ifneeded page::analysis::peg::minimize 0.1 [list source [file join $dir analysis_peg_minimize.tcl]] +package ifneeded page::analysis::peg::reachable 0.1 [list source [file join $dir analysis_peg_reachable.tcl]] +package ifneeded page::analysis::peg::realizable 0.1 [list source [file join $dir analysis_peg_realizable.tcl]] +package ifneeded page::analysis::peg::emodes 0.1 [list source [file join $dir analysis_peg_emodes.tcl]] +package ifneeded page::compiler::peg::mecpu 0.1.1 [list source [file join $dir compiler_peg_mecpu.tcl]] + +# -- Various other utilities -- + +package ifneeded page::util::peg 0.1 [list source [file join $dir util_peg.tcl]] +package ifneeded page::util::quote 0.1 [list source [file join $dir util_quote.tcl]] +package ifneeded page::util::flow 0.1 [list source [file join $dir util_flow.tcl]] diff --git a/tcllib/modules/page/pluginmgr.tcl b/tcllib/modules/page/pluginmgr.tcl new file mode 100644 index 0000000..ac00192 --- /dev/null +++ b/tcllib/modules/page/pluginmgr.tcl @@ -0,0 +1,581 @@ +# -*- tcl -*- +# ### ### ### ######### ######### ######### + +## This package provides custom plugin management specific to PAGE. It +## is built on top of the generic plugin management framework (See +## ---> pluginmgr). + +# ### ### ### ######### ######### ######### +## Requisites + +package require fileutil +package require pluginmgr ; # Generic plugin management framework + +namespace eval ::page::pluginmgr {} + +# ### ### ### ######### ######### ######### +## API (Public, exported) + +proc ::page::pluginmgr::reportvia {cmd} { + variable reportcmd $cmd + return +} + +proc ::page::pluginmgr::log {cmd} { + variable reader + variable writer + variable transforms + + set iplist {} + lappend iplist [$reader interpreter] + lappend iplist [$writer interpreter] + foreach t $transforms { + lappend iplist [$t interpreter] + } + + if {$cmd eq ""} { + # No logging. Disable with empty command, + # to allow the system to completely remove + # them from the bytecode (= No execution + # overhead). + + foreach ip $iplist { + $ip eval [list proc page_log_error args {}] + $ip eval [list proc page_log_warning args {}] + $ip eval [list proc page_log_info args {}] + } + } else { + # Activate logging. Make the commands in + # the interpreters aliases to us. + + foreach ip $iplist { + interp alias $ip page_log_error {} ${cmd}::error + interp alias $ip page_log_warning {} ${cmd}::warning + interp alias $ip page_log_info {} ${cmd}::info + } + } + return +} + +proc ::page::pluginmgr::reader {name} { + variable reader + + $reader load $name + return [$reader do page_roptions] +} + +proc ::page::pluginmgr::rconfigure {dict} { + variable reader + foreach {k v} $dict { + $reader do page_rconfigure $k $v + } + return +} + +proc ::page::pluginmgr::rtimeable {} { + variable reader + return [$reader do page_rfeature timeable] +} + +proc ::page::pluginmgr::rtime {} { + variable reader + $reader do page_rtime + return +} + +proc ::page::pluginmgr::rgettime {} { + variable reader + return [$reader do page_rgettime] +} + +proc ::page::pluginmgr::rhelp {} { + variable reader + return [$reader do page_rhelp] +} + +proc ::page::pluginmgr::rlabel {} { + variable reader + return [$reader do page_rlabel] +} + +proc ::page::pluginmgr::read {read eof {complete {}}} { + variable reader + + #interp alias $ip page_read {} {*}$read + #interp alias $ip page_eof {} {*}$eof + + set ip [$reader interpreter] + eval [linsert $read 0 interp alias $ip page_read {}] + eval [linsert $eof 0 interp alias $ip page_eof {}] + + if {![llength $complete]} { + interp alias $ip page_read_done {} ::page::pluginmgr::Nop + } else { + eval [linsert $complete 0 interp alias $ip page_read_done {}] + } + + return [$reader do page_rrun] +} + +proc ::page::pluginmgr::writer {name} { + variable writer + + $writer load $name + return [$writer do page_woptions] +} + +proc ::page::pluginmgr::wconfigure {dict} { + variable writer + foreach {k v} $dict { + $writer do page_wconfigure $k $v + } + return +} + +proc ::page::pluginmgr::wtimeable {} { + variable writer + return [$writer do page_wfeature timeable] +} + +proc ::page::pluginmgr::wtime {} { + variable writer + $writer do page_wtime + return +} + +proc ::page::pluginmgr::wgettime {} { + variable writer + return [$writer do page_wgettime] +} + +proc ::page::pluginmgr::whelp {} { + variable writer + return [$writer do page_whelp] +} + +proc ::page::pluginmgr::wlabel {} { + variable writer + return [$writer do page_wlabel] +} + +proc ::page::pluginmgr::write {chan data} { + variable writer + + $writer do page_wrun $chan $data + return +} + +proc ::page::pluginmgr::transform {name} { + variable transform + variable transforms + + $transform load $name + + set id [llength $transforms] + set opt [$transform do page_toptions] + lappend transforms [$transform clone] + + return [list $id $opt] +} + +proc ::page::pluginmgr::tconfigure {id dict} { + variable transforms + + set t [lindex $transforms $id] + + foreach {k v} $dict { + $t do page_tconfigure $k $v + } + return +} + +proc ::page::pluginmgr::ttimeable {id} { + variable transforms + set t [lindex $transforms $id] + return [$t do page_tfeature timeable] +} + +proc ::page::pluginmgr::ttime {id} { + variable transforms + set t [lindex $transforms $id] + $t do page_ttime + return +} + +proc ::page::pluginmgr::tgettime {id} { + variable transforms + set t [lindex $transforms $id] + return [$t do page_tgettime] +} + +proc ::page::pluginmgr::thelp {id} { + variable transforms + set t [lindex $transforms $id] + return [$t do page_thelp] +} + +proc ::page::pluginmgr::tlabel {id} { + variable transforms + set t [lindex $transforms $id] + return [$t do page_tlabel] +} + +proc ::page::pluginmgr::transform_do {id data} { + variable transforms + variable reader + + set t [lindex $transforms $id] + + return [$t do page_trun $data] +} + +proc ::page::pluginmgr::configuration {name} { + variable config + + if {[file exists $name]} { + # Try as plugin first. On failure read it as list of options, + # separated by spaces and tabs, and possibly quoted with + # quotes and double-quotes. + + if {[catch {$config load $name}]} { + set ch [open $name r] + set options [::read $ch] + close $ch + + set def {} + while {[string length $options]} { + if {[regsub "^\[ \t\n\]+" $options {} options]} { + # Skip whitespace + continue + } + if {[regexp -indices {^'(([^']|(''))*)'} \ + $options -> word]} { + foreach {__ end} $word break + lappend def [string map {'' '} [string range $options 1 $end]] + set options [string range $options [incr end 2] end] + } elseif {[regexp -indices {^"(([^"]|(""))*)"} \ + $options -> word]} { + foreach {__ end} $word break + lappend def [string map {{""} {"}} [string range $options 1 $end]] + set options [string range $options [incr end 2] end] + } elseif {[regexp -indices "^(\[^ \t\n\]+)" \ + $options -> word]} { + foreach {__ end} $word break + lappend def [string range $options 0 $end] + set options [string range $options [incr end] end] + } + } + return $def + } + } else { + $config load $name + } + set def [$config do page_cdefinition] + $config unload + return $def +} + +proc ::page::pluginmgr::report {level text {from {}} {to {}}} { + variable replevel + variable reportcmd + uplevel #0 [linsert $reportcmd end $replevel($level) $text $from $to] + return +} + +# ### ### ### ######### ######### ######### +## Internals + +## Data structures +## +## - reader | Instances of pluginmgr configured for input, +## - transform | transformational, and output plugins. The +## - writer | manager for transforms is actually a template +## | from which the actual instances are cloned. + +## - reportcmd | Callback for reporting of input error and warnings. +## - replevel | Mapping from chosen level to the right-padded text +## | to use. + +namespace eval ::page::pluginmgr { + variable replevel + array set replevel { + info {info } + warning {warning} + error {error } + } +} + +proc ::page::pluginmgr::Initialize {} { + InitializeReporting + InitializeConfig + InitializeReader + InitializeTransform + InitializeWriter + return +} + +proc ::page::pluginmgr::InitializeReader {} { + variable commands + variable reader_api + variable reader [pluginmgr RD \ + -setup ::page::pluginmgr::InitializeReaderIp \ + -pattern page::reader::* \ + -api $reader_api \ + -cmdip {} \ + -cmds $commands] + + # The page_log_* commands are set later, when it is known if + # logging is active or not, as their implementation depends on + # this. + + pluginmgr::paths $reader page::reader + return +} + +proc ::page::pluginmgr::InitializeReaderIp {p ip} { + interp eval $ip { + # @sak notprovided page::plugin + # @sak notprovided page::plugin::reader + package provide page::plugin 1.0 + package provide page::plugin::reader 1.0 + } + interp alias $ip puts {} puts + interp alias $ip open {} ::page::pluginmgr::AliasOpen $ip + interp alias $ip write {} ::page::pluginmgr::WriteFile $ip + return +} + +proc ::page::pluginmgr::InitializeWriter {} { + variable commands + variable writer_api + variable writer [pluginmgr WR \ + -setup ::page::pluginmgr::InitializeWriterIp \ + -pattern page::writer::* \ + -api $writer_api \ + -cmdip {} \ + -cmds $commands] + + # The page_log_* commands are set later, when it is known if + # logging is active or not, as their implementation depends on + # this. + + pluginmgr::paths $writer page::writer + return +} + +proc ::page::pluginmgr::InitializeWriterIp {p ip} { + interp eval $ip { + # @sak notprovided page::plugin + # @sak notprovided page::plugin::writer + package provide page::plugin 1.0 + package provide page::plugin::writer 1.0 + } + interp alias $ip puts {} puts + interp alias $ip open {} ::page::pluginmgr::AliasOpen $ip + interp alias $ip write {} ::page::pluginmgr::WriteFile $ip + return +} + +proc ::page::pluginmgr::InitializeTransform {} { + variable transforms {} + variable commands + variable transform_api + variable transform [pluginmgr TR \ + -setup ::page::pluginmgr::InitializeTransformIp \ + -pattern page::transform::* \ + -api $transform_api \ + -cmdip {} \ + -cmds $commands] + + # The page_log_* commands are set later, when it is known if + # logging is active or not, as their implementation depends on + # this. + + pluginmgr::paths $transform page::transform + return +} + +proc ::page::pluginmgr::InitializeTransformIp {p ip} { + interp eval $ip { + # @sak notprovided page::plugin + # @sak notprovided page::plugin::transform + package provide page::plugin 1.0 + package provide page::plugin::transform 1.0 + } + interp alias $ip puts {} puts + interp alias $ip open {} ::page::pluginmgr::AliasOpen $ip + interp alias $ip write {} ::page::pluginmgr::WriteFile $ip + return +} + +proc ::page::pluginmgr::InitializeConfig {} { + variable config [pluginmgr CO \ + -pattern page::config::* \ + -api {page_cdefinition}] + + pluginmgr::paths $config page::config + return +} + +proc ::page::pluginmgr::InitializeReporting {} { + variable reportcmd ::page::pluginmgr::ReportStderr + return +} + +proc ::page::pluginmgr::ReportStderr {level text from to} { + # from = epsilon | list (line col) + # to = epsilon | list (line col) + # line = 5 digits, col = 3 digits + + if { + ($text eq "") && + ![llength $from] && + ![llength $to] + } { + puts stderr "" + return + } + + puts -nonewline stderr $level + WriteLocation $from + if {![llength $to]} { + puts -nonewline stderr { } + } else { + puts -nonewline stderr {-} + } + WriteLocation $to + puts -nonewline stderr " " + puts -nonewline stderr $text + puts stderr "" + return +} + +proc ::page::pluginmgr::WriteLocation {loc} { + if {![llength $loc]} { + set text { } + } else { + set line [lindex $loc 0] + set col [lindex $loc 1] + set text {} + if {![string length $line]} { + append text _____ + } else { + append text [string map {{ } _} [format %5d $line]] + } + append text @ + if {![string length $col]} { + append text ___ + } else { + append text [string map {{ } _} [format %3d $col]] + } + } + puts -nonewline stderr $text + return +} + +proc ::page::pluginmgr::AliasOpen {slave file {acc {}} {perm {}}} { + + if {$acc eq ""} {set acc r} + + ::safe::Log $slave ============================================= + ::safe::Log $slave "open $file $acc $perm" + + if {[regexp {[wa+]|(WRONLY)|(RDWR)|(APPEND)|(CREAT)|(TRUNC)} $acc]} { + # Do not allow write acess. + ::safe::Log $slave "permission denied" + ::safe::Log $slave 0/============================================ + return -code error "permission denied" + } + + if {[catch {set file [::safe::TranslatePath $slave $file]} msg]} { + ::safe::Log $slave $msg + ::safe::Log $slave "permission denied" + ::safe::Log $slave 1/============================================ + return -code error "permission denied" + } + + # check that the path is in the access path of that slave + + if {[catch {::safe::FileInAccessPath $slave $file} msg]} { + ::safe::Log $slave $msg + ::safe::Log $slave "permission denied" + ::safe::Log $slave 2/============================================ + return -code error "permission denied" + } + + # do the checks on the filename : + + if {[catch {::safe::CheckFileName $slave $file} msg]} { + ::safe::Log $slave "$file: $msg" + ::safe::Log $slave "$msg" + ::safe::Log $slave 3/============================================ + return -code error $msg + } + + if {[catch {::interp invokehidden $slave open $file $acc} msg]} { + ::safe::Log $slave "Caught: $msg" + ::safe::Log $slave "script error" + ::safe::Log $slave 4/============================================ + return -code error "script error" + } + + ::safe::Log $slave =/============================================ + return $msg + +} + +proc ::page::pluginmgr::Nop {args} {} + +proc ::page::pluginmgr::WriteFile {slave file text} { + if {[file pathtype $file] ne "relative"} { + set file [file join [pwd] [file tail $fail]] + } + file mkdir [file dirname $file] + fileutil::writeFile $file $text + return +} + +# ### ### ### ######### ######### ######### +## Initialization + +namespace eval ::page::pluginmgr { + + # List of functions in the various plugin APIs + + variable reader_api { + page_rhelp + page_rlabel + page_roptions + page_rconfigure + page_rrun + page_rfeature + } + variable writer_api { + page_whelp + page_wlabel + page_woptions + page_wconfigure + page_wrun + page_wfeature + } + variable transform_api { + page_thelp + page_tlabel + page_toptions + page_tconfigure + page_trun + page_tfeature + } + variable commands { + page_info {::page::pluginmgr::report info} + page_warning {::page::pluginmgr::report warning} + page_error {::page::pluginmgr::report error} + } +} + +::page::pluginmgr::Initialize + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::pluginmgr 0.2 diff --git a/tcllib/modules/page/plugins/config_peg.tcl b/tcllib/modules/page/plugins/config_peg.tcl new file mode 100644 index 0000000..517877f --- /dev/null +++ b/tcllib/modules/page/plugins/config_peg.tcl @@ -0,0 +1,14 @@ +# -*- tcl -*- $Id: config_peg.tcl,v 1.2 2005/09/28 06:16:38 andreas_kupries Exp $ + +package provide page::config::peg 0.1 + +proc page_cdefinition {} { + return { + --reset + --append + --reader peg + --transform reachable + --transform realizable + --writer me + } +} diff --git a/tcllib/modules/page/plugins/pkgIndex.tcl b/tcllib/modules/page/plugins/pkgIndex.tcl new file mode 100644 index 0000000..8b6332a --- /dev/null +++ b/tcllib/modules/page/plugins/pkgIndex.tcl @@ -0,0 +1,34 @@ +#puts @plugins +# -- PAGE plugin packages +# -- ---- Canned configurations + +package ifneeded page::config::peg 0.1 [list source [file join $dir config_peg.tcl]] + +# -- PAGE plugin packages +# -- ---- Readers + +package ifneeded page::reader::peg 0.1 [list source [file join $dir reader_peg.tcl]] +package ifneeded page::reader::lemon 0.1 [list source [file join $dir reader_lemon.tcl]] +package ifneeded page::reader::hb 0.1 [list source [file join $dir reader_hb.tcl]] +package ifneeded page::reader::ser 0.1 [list source [file join $dir reader_ser.tcl]] +package ifneeded page::reader::treeser 0.1 [list source [file join $dir reader_treeser.tcl]] + +# -- PAGE plugin packages +# -- ---- Writers + +package ifneeded page::writer::null 0.1 [list source [file join $dir writer_null.tcl]] +package ifneeded page::writer::me 0.1 [list source [file join $dir writer_me.tcl]] +package ifneeded page::writer::mecpu 0.1.1 [list source [file join $dir writer_mecpu.tcl]] +package ifneeded page::writer::tree 0.1 [list source [file join $dir writer_tree.tcl]] +package ifneeded page::writer::tpc 0.1 [list source [file join $dir writer_tpc.tcl]] +package ifneeded page::writer::hb 0.1 [list source [file join $dir writer_hb.tcl]] +package ifneeded page::writer::ser 0.1 [list source [file join $dir writer_ser.tcl]] +package ifneeded page::writer::peg 0.1 [list source [file join $dir writer_peg.tcl]] +package ifneeded page::writer::identity 0.1 [list source [file join $dir writer_identity.tcl]] + +# -- PAGE plugin packages +# -- ---- Transformations + +package ifneeded page::transform::reachable 0.1 [list source [file join $dir transform_reachable.tcl]] +package ifneeded page::transform::realizable 0.1 [list source [file join $dir transform_realizable.tcl]] +package ifneeded page::transform::mecpu 0.1 [list source [file join $dir transform_mecpu.tcl]] diff --git a/tcllib/modules/page/plugins/reader_hb.tcl b/tcllib/modules/page/plugins/reader_hb.tcl new file mode 100644 index 0000000..4cee18c --- /dev/null +++ b/tcllib/modules/page/plugins/reader_hb.tcl @@ -0,0 +1,114 @@ +# -*- tcl -*- +# -- $Id: reader_hb.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - reader - HB ~ Half baked PEG Container +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_read | Access to the input stream. +# page_read_done | +# page_eof | +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_rfeature | Query for special plugin features page might wish to use. +# page_rtime | Activate collection of timing statistics. +# page_rgettime | Return the collected timing statistics. +# page_rlabel | User readable label for the plugin. +# page_rhelp | Doctools help text for plugin. +# page_roptions | Options understood by plugin. +# page_rconfigure | Option (re)configuration. +# page_rdata | External access to processed input stream. +# page_rrun | Process input stream per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require struct::tree ; # Data structure. +package require page::parse::peghb + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_rlabel {} { + return {Halfbaked PEG Container} +} + +proc page_rfeature {key} { + return [string eq $key timeable] +} + +proc page_rtime {} { + global timed + set timed 1 + return +} + +proc page_rgettime {} { + global usec + return $usec +} + +proc page_rhelp {} { + return {} +} + +proc page_roptions {} { + return {} +} + +proc page_rconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +## proc page_rdata {} {} +## Created in 'Initialize' + +proc page_rrun {} { + global timed usec + page_log_info "reader/hb/run/parse" + + struct::tree ::tree + + if {$timed} { + set usec [lindex [time { + page::parse::peghb [page_read] ::tree + }] 0] ; #{} + } else { + page::parse::peghb [page_read] ::tree + } + page_read_done + + set ast [::tree serialize] + ::tree destroy + + page_log_info "reader/hb/run/ok" + return $ast +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::reader::hb 0.1 diff --git a/tcllib/modules/page/plugins/reader_lemon.tcl b/tcllib/modules/page/plugins/reader_lemon.tcl new file mode 100644 index 0000000..16df290 --- /dev/null +++ b/tcllib/modules/page/plugins/reader_lemon.tcl @@ -0,0 +1,170 @@ +# -*- tcl -*- +# -- $Id: reader_lemon.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - reader - LEMON ~ Grammar specification as understood +# by drh's lemon parser generator. +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_read | Access to the input stream. +# page_read_done | +# page_eof | +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_rfeature | Query for special plugin features page might wish to use. +# page_rtime | Activate collection of timing statistics. +# page_rgettime | Return the collected timing statistics. +# page_rlabel | User readable label for the plugin. +# page_rhelp | Doctools help text for plugin. +# page_roptions | Options understood by plugin. +# page_rconfigure | Option (re)configuration. +# page_rdata | External access to processed input stream. +# page_rrun | Process input stream per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::norm::lemon ; # Normalize AST generated by PE matcher. +package require page::parse::lemon ; # Mengine based parser for Lemon grammars. +package require struct::tree ; # Data structure. +package require grammar::me::util ; # AST conversion + +global usec +global timed +set timed 0 + +global cline +global ccol + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_rlabel {} { + return {Lemon specification} +} + +proc page_rfeature {key} { + return [string eq $key timeable] +} + +proc page_rtime {} { + global timed + set timed 1 + return +} + +proc page_rgettime {} { + global usec + return $usec +} + +proc page_rhelp {} { + return {} +} + +proc page_roptions {} { + return {} +} + +proc page_rconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +## proc page_rdata {} {} +## Created in 'Initialize' + +proc page_rrun {} { + global timed usec cline ccol + page_log_info "reader/lemon/run/parse" + + set ast {} + set err {} + + # Location of the next character to be read. + set cline 1 + set ccol 0 + + if {$timed} { + set usec [lindex [time { + set ok [::page::parse::lemon::parse ::Next err ast] + }] 0] ; #{} + } else { + set ok [::page::parse::lemon::parse ::Next err ast] + } + page_read_done + page_log_info "reader/lemon/run/check-for-errors" + + if {!$ok} { + foreach {olc messages} $err break + foreach {offset linecol} $olc break + foreach {line col} $linecol break + + set olc [string map {{ } _} \ + [format %5d $line]]@[string map {{ } _} \ + [format %3d $col]]/([format %5d $offset]) + + foreach m $messages { + page_log_error "reader/lemon/run: $olc: $m" + page_error $m $linecol + } + + page_log_info "reader/lemon/run/failed" + return {} + } + + page_log_info "reader/lemon/run/ast-conversion" + + struct::tree ::tree + ::grammar::me::util::ast2etree $ast ::grammar::me::tcl ::tree + ::page::util::norm::lemon ::tree + + set ast [::tree serialize] + ::tree destroy + + page_log_info "reader/lemon/run/ok" + return $ast +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +proc Next {} { + global cline ccol + + if {[page_eof]} {return {}} + + set ch [page_read 1] + + if {$ch eq ""} {return {}} + + set tok [list $ch {} $cline $ccol] + + if {$ch eq "\n"} { + incr cline ; set ccol 0 + } else { + incr ccol + } + + return $tok +} + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::reader::lemon 0.1 diff --git a/tcllib/modules/page/plugins/reader_peg.tcl b/tcllib/modules/page/plugins/reader_peg.tcl new file mode 100644 index 0000000..52fd6a8 --- /dev/null +++ b/tcllib/modules/page/plugins/reader_peg.tcl @@ -0,0 +1,169 @@ +# -*- tcl -*- +# -- $Id: reader_peg.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - reader - PEG ~ Parsing Expression Grammar +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_read | Access to the input stream. +# page_read_done | +# page_eof | +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_rfeature | Query for special plugin features page might wish to use. +# page_rtime | Activate collection of timing statistics. +# page_rgettime | Return the collected timing statistics. +# page_rlabel | User readable label for the plugin. +# page_rhelp | Doctools help text for plugin. +# page_roptions | Options understood by plugin. +# page_rconfigure | Option (re)configuration. +# page_rdata | External access to processed input stream. +# page_rrun | Process input stream per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::norm::peg ; # Normalize AST generated by reader of PEG grammars +package require page::parse::peg ; # Mengine based parser for PE grammars. +package require struct::tree ; # Data structure. +package require grammar::me::util ; # AST conversion + +global usec +global timed +set timed 0 + +global cline +global ccol + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_rlabel {} { + return {Parsing Expression Grammar} +} + +proc page_rfeature {key} { + return [string eq $key timeable] +} + +proc page_rtime {} { + global timed + set timed 1 + return +} + +proc page_rgettime {} { + global usec + return $usec +} + +proc page_rhelp {} { + return {} +} + +proc page_roptions {} { + return {} +} + +proc page_rconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +## proc page_rdata {} {} +## Created in 'Initialize' + +proc page_rrun {} { + global timed usec cline ccol + page_log_info "reader/peg/run/parse" + + set ast {} + set err {} + + # Location of the next character to be read. + set cline 1 + set ccol 0 + + if {$timed} { + set usec [lindex [time { + set ok [::page::parse::peg::parse ::Next err ast] + }] 0] ; #{} + } else { + set ok [::page::parse::peg::parse ::Next err ast] + } + page_read_done + page_log_info "reader/peg/run/check-for-errors" + + if {!$ok} { + foreach {olc messages} $err break + foreach {offset linecol} $olc break + foreach {line col} $linecol break + + set olc [string map {{ } _} \ + [format %5d $line]]@[string map {{ } _} \ + [format %3d $col]]/([format %5d $offset]) + + foreach m $messages { + page_log_error "reader/peg/run: $olc: $m" + page_error $m $linecol + } + + page_log_info "reader/peg/run/failed" + return {} + } + + page_log_info "reader/peg/run/ast-conversion" + + struct::tree ::tree + ::grammar::me::util::ast2etree $ast ::grammar::me::tcl ::tree + ::page::util::norm::peg ::tree + + set ast [::tree serialize] + ::tree destroy + + page_log_info "reader/peg/run/ok" + return $ast +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +proc Next {} { + global cline ccol + + if {[page_eof]} {return {}} + + set ch [page_read 1] + + if {$ch eq ""} {return {}} + + set tok [list $ch {} $cline $ccol] + + if {$ch eq "\n"} { + incr cline ; set ccol 0 + } else { + incr ccol + } + + return $tok +} + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::reader::peg 0.1 diff --git a/tcllib/modules/page/plugins/reader_ser.tcl b/tcllib/modules/page/plugins/reader_ser.tcl new file mode 100644 index 0000000..b4e1a68 --- /dev/null +++ b/tcllib/modules/page/plugins/reader_ser.tcl @@ -0,0 +1,114 @@ +# -*- tcl -*- +# -- $Id: reader_ser.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - reader - SER ~ Serialized PEG Container +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_read | Access to the input stream. +# page_read_done | +# page_eof | +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_rfeature | Query for special plugin features page might wish to use. +# page_rtime | Activate collection of timing statistics. +# page_rgettime | Return the collected timing statistics. +# page_rlabel | User readable label for the plugin. +# page_rhelp | Doctools help text for plugin. +# page_roptions | Options understood by plugin. +# page_rconfigure | Option (re)configuration. +# page_rdata | External access to processed input stream. +# page_rrun | Process input stream per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require struct::tree ; # Data structure. +package require page::parse::pegser + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_rlabel {} { + return {Serialized PEG Container} +} + +proc page_rfeature {key} { + return [string eq $key timeable] +} + +proc page_rtime {} { + global timed + set timed 1 + return +} + +proc page_rgettime {} { + global usec + return $usec +} + +proc page_rhelp {} { + return {} +} + +proc page_roptions {} { + return {} +} + +proc page_rconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +## proc page_rdata {} {} +## Created in 'Initialize' + +proc page_rrun {} { + global timed usec + page_log_info "reader/ser/run/parse" + + struct::tree ::tree + + if {$timed} { + set usec [lindex [time { + page::parse::pegser [page_read] ::tree + }] 0] ; #{} + } else { + page::parse::pegser [page_read] ::tree + } + page_read_done + + set ast [::tree serialize] + ::tree destroy + + page_log_info "reader/ser/run/ok" + return $ast +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::reader::ser 0.1 diff --git a/tcllib/modules/page/plugins/reader_treeser.tcl b/tcllib/modules/page/plugins/reader_treeser.tcl new file mode 100644 index 0000000..6394d7b --- /dev/null +++ b/tcllib/modules/page/plugins/reader_treeser.tcl @@ -0,0 +1,116 @@ +# -*- tcl -*- +# -- $Id: reader_treeser.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - reader - TREESER ~ Serialized TREE +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_read | Access to the input stream. +# page_read_done | +# page_eof | +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_rfeature | Query for special plugin features page might wish to use. +# page_rtime | Activate collection of timing statistics. +# page_rgettime | Return the collected timing statistics. +# page_rlabel | User readable label for the plugin. +# page_rhelp | Doctools help text for plugin. +# page_roptions | Options understood by plugin. +# page_rconfigure | Option (re)configuration. +# page_rdata | External access to processed input stream. +# page_rrun | Process input stream per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_rlabel {} { + return {Serialized Tree} +} + +proc page_rfeature {key} { + return [string eq $key timeable] +} + +proc page_rtime {} { + global timed + set timed 1 + return +} + +proc page_rgettime {} { + global usec + return $usec +} + +proc page_rhelp {} { + return {} +} + +proc page_roptions {} { + return {} +} + +proc page_rconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +## proc page_rdata {} {} +## Created in 'Initialize' + +proc page_rrun {} { + global timed usec + page_log_info "reader/treeser/run/parse" + + if {$timed} { + set usec [lindex [time { + set data [page_read] + }] 0] ; #{} + } else { + set data [page_read] + } + page_read_done + + # Reading and passing it on is trivial. + # Here however we validate the we truly got + # a sensible serialization. + + struct::tree ::tree + ::tree deserialize $data + ::tree destroy + + page_log_info "reader/treeser/run/ok" + return $data +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::reader::treeser 0.1 diff --git a/tcllib/modules/page/plugins/transform_mecpu.tcl b/tcllib/modules/page/plugins/transform_mecpu.tcl new file mode 100644 index 0000000..4c48e53 --- /dev/null +++ b/tcllib/modules/page/plugins/transform_mecpu.tcl @@ -0,0 +1,107 @@ +# -*- tcl -*- +# -- $Id: transform_mecpu.tcl,v 1.1 2006/07/01 01:35:21 andreas_kupries Exp $ --- +# +# PAGE plugin - transform - mecpu ~ Translation of grammar to ME cpu instruction set. +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_tdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_tfeature | Query for special plugin features page might wish to use. +# page_ttime | Activate collection of timing statistics. +# page_tgettime | Return the collected timing statistics. +# page_tlabel | User readable label for the plugin. +# page_thelp | Doctools help text for plugin. +# page_toptions | Options understood by plugin. +# page_tconfigure | Option (re)configuration. +# page_trun | Transform input data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::compiler::peg::mecpu +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_tlabel {} { + return {ME cpu Translation} +} + +proc page_tfeature {key} { + return [string eq $key timeable] +} + +proc page_ttime {} { + global timed + set timed 1 + return +} + +proc page_tgettime {} { + global usec + return $usec +} + +proc page_thelp {} { + return {} +} + +proc page_toptions {} { + return {} +} + +proc page_tconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_trun {data} { + global timed usec + page_log_info "transform/mecpu/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::compiler::peg::mecpu ::tree + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::compiler::peg::mecpu ::tree + } + set name [::tree get root name] + set asm [::tree get root asm] + ::tree destroy + + page_log_info "transform/mecpu/run/ok" + return [list $name $asm] +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::transform::mecpu 0.1 diff --git a/tcllib/modules/page/plugins/transform_reachable.tcl b/tcllib/modules/page/plugins/transform_reachable.tcl new file mode 100644 index 0000000..2dee6dd --- /dev/null +++ b/tcllib/modules/page/plugins/transform_reachable.tcl @@ -0,0 +1,107 @@ +# -*- tcl -*- +# -- $Id: transform_reachable.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - transform - reachable ~ Reachability Analysis +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_tdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_tfeature | Query for special plugin features page might wish to use. +# page_ttime | Activate collection of timing statistics. +# page_tgettime | Return the collected timing statistics. +# page_tlabel | User readable label for the plugin. +# page_thelp | Doctools help text for plugin. +# page_toptions | Options understood by plugin. +# page_tconfigure | Option (re)configuration. +# page_trun | Transform input data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::analysis::peg::reachable +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_tlabel {} { + return Reachability +} + +proc page_tfeature {key} { + return [string eq $key timeable] +} + +proc page_ttime {} { + global timed + set timed 1 + return +} + +proc page_tgettime {} { + global usec + return $usec +} + +proc page_thelp {} { + return {} +} + +proc page_toptions {} { + return {} +} + +proc page_tconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_trun {data} { + global timed usec + page_log_info "transform/reachable/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + ::page::analysis::peg::reachable::remove! ::tree + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + ::page::analysis::peg::reachable::remove! ::tree + } + + set data [::tree serialize] + ::tree destroy + + page_log_info "transform/reachable/run/ok" + return $data +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::transform::reachable 0.1 diff --git a/tcllib/modules/page/plugins/transform_realizable.tcl b/tcllib/modules/page/plugins/transform_realizable.tcl new file mode 100644 index 0000000..38efaeb --- /dev/null +++ b/tcllib/modules/page/plugins/transform_realizable.tcl @@ -0,0 +1,106 @@ +# -*- tcl -*- +# -- $Id: transform_realizable.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - transform - realizable ~ Realizability Analysis +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_tdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_tfeature | Query for special plugin features page might wish to use. +# page_ttime | Activate collection of timing statistics. +# page_tgettime | Return the collected timing statistics. +# page_tlabel | User readable label for the plugin. +# page_thelp | Doctools help text for plugin. +# page_toptions | Options understood by plugin. +# page_tconfigure | Option (re)configuration. +# page_trun | Transform input data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::analysis::peg::realizable +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_tlabel {} { + return Realizability +} + +proc page_tfeature {key} { + return [string eq $key timeable] +} + +proc page_ttime {} { + global timed + set timed 1 + return +} + +proc page_tgettime {} { + global usec + return $usec +} + +proc page_thelp {} { + return {} +} + +proc page_toptions {} { + return {} +} + +proc page_tconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_trun {data} { + global timed usec + page_log_info "transform/realizable/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + ::page::analysis::peg::realizable::remove! ::tree + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + ::page::analysis::peg::realizable::remove! ::tree + } + set data [::tree serialize] + ::tree destroy + + page_log_info "transform/realizable/run/ok" + return $data +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::transform::realizable 0.1 diff --git a/tcllib/modules/page/plugins/writer_hb.tcl b/tcllib/modules/page/plugins/writer_hb.tcl new file mode 100644 index 0000000..711e62a --- /dev/null +++ b/tcllib/modules/page/plugins/writer_hb.tcl @@ -0,0 +1,106 @@ +# -*- tcl -*- +# -- $Id: writer_hb.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - HB ~ Half Baked / Tcl Peg Container +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::hb +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Half baked PEG Container} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} + +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/hb/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::peg::hb ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::peg::hb ::tree $chan + } + + page_log_info "writer/hb/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::hb 0.1 diff --git a/tcllib/modules/page/plugins/writer_identity.tcl b/tcllib/modules/page/plugins/writer_identity.tcl new file mode 100644 index 0000000..b5d9cd4 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_identity.tcl @@ -0,0 +1,98 @@ +# -*- tcl -*- +# -- $Id: writer_identity.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - Generic dump +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Raw data unchanged} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/identity/run/" + + if {$timed} { + set usec [lindex [time { + puts $chan $data + }] 0] ; #{} + } else { + puts $chan $data + } + + page_log_info "writer/identity/run/ok" + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::identity 0.1 diff --git a/tcllib/modules/page/plugins/writer_me.tcl b/tcllib/modules/page/plugins/writer_me.tcl new file mode 100644 index 0000000..5e4d6b1 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_me.tcl @@ -0,0 +1,115 @@ +# -*- tcl -*- +# -- $Id: writer_me.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - ME ~ Match Engine +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::me +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {MatchEngine RecDescent} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} + +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {--package --copyright} +} + +proc page_wconfigure {option value} { + switch -exact -- $option { + --package { + page::gen::peg::me::package $value + } + --copyright { + page::gen::peg::me::copyright $value + } + default { + return -code error "Cannot set value of unknown option \"$option\"" + } + } +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/me/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::peg::me ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::peg::me ::tree $chan + } + page_log_info "writer/me/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::me 0.1 diff --git a/tcllib/modules/page/plugins/writer_mecpu.tcl b/tcllib/modules/page/plugins/writer_mecpu.tcl new file mode 100644 index 0000000..00e5508 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_mecpu.tcl @@ -0,0 +1,116 @@ +# -*- tcl -*- +# -- $Id: writer_mecpu.tcl,v 1.2 2007/03/21 23:15:53 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - ME cpu ~ Match Engine CPU +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::mecpu + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {ME cpu Assembler} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} + +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {--package --copyright --template --cmarker} +} + +proc page_wconfigure {option value} { + switch -exact -- $option { + --package { + page::gen::peg::mecpu::package $value + } + --copyright { + page::gen::peg::mecpu::copyright $value + } + --template { + page::gen::peg::mecpu::template $value + } + --cmarker { + page::gen::peg::mecpu::cmarker $value + } + default { + return -code error "Cannot set value of unknown option \"$option\"" + } + } +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/me-cpu/run/" + + if {$timed} { + set usec [lindex [time { + page::gen::peg::mecpu $data $chan + }] 0] ; #{} + } else { + page::gen::peg::mecpu $data $chan + } + page_log_info "writer/me-cpu/run/ok" + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::mecpu 0.1.1 diff --git a/tcllib/modules/page/plugins/writer_null.tcl b/tcllib/modules/page/plugins/writer_null.tcl new file mode 100644 index 0000000..7d15b25 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_null.tcl @@ -0,0 +1,97 @@ +# -*- tcl -*- +# -- $Id: writer_null.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - NULL ~ /dev/null the output +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return /dev/null +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} + +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + if {$timed} { + set usec [lindex [time { + page_log_info "writer/null/run/" + page_log_info "writer/null/run/ok" + }] 0] ; #{} + } else { + page_log_info "writer/null/run/" + page_log_info "writer/null/run/ok" + } + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::null 0.1 diff --git a/tcllib/modules/page/plugins/writer_peg.tcl b/tcllib/modules/page/plugins/writer_peg.tcl new file mode 100644 index 0000000..f8cd060 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_peg.tcl @@ -0,0 +1,106 @@ +# -*- tcl -*- +# -- $Id: writer_peg.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - PEG ~ Canonical PE Grammar +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::canon +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Canonical PEG} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} + +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/peg/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::peg::canon ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::peg::canon ::tree $chan + } + + page_log_info "writer/peg/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::peg 0.1 diff --git a/tcllib/modules/page/plugins/writer_ser.tcl b/tcllib/modules/page/plugins/writer_ser.tcl new file mode 100644 index 0000000..3b977a2 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_ser.tcl @@ -0,0 +1,104 @@ +# -*- tcl -*- +# -- $Id: writer_ser.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - SER ~ Serialized PEG Container +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::ser +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Serialized PEG Container} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/ser/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::peg::ser ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::peg::ser ::tree $chan + } + page_log_info "writer/ser/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::ser 0.1 diff --git a/tcllib/modules/page/plugins/writer_tpc.tcl b/tcllib/modules/page/plugins/writer_tpc.tcl new file mode 100644 index 0000000..6b14328 --- /dev/null +++ b/tcllib/modules/page/plugins/writer_tpc.tcl @@ -0,0 +1,105 @@ +# -*- tcl -*- +# -- $Id: writer_tpc.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - TPC ~ Tcl Peg Container +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::peg::cpkg +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Tcl PEG Container (Package)} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/tpc/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::peg::cpkg ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::peg::cpkg ::tree $chan + } + + page_log_info "writer/tpc/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::tpc 0.1 diff --git a/tcllib/modules/page/plugins/writer_tree.tcl b/tcllib/modules/page/plugins/writer_tree.tcl new file mode 100644 index 0000000..d02458e --- /dev/null +++ b/tcllib/modules/page/plugins/writer_tree.tcl @@ -0,0 +1,105 @@ +# -*- tcl -*- +# -- $Id: writer_tree.tcl,v 1.1 2005/09/28 04:51:22 andreas_kupries Exp $ --- +# +# PAGE plugin - writer - TREE ~ Generic Tree dump +# + +# ### ### ### ######### ######### ######### +## Imported API + +# -----------------+-- +# page_wdata | Access to processed input stream. +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Exported API + +# -----------------+-- +# page_wfeature | Query for special plugin features page might wish to use. +# page_wtime | Activate collection of timing statistics. +# page_wgettime | Return the collected timing statistics. +# page_wlabel | User readable label for the plugin. +# page_whelp | Doctools help text for plugin. +# page_woptions | Options understood by plugin. +# page_wconfigure | Option (re)configuration. +# page_wrun | Generate output from data per plugin configuration and hardwiring. +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::gen::tree::text +package require struct::tree ; # Data structure. + +global usec +global timed +set timed 0 + +# ### ### ### ######### ######### ######### +## Implementation of exported API + +proc page_wlabel {} { + return {Tree Dump} +} + +proc page_wfeature {key} { + return [string eq $key timeable] +} + +proc page_wtime {} { + global timed + set timed 1 + return +} + +proc page_wgettime {} { + global usec + return $usec +} +proc page_whelp {} { + return {} +} + +proc page_woptions {} { + return {} +} + +proc page_wconfigure {option value} { + return -code error "Cannot set value of unknown option \"$option\"" +} + +proc page_wrun {chan data} { + global timed usec + page_log_info "writer/tree/run/" + + if {$timed} { + set usec [lindex [time { + ::struct::tree ::tree deserialize $data + page::gen::tree::text ::tree $chan + }] 0] ; #{} + } else { + ::struct::tree ::tree deserialize $data + page::gen::tree::text ::tree $chan + } + + page_log_info "writer/tree/run/ok" + + ::tree destroy + return +} + +# ### ### ### ######### ######### ######### +## Internal helper code. + +# ### ### ### ######### ######### ######### +## Initialization + +package provide page::writer::tree 0.1 diff --git a/tcllib/modules/page/util_flow.tcl b/tcllib/modules/page/util_flow.tcl new file mode 100644 index 0000000..d8245ff --- /dev/null +++ b/tcllib/modules/page/util_flow.tcl @@ -0,0 +1,90 @@ +# -*- tcl -*- +# General tree iterative walking for dataflow algorithms. + +# ### ### ### ######### ######### ######### +## Requisites + +package require snit + +# ### ### ### ######### ######### ######### +## API + +namespace eval ::page::util::flow {} + +proc ::page::util::flow {start fvar nvar script} { + set f [uplevel 1 [list ::page::util::flow::iter %AUTO% $start $fvar $nvar $script]] + $f destroy + return +} + +# ### ### ### ######### ######### ######### +## Internals + +snit::type ::page::util::flow::iter { + constructor {startset fvar nvar script} { + $self visitl $startset + + # Export the object for use by the flow script + upvar 3 $fvar flow ; set flow $self + upvar 3 $nvar current + + while {[array size visit]} { + set nodes [array names visit] + array unset visit * + + foreach n $nodes { + set current $n + set code [catch {uplevel 3 $script} result] + + # decide what to do upon the return code: + # + # 0 - the body executed successfully + # 1 - the body raised an error + # 2 - the body invoked [return] + # 3 - the body invoked [break] + # 4 - the body invoked [continue] + # everything else - return and pass on the results + + switch -exact -- $code { + 0 {} + 1 { + return -errorinfo $::errorInfo \ + -errorcode $::errorCode -code error $result + } + 3 { + # FRINK: nocheck + return -code break + } + 4 {} + default { + # This includes code 2 (return). + return -code $code $result + } + } + } + } + return + } + + method visit {n} { + set visit($n) . + return + } + + method visitl {nodelist} { + foreach n $nodelist {set visit($n) .} + return + } + + method visita {args} { + foreach n $args {set visit($n) .} + return + } + + variable visit -array {} +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::util::flow 0.1 diff --git a/tcllib/modules/page/util_norm_lemon.tcl b/tcllib/modules/page/util_norm_lemon.tcl new file mode 100644 index 0000000..f604f81 --- /dev/null +++ b/tcllib/modules/page/util_norm_lemon.tcl @@ -0,0 +1,427 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Transformation - Normalize PEG AST for later. + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require treeql +package require page::util::quote +package require page::util::peg + +namespace eval ::page::util::norm::lemon { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl) + + namespace import ::page::util::quote::* + namespace import ::page::util::peg::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::util::norm::lemon {t} { + set q [treeql q -tree $t] + + page_info {[Lemon Normalization]} + + # Retrieve grammar name out of one directive. + # Or from LHS of first rule. + + page_log_info ..Startsymbol + + set start {} + + $q query tree \ + withatt type nonterminal \ + withatt detail StartSymbol \ + descendants \ + withatt type terminal \ + over n { + + lemon::TokReduce $t $n detail + set start [$t get $n detail] + + page_info " StartSymbol: $start" + } + + $q query tree \ + withatt type nonterminal \ + withatt detail Name \ + descendants \ + withatt type terminal \ + over n { + + lemon::TokReduce $t $n detail + set name [$t get $n detail] + + page_info " Name: $name" + + $t set root name $name + } + + page_log_info ..Drop ; lemon::Drop $q $t + page_log_info ..Terminals ; lemon::Terminals $q $t + page_log_info ..Definitions ; lemon::Definitions $q $t + page_log_info ..Rules ; lemon::Rules $q $t start + page_log_info ..Epsilon ; lemon::ElimEpsilon $q $t + page_log_info ..Autoclass ; lemon::AutoClassId $q $t + page_log_info ..Chains + + # Find and cut operator chains, very restricted. Cut only chains + # of x- and /-operators. The other operators have only one child + # by definition and are thus not chains. + + #set q [treeql q -tree $t] + # q query tree over n + foreach n [$t children -all root] { + if {[$t keyexists $n symbol]} continue + if {[llength [$t children $n]] != 1} continue + + set op [$t get $n op] + if {($op ne "/") && ($op ne "x")} continue + $t cut $n + } + + page_log_info ..Flatten + + lemon::flatten $q $t + + # Analysis: Left recursion, and where. + # Manual: Definitions for terminals. + # Definitions for space, comments. + # Integration of this into the grammar. + + # Sentinel for PE algorithms. + $t set root symbol <StartExpression> + + if {$start eq ""} { + page_error " Startsymbol missing" + } else { + set s [$t insert root end] + $t set $s op n + $t set $s sym $start + $t set root start $s + + array set def [$t get root definitions] + + if {![info exists def($start)]} { + page_error " Startsymbol is undefined" + $t set $s def "" + } else { + $t set $s def $def($start) + } + unset def + } + + $q destroy + + page_log_info Ok + return +} + +# ### ### ### ######### ######### ######### +## Documentation +# +## See doc_normalize.txt for the specification of the publicly visible +## attributes. +## +## Internal attributes +## - DATA - Transient storage for terminal data. + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::util::norm::lemon::Drop {q t} { + # Simple normalization. + # All lemon specific data is dropped completely. + + foreach drop { + Directive Codeblock Label Precedence + } { + $q query tree withatt type nonterminal \ + withatt detail $drop over n { + $t delete $n + } + } + + # Some nodes can be dropped, but not their children. + + $q query tree withatt type nonterminal \ + withatt detail Statement over n { + $t cut $n + } + + # Cut the ALL and LemonGrammar nodes, direct access, no search + # needed. + + $t cut [lindex [$t children root] 0] + $t cut [lindex [$t children root] 0] + + return +} + +proc ::page::util::norm::lemon::Terminals {q t} { + # The data for all terminals is stored in their grandparental + # nodes. We get rid of both terminals and their parents. + + $q query tree withatt type terminal over n { + set p [$t parent $n] + set gp [$t parent $p] + + CopyLocation $t $n $gp + AttrCopy $t $n detail $gp DATA + TokReduce $t $gp DATA + $t delete $p + } + + # We can now drop the type attribute, as all the remaining nodes + # (which have it) will contain the value 'nonterminal'. + + $q query tree hasatt type over n { + $t unset $n type + } + return +} + +proc ::page::util::norm::lemon::Definitions {q t} { + # Convert 'Definition' into the sequences they are. + # Sequences of length one will be flattened later. + # Empty sequences (Length zero) are epsilon. + # Epsilon will be later converted to ? of the + # whole choice they are part of. + + $q query tree withatt detail Definition over n { + $t unset $n detail + + if {[$t children $n] < 1} { + $t set $n op epsilon + } else { + $t set $n op x + } + } + return +} + +proc ::page::util::norm::lemon::Rules {q t sv} { + upvar $sv start + # We move nonterminal hint information from nodes into attributes, + # and delete the now irrelevant nodes. + + # Like with the global metadata we move definition specific + # information out of nodes into attributes, get rid of the + # superfluous nodes, and tag the definition roots with marker + # attributes. + + array set defs {} + $q query tree withatt detail Rule over n { + set first [Child $t $n 0] + + set sym [$t get $first DATA] + $t set $n symbol $sym + $t set $n label $sym + $t set $n users {} + $t set $n mode value + + if {$start eq ""} { + page_info " StartSymbol: $sym" + set start $sym + } + + # We get the left extend of the definition from the terminal + # for the symbol it defines. + + MergeLocations $t $first [Rightmost $t $n] $n + $t unset $n detail + + lappend defs($sym) $n + $t cut $first + } + + set d {} + foreach sym [array names defs] { + set nodes $defs($sym) + if {[llength $nodes] == 1} { + lappend d $sym [lindex $nodes 0] + } else { + # Merge multi-node definition together, under a choice. + + set r [$t insert root end] + set c [$t insert $r end] + + $t set $r symbol $sym + $t set $r label $sym + $t set $r users {} + $t set $r mode value + $t set $c op / + + foreach n $nodes { + set seq [lindex [$t children $n] 0] + $t move $c end $seq + $t delete $n + } + + lappend d $sym $r + } + } + + # We remember a mapping from nonterminal names to their defining + # nodes in the root as well, for quick reference later, when we + # build nonterminal usage references + + $t set root definitions $d + return +} + +proc ::page::util::norm::lemon::Rightmost {t n} { + # Determine the rightmost leaf under the specified node. + + if {[$t isleaf $n]} {return $n} + return [Rightmost $t [lindex [$t children $n] end]] +} + +proc ::page::util::norm::lemon::ElimEpsilon {q t} { + # We convert choices with an epsilon in them into + # optional choices without an epsilon branch. + + $q query tree withatt op epsilon over n { + set choice [$t parent $n] + + # Move branches into the epsilon, which becomes the new + # choice. And the choice becomes an option. + foreach c [$t children $choice] { + if {$c eq $n} continue + $t move $n end $c + } + $t set $n op / + $t set $choice op ? + } + return +} + +proc ::page::util::norm::lemon::AutoClassId {q t} { + + array set defs [$t get root definitions] + array set use {} + + $q query tree \ + withatt op x \ + children \ + hasatt DATA \ + over n { + # All identifiers are nonterminals, and for the + # undefined ones we create rules which define + # them as terminal sequences. + + set sym [$t get $n DATA] + $t unset $n DATA + + $t set $n op n + $t set $n sym $sym + + if {![info exists defs($sym)]} { + set defs($sym) [NewTerminal $t $sym] + } + $t set $n def $defs($sym) + + lappend use($sym) $n + $t unset $n detail + } + + $t set root definitions [array get defs] + + foreach sym [array names use] { + $t set $defs($sym) users $use($sym) + } + + $t set root undefined {} + return +} + +proc ::page::util::norm::lemon::NewTerminal {t sym} { + page_log_info " Terminal: $sym" + + set r [$t insert root end] + $t set $r symbol $sym + $t set $r label $sym + $t set $r users {} + $t set $r mode leaf + + set s [$t insert $r end] + $t set $s op x + + foreach ch [split $sym {}] { + set c [$t insert $s end] + $t set $c op t + $t set $c char $ch + } + return $r +} + +# ### ### ### ######### ######### ######### +## Internal. Low-level helpers. + +proc ::page::util::norm::lemon::CopyLocation {t src dst} { + $t set $dst range [$t get $src range] + $t set $dst range_lc [$t get $src range_lc] + return +} + +proc ::page::util::norm::lemon::MergeLocations {t srca srcb dst} { + set ar [$t get $srca range] + set arlc [$t get $srca range_lc] + + set br [$t get $srcb range] + set brlc [$t get $srcb range_lc] + + $t set $dst range [list [lindex $ar 0] [lindex $br 1]] + $t set $dst range_lc [list [lindex $arlc 0] [lindex $brlc 1]] + return +} + +proc ::page::util::norm::lemon::AttrCopy {t src asrc dst adst} { + $t set $dst $adst [$t get $src $asrc] + return +} + +proc ::page::util::norm::lemon::Child {t n index} { + return [lindex [$t children $n] $index] +} + +proc ::page::util::norm::lemon::TokReduce {t src attr} { + set tokens [$t get $src $attr] + set ch {} + foreach tok $tokens { + lappend ch [lindex $tok 0] + } + $t set $src $attr [join $ch {}] + return +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::util::norm::lemon 0.1 diff --git a/tcllib/modules/page/util_norm_peg.tcl b/tcllib/modules/page/util_norm_peg.tcl new file mode 100644 index 0000000..90405be --- /dev/null +++ b/tcllib/modules/page/util_norm_peg.tcl @@ -0,0 +1,415 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / Transformation - Normalize PEG AST for later. + +# This package assumes to be used from within a PAGE plugin. It uses +# the API commands listed below. These are identical across the major +# types of PAGE plugins, allowing this package to be used in reader, +# transform, and writer plugins. It cannot be used in a configuration +# plugin, and this makes no sense either. +# +# To ensure that our assumption is ok we require the relevant pseudo +# package setup by the PAGE plugin management code. +# +# -----------------+-- +# page_info | Reporting to the user. +# page_warning | +# page_error | +# -----------------+-- +# page_log_error | Reporting of internals. +# page_log_warning | +# page_log_info | +# -----------------+-- + +# ### ### ### ######### ######### ######### +## Requisites + +# @mdgen NODEP: page::plugin + +package require page::plugin ; # S.a. pseudo-package. +package require treeql +package require page::util::quote + +namespace eval ::page::util::norm::peg { + # Get the peg char de/encoder commands. + # (unquote, quote'tcl) + + namespace import ::page::util::quote::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::util::norm::peg {t} { + set q [treeql q -tree $t] + + page_info {[PEG Normalization]} + page_log_info ..Terminals ; peg::Terminals $q $t + page_log_info ..Chains ; peg::CutChains $q $t + page_log_info ..Metadata ; peg::Metadata $q $t + page_log_info ..Definitions ; peg::Definitions $q $t + page_log_info ..Expressions ; peg::Expressions $q $t + + # Sentinel for PE algorithms. + $t set root symbol <StartExpression> + $q destroy + + page_log_info Ok + return +} + +# ### ### ### ######### ######### ######### +## Documentation +# +## See doc_normalize.txt for the specification of the publicly visible +## attributes. +## +## Internal attributes +## - DATA - Transient storage for terminal data. + +# ### ### ### ######### ######### ######### +## Internal. Helpers + +proc ::page::util::norm::peg::Terminals {q t} { + # The data for all terminals is stored in their grandparental + # nodes. We get rid of both terminals and their parents. + + $q query tree withatt type terminal over n { + set p [$t parent $n] + set gp [$t parent $p] + + CopyLocation $t $n $gp + AttrCopy $t $n detail $gp DATA + TokReduce $t $gp DATA + $t delete $p + } + + # We can now drop the type attribute, as all the remaining nodes + # (which have it) will contain the value 'nonterminal'. + + $q query tree hasatt type over n { + $t unset $n type + } + return +} + +proc ::page::util::norm::peg::CutChains {q t} { + # All nodes which have exactly one child are irrelevant. We get + # rid of them. The root node is the sole exception. The immediate + # child of the root however is superfluous as well. + + $q query tree notq {root} over n { + if {[llength [$t children $n]] != 1} continue + $t cut $n + } + + foreach n [$t children root] {$t cut $n} + return +} + +proc ::page::util::norm::peg::Metadata {q t} { + # Having the name of the grammar in a tree node is overkill. We + # move this information into an attribute of the root node. + # The node keeping the start expression separate is irrelevant as + # well. We get rid of it, and tag the root of the start expression + # with a marker attribute. + + $q query tree withatt detail Header over n { + set tmp [Child $t $n 0] + set sexpr [Child $t $n 1] + + AttrCopy $t $tmp DATA root name + $t cut $tmp + $t cut $n + break + } + + # Remember the node for the start expression in the root for quick + # access by later stages. + + $t set root start $sexpr + return +} + +proc ::page::util::norm::peg::Definitions {q t} { + # We move nonterminal hint information from nodes into attributes, + # and delete the now irrelevant nodes. + + # NOTE: This transformation is dependent on the removal of all + # nodes with exactly one child, as it removes the all 'Attribute' + # nodes already. Otherwise this transformation would have to put + # the information into the grandparental node. + + # The default mode for nonterminals is 'value'. + + $q query tree withatt detail Definition over n { + $t set $n mode value + } + + foreach {a mode} { + VOID discard + MATCH match + LEAF leaf + } { + $q query tree withatt detail $a over n { + set p [$t parent $n] + $t set $p mode $mode + $t delete $n + } + } + + # Like with the global metadata we move definition specific + # information out of nodes into attributes, get rid of the + # superfluous nodes, and tag the definition roots with marker + # attributes. + + set defs {} + $q query tree withatt detail Definition over n { + # Define mode information for all nonterminals without an + # explicit specification. We also save the mode information + # from deletion when we redo the definition node. + + set first [Child $t $n 0] + + set sym [$t get $first DATA] + $t set $n symbol $sym + $t set $n label $sym + $t set $n users {} + + # Now determine the range in the input covered by the + # definition. The left extent comes from the terminal for the + # nonterminal symbol it defines. The right extent comes from + # the rightmost child under the definition. While this not an + # expression tree yet the location data is sound already. + + MergeLocations $t $first [Rightmost $t $n] $n + $t unset $n detail + + lappend defs $sym $n + $t cut $first + } + + # We remember a mapping from nonterminal names to their defining + # nodes in the root as well, for quick reference later, when we + # build nonterminal usage references + + $t set root definitions $defs + return +} + +proc ::page::util::norm::peg::Rightmost {t n} { + # Determine the rightmost leaf under the specified node. + + if {[$t isleaf $n]} {return $n} + return [Rightmost $t [lindex [$t children $n] end]] +} + +proc ::page::util::norm::peg::Expressions {q t} { + # We now transform the remaining nodes into proper expression + # trees. The order matters, to shed as much nodes as possible + # early, and to avoid unncessary work. + + ExprRanges $q $t + ExprUnaryOps $q $t + ExprChars $q $t + ExprNonterminals $q $t + ExprOperators $q $t + ExprFlatten $q $t + return +} + +proc ::page::util::norm::peg::ExprRanges {q t} { + # Ranges = .. operator + + $q query tree withatt detail Range over n { + # Two the children, both of text 'Char', their data is what we + # take. The children become irrelevant and are removed. + + foreach {b e} [$t children $n] break + set begin [unquote [$t get $b DATA]] + set end [unquote [$t get $e DATA]] + + $t set $n op .. + $t set $n begin $begin + $t set $n end $end + + MergeLocations $t $b $e $n + + $t unset $n detail + + $t delete $b + $t delete $e + } + return +} + +proc ::page::util::norm::peg::ExprUnaryOps {q t} { + # Unary operators ... Their transformation sheds more nodes. + + foreach {a op} { + QUESTION ? + STAR * + PLUS + + AND & + NOT ! + } { + $q query tree withatt detail $a over n { + set p [$t parent $n] + + $t set $p op $op + $t cut $n + + $t unset $p detail + } + } + return +} + +proc ::page::util::norm::peg::ExprChars {q t} { + # Chars = t operator (The remaining Char'acters are plain terminal + # symbols. + + $q query tree withatt detail Char over n { + set ch [unquote [$t get $n DATA]] + + $t set $n op t + $t set $n char $ch + + $t unset $n detail + $t unset $n DATA + } + return +} + +proc ::page::util::norm::peg::ExprNonterminals {q t} { + # Identifiers = n operator (nonterminal references) ... + + array set defs [$t get root definitions] + array set undefined {} + + $q query tree withatt detail Identifier over n { + set sym [$t get $n DATA] + + $t set $n op n + $t set $n sym $sym + + $t unset $n detail + $t unset $n DATA + + # Create x-references between the users and the definition of + # a nonterminal symbol. + + if {![info exists defs($sym)]} { + $t set $n def {} + lappend undefined($sym) $n + continue + } else { + set def $defs($sym) + $t set $n def $def + } + + set users [$t get $def users] + lappend users $n + $t set $def users $users + } + + $t set root undefined [array get undefined] + return +} + +proc ::page::util::norm::peg::ExprOperators {q t} { + # The remaining operator nodes can be changed directly from node + # text to operator. Se we do. + + foreach {a op} { + EPSILON epsilon + ALNUM alnum + ALPHA alpha + DOT dot + Literal x + Class / + Sequence x + Expression / + } { + $q query tree withatt detail $a over n { + $t set $n op $op + $t unset $n detail + } + } + return +} + +proc ::page::util::norm::peg::ExprFlatten {q t} { + # Last tweaks of the expressions. Classes inside of Expressions, + # and Literals in Sequences create nested / or x expressions. We + # locate such and flatten the nested expression, cutting out the + # superfluous operator. + + foreach op {x /} { + # Locate all x operators, whose parents are x operators as + # well, then go back to the child operators and cut them out. + + $q query tree withatt op $op \ + parent unique withatt op $op \ + children withatt op $op \ + over n { + $t cut $n + } + + # Locate all x operators without children and convert them + # into epsilon operators. Because that is what they accept, + # nothing. + + $q query tree withatt op $op over n { + if {[$t numchildren $n]} continue + $t set $n op epsilon + } + } + return +} + +# ### ### ### ######### ######### ######### +## Internal. Low-level helpers. + +proc ::page::util::norm::peg::CopyLocation {t src dst} { + $t set $dst range [$t get $src range] + $t set $dst range_lc [$t get $src range_lc] + return +} + +proc ::page::util::norm::peg::MergeLocations {t srca srcb dst} { + set ar [$t get $srca range] + set arlc [$t get $srca range_lc] + + set br [$t get $srcb range] + set brlc [$t get $srcb range_lc] + + $t set $dst range [list [lindex $ar 0] [lindex $br 1]] + $t set $dst range_lc [list [lindex $arlc 0] [lindex $brlc 1]] + return +} + +proc ::page::util::norm::peg::TokReduce {t src attr} { + set tokens [$t get $src $attr] + set ch {} + foreach tok $tokens { + lappend ch [lindex $tok 0] + } + $t set $src $attr [join $ch {}] + return +} + +proc ::page::util::norm::peg::AttrCopy {t src asrc dst adst} { + $t set $dst $adst [$t get $src $asrc] + return +} + +proc ::page::util::norm::peg::Child {t n index} { + return [lindex [$t children $n] $index] +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::util::norm::peg 0.1 diff --git a/tcllib/modules/page/util_peg.tcl b/tcllib/modules/page/util_peg.tcl new file mode 100644 index 0000000..172c26e --- /dev/null +++ b/tcllib/modules/page/util_peg.tcl @@ -0,0 +1,209 @@ +# -*- tcl -*- +# ### ### ### ######### ######### ######### + +## This package provides a number of utility commands to +## transformations for common operations. It assumes a 'Normalized PE +## Grammar Tree' as input, possibly augmented with attributes coming +## from transformation not in conflict with the base definition. + +# ### ### ### ######### ######### ######### +## Requisites + +package require page::util::quote + +namespace eval ::page::util::peg { + namespace export \ + symbolOf symbolNodeOf \ + updateUndefinedDueRemoval \ + flatten peOf printTclExpr \ + getWarnings printWarnings + + # Get the peg char de/encoder commands. + # (unquote, quote'tcl). + + namespace import ::page::util::quote::* +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::util::peg::symbolNodeOf {t n} { + # Given an arbitrary root it determines the node (itself or an + # ancestor) containing the name of the nonterminal symbol the node + # belongs to, and returns its id. The result is either the root of + # the tree (for the start expression), or a definition mode. + + while {![$t keyexists $n symbol]} { + set n [$t parent $n] + } + return $n +} + +proc ::page::util::peg::symbolOf {t n} { + # As above, but returns the symbol name. + + return [$t get [symbolNodeOf $t $n] symbol] +} + +proc ::page::util::peg::updateUndefinedDueRemoval {t} { + # The removal of nodes may have caused symbols to lose one or more + # users. Example: A used by B and C, B is reachable, C is not, so A + # now loses a node in the expression for C calling it, or rather + # not anymore. + + foreach {sym def} [$t get root definitions] { + set res {} + foreach u [$t get $def users] { + if {![$t exists $u]} continue + lappend res $u + } + $t set $def users $res + } + + # Update the knowledge of undefined nonterminals. To be used when + # a transformation can remove invokations of undefined symbols, + # and is not able to generate such invokations. + + set res {} + foreach {sym invokers} [$t get root undefined] { + set sres {} + foreach n $invokers { + if {![$t exists $n]} continue + lappend sres $n + } + if {[llength $sres]} { + lappend res $sym $sres + } + } + $t set root undefined $res + return +} + +proc ::page::util::peg::flatten {q t} { + # Flatten nested x-, or /-operators. + # See peg_normalize.tcl, peg::normalize::ExprFlatten + + foreach op {x /} { + # Locate all x operators, whose parents are x oerators as + # well, then go back to the child operators and cut them out. + + $q query \ + tree withatt op $op \ + parent unique withatt op $op \ + children withatt op $op \ + over n { + $t cut $n + } + } + return +} + +proc ::page::util::peg::getWarnings {t} { + # Look at the attributes for problems with the grammar and issue + # warnings. They do not prevent us from writing the grammar, but + # still represent problems with it the user should be made aware + # of. + + array set msg {} + array set undefined [$t get root undefined] + foreach sym [array names undefined] { + set msg($sym) {} + foreach ref $undefined($sym) { + lappend msg($sym) "Undefined symbol used by the definition of '[symbolOf $t $ref]'." + } + } + + foreach {sym def} [$t get root definitions] { + if {[llength [$t get $def users]] == 0} { + set msg($sym) [list "This symbol has been defined, but is not used."] + } + } + + return [array get msg] +} + +proc ::page::util::peg::printWarnings {msg} { + if {![llength $msg]} return + + set dict {} + set max -1 + foreach {k v} $msg { + set l [string length [list $k]] + if {$l > $max} {set max $l} + lappend dict [list $k $v $l] + } + + foreach e [lsort -dict -index 0 $dict] { + foreach {k msgs l} $e break + + set off [string repeat " " [expr {$max - $l}]] + page_info "[list $k]$off : [lindex $msgs 0]" + + if {[llength $msgs] > 1} { + set indent [string repeat " " [string length [list $k]]] + foreach m [lrange $msgs 1 end] { + puts stderr " $indent$off : $m" + } + } + } + return +} + +proc ::page::util::peg::peOf {t eroot} { + set op [$t get $eroot op] + set pe [list $op] + + set ch [$t children $eroot] + + if {[llength $ch]} { + foreach c $ch { + lappend pe [peOf $t $c] + } + } elseif {$op eq "n"} { + lappend pe [$t get $eroot sym] + } elseif {$op eq "t"} { + lappend pe [unquote [$t get $eroot char]] + } elseif {$op eq ".."} { + lappend pe \ + [unquote [$t get $eroot begin]] \ + [unquote [$t get $eroot end]] + + } + return $pe +} + +proc ::page::util::peg::printTclExpr {pe} { + list [PrintExprSub $pe] +} + +# ### ### ### ######### ######### ######### +## Internal + +proc ::page::util::peg::PrintExprSub {pe} { + set op [lindex $pe 0] + set args [lrange $pe 1 end] + + #puts stderr "PE [llength $args] $op | $args" + + if {$op eq "t"} { + set a [lindex $args 0] + return "$op [quote'tcl $a]" + } elseif {$op eq ".."} { + set a [lindex $args 0] + set b [lindex $args 1] + return "$op [quote'tcl $a] [quote'tcl $b]" + } elseif {$op eq "n"} { + return $pe + } else { + set res $op + foreach a $args { + lappend res [PrintExprSub $a] + } + return $res + } +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::util::peg 0.1 diff --git a/tcllib/modules/page/util_quote.tcl b/tcllib/modules/page/util_quote.tcl new file mode 100644 index 0000000..6c7b65e --- /dev/null +++ b/tcllib/modules/page/util_quote.tcl @@ -0,0 +1,173 @@ +# -*- tcl -*- +# +# Copyright (c) 2005 by Andreas Kupries <andreas_kupries@users.sourceforge.net> +# Parser Generator / (Un)quoting characters. + +# ### ### ### ######### ######### ######### +## Requisites + +namespace eval ::page::util::quote { + namespace export unquote \ + quote'tcl quote'tclstr quote'tclcom +} + +# ### ### ### ######### ######### ######### +## API + +proc ::page::util::quote::unquote {ch} { + # A character, as stored in the grammar tree + # by the frontend is transformed into a proper + # Tcl character (internal representation). + + switch -exact -- $ch { + "\\n" {return \n} + "\\t" {return \t} + "\\r" {return \r} + "\\[" {return \[} + "\\]" {return \]} + "\\'" {return '} + "\\\"" {return "\""} + "\\\\" {return \\} + } + + if {[regexp {^\\([0-2][0-7][0-7])$} $ch -> ocode]} { + return [format %c $ocode] + } elseif {[regexp {^\\([0-7][0-7]?)$} $ch -> ocode]} { + return [format %c 0$ocode] + } elseif {[regexp {^\\u([0-9a-fA-F][0-9a-fA-F]?[0-9a-fA-F]?[0-9a-fA-F]?)$} $ch -> hcode]} { + return [format %c 0x$hcode] + } + + return $ch +} + +proc ::page::util::quote::quote'tcl {ch} { + # Converts a Tcl character (internal representation) + # into a string which is accepted by the Tcl parser, + # will regenerate the character in question and is + # 7bit ASCII. 'quoted' is a boolean flag and set if + # the returned representation is a \-quoted form. + # Because they have to be treated specially when + # creating a list containing the reperesentation. + + # Special characters + + switch -exact -- $ch { + "\n" {return "\\n"} + "\r" {return "\\r"} + "\t" {return "\\t"} + "\\" - "\;" - + " " - "\"" - + "(" - ")" - + "\{" - "\}" - + "\[" - "\]" { + # Quote space and all the brackets as well, using octal, + # for easy impure list-ness. + + scan $ch %c chcode + return \\[format %o $chcode] + } + } + + scan $ch %c chcode + + # Control characters: Octal + if {[string is control -strict $ch]} { + return \\[format %o $chcode] + } + + # Beyond 7-bit ASCII: Unicode + + if {$chcode > 127} { + return \\u[format %04x $chcode] + } + + # Regular character: Is its own representation. + + return $ch +} + +proc ::page::util::quote::quote'tclstr {ch} { + # Converts a Tcl character (internal representation) + # into a string which is accepted by the Tcl parser and will + # generate a human readable representation of the character in + # question, one which when puts to a channel describes the + # character without using any unprintable characters. It may use + # \-quoting. High utf characters are quoted to avoid problem with + # the still prevalent ascii terminals. It is assumed that the + # string will be used in a ""-quoted environment. + + # Special characters + + switch -exact -- $ch { + " " {return "<blank>"} + "\n" {return "\\\\n"} + "\r" {return "\\\\r"} + "\t" {return "\\\\t"} + "\"" - "\\" - "\;" - + "(" - ")" - + "\{" - "\}" - + "\[" - "\]" { + return \\$ch + } + } + + scan $ch %c chcode + + # Control characters: Octal + if {[string is control -strict $ch]} { + return \\\\[format %o $chcode] + } + + # Beyond 7-bit ASCII: Unicode + + if {$chcode > 127} { + return \\\\u[format %04x $chcode] + } + + # Regular character: Is its own representation. + + return $ch +} + +proc ::page::util::quote::quote'tclcom {ch} { + # Converts a Tcl character (internal representation) + # into a string which is accepted by the Tcl parser when used + # within a Tcl comment. + + # Special characters + + switch -exact -- $ch { + " " {return "<blank>"} + "\n" {return "\\n"} + "\r" {return "\\r"} + "\t" {return "\\t"} + "\"" - + "\{" - "\}" - + "(" - ")" { + return \\$ch + } + } + + scan $ch %c chcode + + # Control characters: Octal + if {[string is control -strict $ch]} { + return \\[format %o $chcode] + } + + # Beyond 7-bit ASCII: Unicode + + if {$chcode > 127} { + return \\u[format %04x $chcode] + } + + # Regular character: Is its own representation. + + return $ch +} + +# ### ### ### ######### ######### ######### +## Ready + +package provide page::util::quote 0.1 |