# and $DIR/../ext/misc.
#
set topdir [file dir [file dir [file normal $argv0]]]
+
set out stdout
-fconfigure stdout -translation {auto lf}
-puts $out {/* DO NOT EDIT!
+
+set headComment {/* DO NOT EDIT!
** This file is automatically generated by the script in the canonical
** SQLite source tree at tool/mkshellc.tcl. That script combines and
** transforms code from various constituent source files of SQLite into
** source file to help make the command-line program easier to compile.
**
** To modify this program, get a copy of the canonical SQLite source tree,
-** edit the src/shell.c.in" and/or some of the other files that are included
-** by "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
+** edit the src/shell.c.in" and/or some of the other files included by
+** "src/shell.c.in", then rerun the tool/mkshellc.tcl script.
*/}
-set in [open $topdir/src/shell.c.in rb]
+ # -it <inc_type>=<include_filename>
+ # -tcl
+
+set customRun 0
+set infiles {}
+array set incTypes [list "*" {}]
+
+while {[llength $argv] > 0} {
+ set argv [lassign $argv opt]
+ if {[regexp {^-{1,2}((help)|(details))$} $opt ma ho]} {
+ if {$ho eq "help"} { set customRun 2 } else { set customRun 3 }
+ } elseif {[regexp {^-it$} $opt]} {
+ set argv [lassign $argv nextOpt]
+ if {![regexp {^(\w+)=(.+)$} $nextOpt ma k v]} {
+ puts stderr "Get help with --help."
+ exit 1
+ }
+ set incTypes($k) $v
+ puts stderr "Include types not yet implemented or needed." ; exit 1
+ } elseif {$opt eq "-tcl"} {
+ puts stderr "Tcl extension not yet implemented." ; exit 1
+ } elseif {[regexp {^[^-]} $opt]} {
+ lappend infiles $opt
+ set customRun 1
+ } else {
+ puts stderr "Skipping unknown option: $opt"
+ }
+}
+if {[llength $infiles] == 0} {
+ set in [open $topdir/src/shell.c.in rb]
+} else {
+ set infiles [lassign $infiles infile]
+ set in [open $infile rb]
+}
set ::cmd_help [dict create]
set ::cmd_dispatch [dict create]
return $rv
}
-# Perform any input collection or deferred output emits.
-# This function may consume additional lines via hFile.
-# Return number of lines absorbed. A 0 return means the
-# input line lx had no meaning to the shuffle processing,
-# in which case it is emitted as-is.
-proc do_shuffle {hFile lx ostrm} {
+array set ::macroTailREs [list \
+ COLLECT_DISPATCH {^\(\s*([\w\*]+)\s*\)\[} \
+ COLLECT_HELP_TEXT {^\[} \
+ CONDITION_COMMAND {^\(\s*(\w+)\s+([^;]+)\);} \
+ DISPATCH_CONFIG {^\[} \
+ DISPATCHABLE_COMMAND {^\(([\w\? ]+)\)(\S)\s*$} \
+ EMIT_DISPATCH {^\((\d*)\)} \
+ EMIT_HELP_TEXT {^\((\d*)\)} \
+]
+array set ::macroUsages [list \
+ COLLECT_DISPATCH "\[\n <dispatch table entry lines>\n \];" \
+ COLLECT_HELP_TEXT "\[\n <help text lines>\n \];" \
+ CONDITION_COMMAND "( name pp_expr );" \
+ DISPATCH_CONFIG "\[\n <NAME=value lines>\n \];" \
+ DISPATCHABLE_COMMAND "( name args... )\x7B\n <code lines>\n \x7D" \
+ EMIT_DISPATCH "( indent );" \
+ EMIT_HELP_TEXT "( indent );" \
+]
+# RE for early discard of non-macro lines, matching all above keywords
+set ::macroKeywordTailRE {^\s{0,8}((?:(?:CO)|(?:DI)|(?:EM))[A-Z_]+)\M(.+)$}
+
+# All macro processor procs return the count of extra input lines consumed.
+
+proc COLLECT_DISPATCH {hFile tailCapture ostrm} {
+ # Collect dispatch table entries, along with ordering info.
set iAte 0
- if {![regexp {^\s{0,4}[A-Z]+} $lx]} {
- puts $ostrm $lx
- } elseif {[regexp {^COLLECT_HELP_TEXT\[} $lx]} {
- incr iAte
- set help_frag {}
- set lx [gets $hFile]
- while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
- lappend help_frag $lx
- set lx [gets $hFile]
- incr iAte
- }
- incr iAte
- set ::cmd_help [dict merge $::cmd_help [chunkify_help $help_frag]]
- } elseif {[regexp {^\s*DISPATCHABLE_COMMAND\(([\w\? ]+)\)(\S)\s*$} $lx ma args tc]
- && $tc eq "\x7B"} {
- set args [split [regsub {\s+} [string trim $args] " "]]
- incr iAte
- set na [llength $args]
- set cmd [lindex $args 0]
- set naPass [dict get $::dispCfg DC_ARG_COUNT]
- if {$na > $naPass} {
- puts stderr "Bad args: $lx"
+ set cmd [lindex $tailCapture 0]
+ set lx [gets $hFile]
+ while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+ lappend disp_frag $lx
+ set grabCmd [dict get $::dispCfg CMD_CAPTURE_RE]
+ if {![regexp $grabCmd $lx ma dcmd]} {
+ puts stderr "malformed dispatch element:\n $lx"
+ incr ::iShuffleErrors
+ } elseif {$cmd ne "*" && $dcmd ne $cmd} {
+ puts stderr "misdeclared dispatch element:\n $lx"
+ incr ::iShuffleErrors
} else {
- while {$na < $naPass} {
- if {![dict exists $::dispCfg "DC_ARG${na}_DEFAULT"]} {
- puts stderr "Too few args: $lx (need $naPass)"
- incr ::iShuffleErrors
- break
- } else {
- lappend args [subst [dict get $::dispCfg "DC_ARG${na}_DEFAULT"]]
- }
- incr na
- }
- set body {}
- while {![eof $hFile]} {
- set lb [gets $hFile]
- incr iAte
- lappend body $lb
- if {[regexp "^\x7D\\s*\$" $lb]} { break }
- }
- for {set aix 1} {$aix < $na} {incr aix} {
- set av [lindex $args $aix]
- if {$av eq "?"} {
- set ai [expr {$aix + 1}]
- set av [subst [dict get $::dispCfg "DC_ARG${ai}_DEFAULT"]]
- }
- set "arg$aix" $av
- }
- if {$cmd ne "?"} {
- set rsct [dict get $::dispCfg STORAGE_CLASS]
- set rsct "$rsct [dict get $::dispCfg RETURN_TYPE]"
- set argexp [subst [dict get $::dispCfg ARGS_SIGNATURE]]
- set fname [subst [dict get $::dispCfg DISPATCHEE_NAME]]
- set funcOpen "$rsct $fname\($argexp\)\x7B"
- set dispEntry [subst [dict get $::dispCfg DISPATCH_ENTRY]]
- emit_conditionally $cmd [linsert $body 0 $funcOpen] $ostrm
- dict set ::cmd_dispatch $cmd [list $dispEntry]
- }
+ dict set ::cmd_dispatch $dcmd [list $lx]
}
- } elseif {[regexp {^\s*EMIT_HELP_TEXT\((\d*)\)} $lx ma indent]} {
+ set lx [gets $hFile]
incr iAte
- foreach htc [lsort [dict keys $::cmd_help]] {
- emit_conditionally $htc [dict get $::cmd_help $htc] $ostrm
- }
- } elseif {[regexp {^COLLECT_DISPATCH\(\s*([\w\*]+)\s*\)\[} $lx ma cmd]} {
+ }
+ incr iAte
+ return $iAte
+}
+
+proc COLLECT_HELP_TEXT {hFile tailCaptureEmpty ostrm} {
+ # Collect help text table values, along with ordering info.
+ set iAte 0
+ set help_frag {}
+ set lx [gets $hFile]
+ while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+ lappend help_frag $lx
+ set lx [gets $hFile]
incr iAte
+ }
+ incr iAte
+ set ::cmd_help [dict merge $::cmd_help [chunkify_help $help_frag]]
+ return $iAte
+}
+
+proc CONDITION_COMMAND {hFile tailCap ostrm} {
+ # Name a command to be conditionally available, with the condition.
+ condition_command [lindex $tailCap 0] [string trim [lindex $tailCap 1]]
+ return 0
+}
+
+proc DISPATCH_CONFIG {hFile tailCaptureEmpty ostrm} {
+ # Set parameters affecting generated dispatchable command function
+ # signatures and generated dispatch table entries.
+ set iAte 0
+ set def_disp {}
+ set lx [gets $hFile]
+ while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
+ lappend def_disp $lx
set lx [gets $hFile]
- while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
- lappend disp_frag $lx
- set grabCmd [dict get $::dispCfg CMD_CAPTURE_RE]
- if {![regexp $grabCmd $lx ma dcmd]} {
- puts stderr "malformed dispatch element:\n $lx"
- incr ::iShuffleErrors
- } elseif {$cmd ne "*" && $dcmd ne $cmd} {
- puts stderr "misdeclared dispatch element:\n $lx"
+ incr iAte
+ }
+ incr iAte
+ foreach line $def_disp {
+ if {[regexp {^\s*(\w+)=(.+)$} $line ma k v]} {
+ dict set ::dispCfg $k $v
+ }
+ }
+ return $iAte
+}
+
+proc DISPATCHABLE_COMMAND {hFile tailCapture ostrm} {
+ # Generate and emit a function definition, maybe wrapped as set by
+ # CONDITION_COMMAND(), and generate/collect its dispatch table entry.
+ lassign $tailCapture args tc
+ if {$tc ne "\x7B"} {
+ yap_usage "DISPATCHABLE_COMMAND($args)$tc" DISPATCHABLE_COMMAND
+ incr $::iShuffleErrors
+ return 0
+ }
+ set iAte 0
+ set args [split [regsub {\s+} [string trim $args] " "]]
+ incr iAte
+ set na [llength $args]
+ set cmd [lindex $args 0]
+ set naPass [dict get $::dispCfg DC_ARG_COUNT]
+ if {$na > $naPass} {
+ puts stderr "Bad args: $lx"
+ } else {
+ while {$na < $naPass} {
+ if {![dict exists $::dispCfg "DC_ARG${na}_DEFAULT"]} {
+ puts stderr "Too few args: $lx (need $naPass)"
incr ::iShuffleErrors
+ break
} else {
- dict set ::cmd_dispatch $dcmd [list $lx]
+ lappend args [subst [dict get $::dispCfg "DC_ARG${na}_DEFAULT"]]
}
- set lx [gets $hFile]
- incr iAte
- }
- incr iAte
- } elseif {[regexp {^\s*EMIT_DISPATCH\((\d*)\)} $lx ma indent]} {
- incr iAte
- foreach cmd [lsort [dict keys $::cmd_dispatch]] {
- emit_conditionally $cmd [dict get $::cmd_dispatch $cmd] $ostrm $indent
+ incr na
}
- } elseif {[regexp {^CONDITION_COMMAND\(\s*(\w+)\s+([^;]+)\);} $lx ma cmd pp_expr]} {
- incr iAte
- condition_command $cmd [string trim $pp_expr]
- } elseif {[regexp {^DISPATCH_CONFIG\[} $lx]} {
- incr iAte
- set def_disp {}
- set lx [gets $hFile]
- while {![eof $hFile] && ![regexp {^\s*\];} $lx]} {
- lappend def_disp $lx
- set lx [gets $hFile]
+ set body {}
+ while {![eof $hFile]} {
+ set lb [gets $hFile]
incr iAte
+ lappend body $lb
+ if {[regexp "^\x7D\\s*\$" $lb]} { break }
}
- incr iAte
- foreach line $def_disp {
- if {[regexp {^\s*(\w+)=(.+)$} $line ma k v]} {
- dict set ::dispCfg $k $v
+ for {set aix 1} {$aix < $na} {incr aix} {
+ set av [lindex $args $aix]
+ if {$av eq "?"} {
+ set ai [expr {$aix + 1}]
+ set av [subst [dict get $::dispCfg "DC_ARG${ai}_DEFAULT"]]
}
+ set "arg$aix" $av
}
- } else {
+ if {$cmd ne "?"} {
+ set rsct [dict get $::dispCfg STORAGE_CLASS]
+ set rsct "$rsct [dict get $::dispCfg RETURN_TYPE]"
+ set argexp [subst [dict get $::dispCfg ARGS_SIGNATURE]]
+ set fname [subst [dict get $::dispCfg DISPATCHEE_NAME]]
+ set funcOpen "$rsct $fname\($argexp\)\x7B"
+ set dispEntry [subst [dict get $::dispCfg DISPATCH_ENTRY]]
+ emit_conditionally $cmd [linsert $body 0 $funcOpen] $ostrm
+ dict set ::cmd_dispatch $cmd [list $dispEntry]
+ }
+ }
+ return $iAte
+}
+
+proc EMIT_DISPATCH {hFile tailCap ostrm} {
+ # Emit the collected dispatch table entries, in command order, maybe
+ # wrapped with a conditional construct as set by CONDITION_COMMAND().
+ foreach cmd [lsort [dict keys $::cmd_dispatch]] {
+ emit_conditionally $cmd [dict get $::cmd_dispatch $cmd] $ostrm $tailCap
+ }
+ return 0
+}
+
+proc EMIT_HELP_TEXT {hFile tailCap ostrm} {
+ # Emit the collected help text table entries, in command order, maybe
+ # wrapped with a conditional construct as set by CONDITION_COMMAND().
+ foreach htc [lsort [dict keys $::cmd_help]] {
+ emit_conditionally $htc [dict get $::cmd_help $htc] $ostrm $tailCap
+ }
+ return 0
+}
+
+proc say_usage {macros {extra {}}} {
+ puts stderr "Usage:$extra"
+ foreach m $macros {puts stderr " $m$::macroUsages($m)"}
+}
+proc yap_usage {got macro} {
+ puts stderr "Bad macro use: $got"
+ say_usage $macro
+}
+
+# Perform any input collection or deferred output emits.
+# This function may consume additional lines via hFile.
+# Return number of lines absorbed. A 0 return means the
+# input line lx had no meaning to the shuffle processing,
+# in which case it is emitted as-is.
+proc do_shuffle {hFile lx ostrm} {
+ set iAte 0
+ if {![regexp $::macroKeywordTailRE $lx ma macro tail] \
+ || ![info exists ::macroTailREs($macro)]} {
puts $ostrm $lx
+ } else {
+ # It's an attempted macro invocation line. Process or fail and yap.
+ incr iAte ; # Eat the macro and whatever it swallows (if invoked).
+ set tailCap [regexp -inline $::macroTailREs($macro) $tail]
+ if {[llength $tailCap]>0} {
+ # Call like-named proc with any args captured by the corresponding RE.
+ incr iAte [$macro $hFile [lrange $tailCap 1 end] $ostrm]
+ } else {
+ # ToDo: complain
+ incr $::iShuffleErrors
+ }
}
return $iAte
}
return [string map [list __declspec(dllexport) {}] $line]
}
+if {$customRun == 2} {
+ # Show options and usage
+ say_usage [lsort [array names ::macroUsages]] {
+ mkshellc.tcl <options>
+ <options> may be either --help, --details, or any sequence of:
+ <input_filename>
+ -it <inc_type>=<include_filename>
+ -tcl
+ If no input files are specified, <PROJECT_ROOT>/src/shell.c.in will be
+ read. Input files are read and processed, producing output to sdout.
+ They may include macro lines or line sequences matching any of:
+ INCUDE <file_name>
+ INCUDE(<inc_type>) }
+ puts stderr { Use --details option for effects of these macros.}
+ exit 0
+} elseif {$customRun == 3} {
+ set sfd [open $argv0 r]
+ while {![eof $sfd]} {
+ if {[regexp {^proc ([A-Z_]+\M)} [gets $sfd] ma macro]} {
+ if {[info exists ::macroTailREs($macro)]} {
+ set effects {}
+ while {[regexp {^\s+#\s*(.+)$} [gets $sfd] ma effect]} {
+ lappend effects " $effect"
+ }
+ puts stderr "\nThe $macro macro will:"
+ puts stderr [join $effects "\n"]
+ }
+ }
+ }
+ close $sfd
+ exit 0
+}
+
+fconfigure stdout -translation {auto lf}
+if {$customRun == 0} {
+ puts $out $headComment
+}
+
set iLine 0
while {1} {
set lx [transform_line [gets $in] 0]
}
set iAte [do_shuffle $in $lx $out]
if {$iAte > 0} {
+if {![regexp {^\d+$} $iAte]} {puts "??? $iAte"}
incr iLine [expr {$iAte - 1}]
}
}
-close $in
+if {$customRun < 2} {
+ close $in
+}
close $out
exit $::iShuffleErrors