Marpa

Artifact [867ac842b1]
Login

Artifact [867ac842b1]

Bounty program for improvements to Tcl and certain Tcl packages.

Artifact 867ac842b1ffd0e8bf5c9caf9fd33fbefa1f4755:


     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
   100
   101
   102
   103
   104
   105
   106
   107
   108
   109
   110
   111
   112
   113
   114
   115
   116
   117
   118
   119
   120
   121
   122
   123
   124
   125
   126
   127
   128
   129
   130
   131
   132
   133
   134
   135
   136
   137
   138
   139
   140
   141
   142
   143
   144
   145
   146
   147
   148
   149
   150
   151
   152
   153
   154
   155
   156
   157
   158
   159
   160
   161
   162
   163
   164
   165
   166
   167
   168
   169
   170
   171
   172
   173
   174
   175
   176
   177
   178
   179
   180
   181
   182
   183
   184
   185
   186
   187
   188
   189
   190
   191
   192
   193
   194
   195
   196
   197
   198
   199
   200
   201
   202
   203
   204
   205
   206
   207
   208
   209
   210
   211
   212
   213
   214
   215
   216
   217
   218
   219
   220
   221
   222
   223
   224
   225
   226
   227
   228
   229
   230
   231
   232
   233
   234
   235
   236
   237
   238
   239
   240
   241
   242
   243
   244
   245
   246
   247
   248
   249
   250
   251
   252
   253
   254
   255
   256
   257
   258
   259
   260
   261
   262
   263
   264
   265
   266
   267
   268
   269
   270
   271
   272
   273
   274
   275
   276
   277
   278
   279
   280
   281
   282
   283
   284
   285
   286
   287
   288
   289
   290
   291
   292
# -*- tcl -*-
##
# (c) 2017 Andreas Kupries http://wiki.tcl.tk/andreas%20kupries
#                          http://core.tcl.tk/akupries/
##
# This code is BSD-licensed.

# SLIF support. Transformation of precedenced priority rules into
#               non-precedenced form.
##
# See doc/precedence-rewrite
# See doc.1/facts-precedence.txt
    
# # ## ### ##### ######## #############
## Administrivia

# @@ Meta Begin
# Package marpa::slif::precedence 0
# Meta author      {Andreas Kupries}
# Meta category    {Parser/Lexer Generator}
# Meta description Part of TclMarpa. Grammar transformer.
# Meta description Rewrites precedenced priority rules into sets where
# Meta description the precedence is directly encoded in the structure
# Meta description of the rules
# Meta location    http:/core.tcl.tk/akupries/marpa
# Meta platform    tcl
# Meta require     {Tcl 8.5}
# Meta require     TclOO
# Meta require     debug
# Meta require     debug::caller
# Meta require     marpa::util
# Meta subject     marpa transform precedence
# @@ Meta End

# Notes
# - Precedences are integers <= 0.
# - The tightest precedence is 0.
# - The loosest  precedence is some K <= 0
#
# - tighter x --> x+1 for x < 0, 0 for x == 0
# - looser  x --> x-1 for K < x, K for x == K
#
# Assumptions:
# - E's loosest precedence is K < 0
# - E is the LHS of the precedenced rules
# - E is recursive, i.e. appears in at least one RHS
#
# Rewrite rules
##
# 1. Replace E with
#	(( E ::= E[K] ))
#
# 2. Add
#	(( E[x] ::= E[tighter(x)] ))
#	for all x in [K...1]
#
# 3. Add
#	(( E[x] ::= ... E[x] ... E[tighter(x)] ... ))
#	for (( E ::= ... E ... E ... ))
#	[left-assoc, at precedence x]
#
# 4. Add
#	(( E[x] ::= ... E[tighter(x)] ... E[x] ... ))
#	for (( E ::= ... E ... E ... ))
#	[right-assoc, at precedence x]
#
# 5. Add
#	(( E[x] ::= ... E[K] ... ))	/f.a E in the input
#	for (( E ::= ... E ... ))
#	    [group-assoc, at precedence x]
#
# Notes on the priority rules in the result:
# - All are at precedence level 0, assoc "left".
# - In case of L0 the attributes action, mask and assoc are
#   left out, otherwise see below.
# - All from (3,4,5) have the
#   -	action,
#   -	mask, and
#   -	name
#   of their origin rule.
# - All from (1, 2) have
#   -	action {special hide}, --  A form of `first` which prevents the
#                                \ symbol from making its own AST node.
#   -	mask {0}, and
#   -	no name.

# In generated symbols, i.e. FOO((x)) for origin FOO at level x the
# levels are inverted (0-x), stting -K the loosest precedence.
# Precedence 0 keeps being the tightest. This is easier to read, while
# the negative precedence levels were easier to generate from within
# the semantics.

# Thinking about this now I am not sure what I was thinking at the
# time. Incrementing up to mean `looser precedence` should work just
# as well. <-- Consider FUTURE TODO to redo this part of the
# semantics, and here, of course.

# # ## ### ##### ######## #############
## Requisites

package require Tcl 8.5
package require TclOO         ;# Implies Tcl 8.5 requirement.
package require debug
package require debug::caller
package require marpa::util

debug define marpa/slif/precedence

# # ## ### ##### ######## #############

namespace eval ::marpa::slif::precedence {
    namespace export rewrite rewrite1 2container
    namespace ensemble create
    namespace import ::marpa::X
}

# # ## ### ##### ######## #############
## Public API

proc ::marpa::slif::precedence::rewrite {container} {
    debug.marpa/slif/precedence {}
    set commands {}
    foreach layer {g1 l0} {
	foreach sym [$container $layer symbols] {
	    # Without an actual range of precedences a rewrite is not
	    # worth it. This also automatically exludes the trivially
	    # non-recursive rules (atoms and quantified) from
	    # consideration.
	    set min_p [$container $layer min-precedence $sym]
	    if {$min_p == 0} continue

	    # Exclude priority rules which are not recursive.  This is
	    # a more expensive check, iterating over all the
	    # alternates and their elements, thus why it is the second
	    # check after the easier test for a precedence range.
	    if {![$container $layer recursive $sym]} continue

	    lappend commands {*}[rewrite1 $layer $sym $min_p [$container $layer get $sym]]
	}
    }
    return $commands
}

proc ::marpa::slif::precedence::2container {rewrite container} {
    debug.marpa/slif/precedence {}
    foreach command $rewrite {
	$container {*}$command
    }
    return
}

proc ::marpa::slif::precedence::rewrite1 {grcode lhs min alternatives} {
    debug.marpa/slif/precedence {}
    # alternatives :: list(alter)
    # alter        :: tuple(rhs precedence ...)
    # rhs          :: list(symbol)
    # ...          :: {*}dict ( name action assoc mask )
    # action, assoc, mask are G1 only
    # assoc defaults to "left".

    # result :: list(commands)

    set result {}
    R $lhs

    # Note:
    # - "attr" is used by "P" to fill in the attributes of the new
    #   priority rules.

    set smin [S $min]
    set attr [list action {special hide} mask {0}]

    # Rewrite (1) - Ground
    P $lhs [list $smin]

    # Rewrite (2) - Spine
    for {
	set current $min
	set tighter [expr {$min + 1}]
    } {$current < 0} {
	incr current
	incr tighter} {
	P [S $current] [list [S $tighter]]
    }

    # Rewrite (3-5) - Rules per assoc type
    foreach alter $alternatives {
	P {*}[MapAlter $lhs $alter $smin]
    }

    return $result
}

# # ## ### ##### ######## #############
## Internal helpers

proc ::marpa::slif::precedence::MapAlter {lhs alter smin} {
    debug.marpa/slif/precedence {}
    # Export to caller for import into the following P.
    upvar 1 attr attr

    set attr [lassign $alter __type__ rhs precedence]
    if {[dict exists $attr assoc]} {
	set assoc [dict get $attr assoc]
    } else {
	set assoc left
    }

    set scurrent [S $precedence]
    set stighter [S [expr {min($precedence + 1,0)}]]

    switch -exact -- $assoc {
	left {
	    # Rewrite (3)
	    # E[x] ::= ... E[x] ... E[tighter(x)]...
	    set newrhs [MapFirst $rhs $lhs $scurrent $stighter]
	}
	right {
	    # Rewrite (4)
	    # E[x] ::= ... E[tighter(x)] ... E[x]...
	    # Like left, but in reverse.
	    set newrhs [lreverse [MapFirst [lreverse $rhs] $lhs $scurrent $stighter]]
	}
	group {
	    # Rewrite (5)
	    # E[x] ::= ... E[K] ..., for all E in rhs
	   set newrhs [MapAll $rhs $lhs $smin]
       }
	default {
	    X "Bad assocation $assoc" SLIF PRECEDENCE BAD ASSOC
	}
    }
    return [list $scurrent $newrhs]
}

proc ::marpa::slif::precedence::MapFirst {rhs lhs newfirst newplus} {
    debug.marpa/slif/precedence {}
    set new $newfirst
    return [lmap sym $rhs {
	if {$sym eq $lhs} {
	    set res $new
	    set new $newplus
	    set res
	} else {
	    set sym
	}
    }]
}

proc ::marpa::slif::precedence::MapAll {rhs lhs new} {
    debug.marpa/slif/precedence {}
    return [lmap sym $rhs {
	if {$sym eq $lhs} {
	    set new
	} else {
	    set sym
	}
    }]
}

proc ::marpa::slif::precedence::S {k} {
    debug.marpa/slif/precedence {}
    upvar 1 lhs lhs
    return "${lhs}(([expr {-$k}]))"
}

proc ::marpa::slif::precedence::R {sym} {
    debug.marpa/slif/precedence {}
    upvar 1 grcode grcode result result
    lappend result [list $grcode remove $sym]
    return
}

proc ::marpa::slif::precedence::P {sym rhs} {
    debug.marpa/slif/precedence {}
    upvar 1 grcode grcode result result attr attr

    set newattr $attr
    dict set newattr assoc left
    if {$grcode eq "l0"} {
	dict unset newattr action
	dict unset newattr mask
	dict unset newattr assoc
    }

    lappend result [list $grcode priority-rule $sym $rhs 0 {*}$newattr]
    return
}

# # ## ### ##### ######## #############
package provide marpa::slif::precedence 0
return