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
|
# -*- 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
|