XDR parsing in (nearly) pure tcl -- 20040609 CMcCThe following is some code to turn XDR definitions into tcl code to parse and generate XDR binary, sufficient to implement things like the mount.x files which define message flow in NFS.Unfinished, rough.xdr-tcl.l is a ylex/yeti program to lex the XDR definiton language parser
#! /bin/sh
# \
exec itclsh "$0" ${1+"$@"}
# Generate Lexical Analyzer for XDR - xdr-lex.tcl
lappend auto_path /usr/local/lib
package require yeti
package require ylex
set xdr_lex [yeti::ylex \#auto -name xdr_lexer]
$xdr_lex macro \
OCOMM {/[*]} \
CCOMM {[*]/} \
WS {[ \t\f]} \
D {[0-9]} \
LD {[\.0-9]} \
E {[DEde][+-]?[0-9]+} \
IDS {[a-zA-Z]} \
IDCH {[a-zA-Z0-9_.$]} \
INT {[-]?[0-9]+} \
LT {[<]} \
GT {[>]} \
OR {[|]} \
DOT {[.]} \
STAR {[*]} \
CARET {\^} \
LP {[(]} \
RP {[)]} \
STRING {"([^"]|"")*"} \
EOL {\n} \
other {.} \
HASH {#} \
LBRACKET {\[} \
RBRACKET {\]} \
LBRACE {\{} \
RBRACE {\}} \
LP {[(]} \
RP {[)]} \
SEMI {[;]} \
COLON {[:]} \
EQUAL {[=]} \
COMMA {[,]}
$xdr_lex code public {
variable lineno 0
}
$xdr_lex code reset {
set lineno 0
}
$xdr_lex add -state INITIAL <OCOMM> {
#puts stderr "COMMENT"
set yystate COMMENT
}
$xdr_lex add -state COMMENT <CCOMM> {
#puts stderr "END COMMENT"
set yystate INITIAL
}
$xdr_lex add -state COMMENT . {
}
$xdr_lex add -state INITIAL {\n<WS>+} {}
$xdr_lex add -state INITIAL \n {
# ignore new lines - don't combine with white space
incr lineno;
}
$xdr_lex add -state INITIAL <WS>+ {
# ignore white space
}
$xdr_lex add -state INITIAL <INT> {
return [list INT $yytext]
}
# add special character macros
foreach ch {
LBRACE RBRACE LT GT LBRACKET RBRACKET LP RP
COMMA EQUAL SEMI COLON STAR INT
} {
$xdr_lex add -state INITIAL [list <${ch}>] [list return S_$ch]
}
set reserved {
opaque string void
unsigned int hyper float double quadruple bool
enum struct union
switch case default
const typedef
}
#foreach char [split $word {}] {
#append pattern \[[string toupper $char]$char\]
#}
# add reserved words
foreach word $reserved {
$xdr_lex add -state INITIAL -nocase ${word} \
"return \[list S_[string toupper $word]]"
}
$xdr_lex add -state INITIAL <IDCH>+ {
return [list S_ID [string trim $yytext]]
}
# generate the scanner code to stdout
puts [$xdr_lex dump]
delete object $xdr_lexxdr-tcl.y is a yeti file to generate an XDR language parser. #! /bin/sh
# \
exec itclsh "$0" ${1+"$@"}
if { [info script] == "$::argv0" } {
lappend auto_path [pwd]
}
# Lexical analyzer for XDR.
lappend auto_path /usr/local/lib
package require yeti
package require ylex
set xdr_parser [eval yeti::yeti \#auto -name xdr_parser -start specification -verbose 4]
$xdr_parser code public {
method getstate {} {
return [list $yystate $yylhs]
}
public variable yyterm ""
}
$xdr_parser code error {
upvar yyterm yyterm
#puts stderr "Error: $yyerrmsg / $yyterm"
}
$xdr_parser add {
specification {definition specification} {return [list $1 $2]}
| definition {}
definition constdef {}
| typedef {return $1}
constant INT {return $1}
constdef {S_CONST S_ID S_EQUAL constant S_SEMI} {
return [CONST $2 $4]
}
assign {S_ID S_EQUAL value} {return [concat [list $1] [list $3]]}
assignments assign {}
| {assignments S_COMMA assign} {return [concat $1 $3]}
enumbody {S_LBRACE assignments S_RBRACE} {
return $2
}
declarations {declaration S_SEMI} {return [list $1]}
| {declaration S_SEMI declarations} {return [concat [list $1] $3]}
structbody {S_LBRACE declarations S_RBRACE} {
return $2
}
unionbody {S_SWITCH S_LP declaration S_RP S_LBRACE cases S_RBRACE} {
return [concat [list $3] $6]
}
typedef {S_TYPEDEF declaration S_SEMI} {return [SEM TYPEDEF $2]}
| {S_ENUM S_ID enumbody S_SEMI} {return [eval SEM ENUM $2 $3]}
| {S_STRUCT S_ID structbody S_SEMI} {return [eval SEM STRUCT $2 $3]}
| {S_UNION S_ID unionbody S_SEMI} {return [eval SEM UNION $2 $3]}
typespec S_BOOL {return [SCALAR BOOL]}
| S_FLOAT {return [SCALAR FLOAT]}
| S_DOUBLE {return [SCALAR DOUBLE ]}
| S_QUADRUPLE {return [SCALAR QUADRUPLE]}
| S_INT {return [SCALAR INT]}
| S_HYPER {return [SCALAR HYPER]}
| {S_UNSIGNED S_INT} {return [SCALAR INT UNSIGNED]}
| {S_UNSIGNED S_HYPER} {return [SCALAR HYPER UNSIGNED]}
| {S_ENUM enumbody} {return [SEM A_ENUM $2]}
| {S_STRUCT structbody} {return [SEM A_STRUCT $2]}
| {S_UNION unionbody} {return [SEM A_UNION $2]}
| S_ID {return $1}
value constant {return $1}
| S_ID {return [CONST_LOOKUP $1]}
value_or_nil value {return $1}
| {} {return ""}
declaration S_VOID {
return [SEM VOID]
}
| {S_STRING S_ID S_LT value_or_nil S_GT} {
return [eval SEM STRING $2 $4]
}
| {S_OPAQUE S_ID S_LT value_or_nil S_GT} {
return [eval SEM OPAQUE_VECTOR $2 $4]
}
| {S_OPAQUE S_ID S_LBRACKET value S_RBRACKET} {
return [SEM OPAQUE $2 $4]
}
| {typespec S_STAR S_ID} {
return [SEM OPTIONAL $1 $3]
}
| {typespec S_ID S_LT value_or_nil S_GT} {
return [eval SEM TYPE $1 $2 $4]
}
| {typespec S_ID S_LBRACKET value S_RBRACKET} {
return [SEM VECTOR $1 $2 $4]
}
| {typespec S_ID} {
return [SEM DECLARE $2 $1]
}
case_value constant {return $1}
| S_ID {return [list CASE $1]}
case {S_CASE case_value S_COLON declaration S_SEMI} {
return [concat [list $2] [list $4]]
}
defcase {S_DEFAULT S_COLON declaration S_SEMI} {
return [concat "" [list $3]]
}
cases case {
return $1
}
| {case cases} {
return [concat $1 $2]
}
| {case defcase} {
return [concat $1 $2]
}
}
#
# generate the parser code to stdout
#
puts [$xdr_parser dump]
delete object $xdr_parserxdr.tcl is a series of routines to pack/unpack XDR # routines for packing and unpacking fundamental types
proc VOID_pack {s v} {
}
proc VOID_unpack {s v} {
}
proc STRING_pack {s v} {
upvar $v var
upvar $s string
}
proc STRING_unpack {s v} {
upvar $v var
upvar $s string
}
proc OPAQUE_VECTOR_pack {s v} {
upvar $v var
upvar $s string
}
proc OPAQUE_VECTOR_unpack {s v} {
upvar $v var
upvar $s string
}
proc OPAQUE_pack {s v len} {
upvar $v var
upvar $s string
}
proc OPAQUE_unpack {s v len} {
upvar $v var
upvar $s string
}
proc BOOL_pack {s v} {
upvar $v var
upvar $s string
append string [binary format I $var]
}
proc BOOL_unpack {s v} {
upvar $v var
upvar $s string
binary scan $string I var
set string [string $string 4 end]
}
proc INT_pack {s v} {
upvar $v var
upvar $s string
append string [binary format I $var]
}
proc INT_unpack {s v} {
upvar $v var
upvar $s string
binary scan $string I var
set string [string range $string 4 end]
}
proc INT_UNSIGNED_pack {s v} {
upvar $v var
upvar $s string
append string [binary format I $var]
}
proc INT_UNSIGNED_unpack {s v} {
upvar $v var
upvar $s string
binary scan $string I var
set string [string range $string 4 end]
}
proc SHORT_pack {s v} {
upvar $v var
upvar $s string
append string [binary format S $var]
}
proc SHORT_unpack {s v} {
upvar $v var
upvar $s string
binary scan $string S var
set string [string range $string 2 end]
}
proc CHAR_pack {s v} {
upvar $v var
upvar $s string
append string [string index $var 0]
}
proc CHAR_unpack {s v} {
upvar $v var
upvar $s string
set var [string index $string 0]
set string [string range $string 1 end]
}
proc UCHAR_pack {s v} {
upvar $v var
upvar $s string
append string [string index $var 0]
}
proc UCHAR_unpack {s v} {
upvar $v var
upvar $s string
set var [string index $string 0]
set string [string range $string 1 end]
}
proc FLOAT_pack {s v} {
upvar $v var
upvar $s string
}
proc FLOAT_unpack {s v} {
upvar $v var
upvar $s string
}
proc DOUBLE_pack {s v} {
upvar $v var
upvar $s string
error "can't handle DOUBLE"
}
proc DOUBLE_unpack {s v} {
upvar $v var
upvar $s string
set string [string range $string 4 end]
}
proc QUADRUPLE_pack {s v} {
upvar $v var
upvar $s string
error "can't handle QUADRUPLE"
}
proc QUADRUPLE_unpack {s v} {
upvar $v var
upvar $s string
set string [string range $string 8 end]
}
proc HYPER_pack {s v} {
upvar $v var
upvar $s string
error "can't handle HYPER"
}
proc HYPER_unpack {s v} {
upvar $v var
upvar $s string
set string [string range $string 8 end]
}
proc HYPER_UNSIGNED_pack {s v} {
upvar $v var
upvar $s string
error "can't handle UNSIGNED HYPER"
}
proc HYPER_UNSIGNED_unpack {s v} {
upvar $v var
upvar $s string
set string [string range $string 8 end]
}
