Error processing request

Parameters

CONTENT_LENGTH0
REQUEST_METHODGET
REQUEST_URI/revision/Options+and+Arguments?V=2
QUERY_STRINGV=2
CONTENT_TYPE
DOCUMENT_URI/revision/Options+and+Arguments
DOCUMENT_ROOT/var/www/nikit/nikit/nginx/../docroot
SCGI1
SERVER_PROTOCOLHTTP/1.1
HTTPSon
REMOTE_ADDR172.69.58.31
REMOTE_PORT16268
SERVER_PORT4443
SERVER_NAMEwiki.tcl-lang.org
HTTP_HOSTwiki.tcl-lang.org
HTTP_CONNECTIONKeep-Alive
HTTP_ACCEPT_ENCODINGgzip, br
HTTP_X_FORWARDED_FOR3.140.185.147
HTTP_CF_RAY87bf95595cb81230-ORD
HTTP_X_FORWARDED_PROTOhttps
HTTP_CF_VISITOR{"scheme":"https"}
HTTP_ACCEPT*/*
HTTP_USER_AGENTMozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; ClaudeBot/1.0; [email protected])
HTTP_CF_CONNECTING_IP3.140.185.147
HTTP_CDN_LOOPcloudflare
HTTP_CF_IPCOUNTRYUS

Body


Error

Unknow state transition: LINE -> END

-code

1

-level

0

-errorstack

INNER {returnImm {Unknow state transition: LINE -> END} {}} CALL {my render_wikit {Options and Arguments} {Supporting options like the core commands do is a common itch for Tcl'ers, as evidenced by the number of pages in [Category Argument Processing]!  Here's a version I found recently in an old code folder, dusted off and have adopted as my go-to.  The main features are that its use should be obvious, good error messages are generated and you don't end up writing a [proc] wrapper that takes two or more multi-line arguments.

Here's a demo:
======
proc what {args} {
    options {{-loud} {-colour red green blue black} {-count 5}}
    arguments {this {that {}} args}
    foreach name [info locals] {
        puts "$name = [set $name]"
    }
}
======

And the module, which is hopefully short enough to act as its own documentation (and encouragement to extend!).
======
# commented sections are questionable support for validation of arguments (not opts - conflicts with multi-value form).

namespace eval options {

    proc options {args} {
        while {[llength $args] > 1} {
            if {[string match [lindex $args 0]* -arrayvariable]} {
                set args [lassign $args _ value]
                set name -arrayvariable
                set options($name) $value
            } else {
                error "Unknown option \"[lindex $args 0]\": must be one of -arrayvariable"
            }
        }
        if {[info exists options(-arrayvariable)]} {
            set upset [format {apply {{name value} {
                    uplevel 2 [list set %s(-$name) $value]
            }}} $value]
        } else {
            set upset [format {apply {{name value} {
                    uplevel 2 [list set $name $value]
            }}}]
        }
        # parse optspec
        set spec [lindex $args 0]
        foreach optspec $spec {
            set name [lindex $optspec 0]
            switch [llength $optspec] {
                1 {
                    dict set opts $name type 0 ;# flag
                    {*}$upset [string range $name 1 end] 0
                    #dict set opts $name value 0
                } 
                2 {
                    dict set opts $name type 1 ;# arbitrary value
                    dict set opts $name default [lindex $optspec 1]
                    {*}$upset [string range $name 1 end] [lindex $optspec 1]
                    #dict set opts $name value [lindex $optspec 1]
                }
                default {
                    dict set opts $name type 2 ;# choice
                    dict set opts $name default [lindex $optspec 1]
                    dict set opts $name values [lrange $optspec 1 end]
                    {*}$upset [string range $name 1 end] [lindex $optspec 1]
                }
            }
        }
        # get caller's args
        upvar 1 args argv
        for {set i 0} {$i<[llength $argv]} {} {
            set arg [lindex $argv $i]
            if {![string match -* $arg]} {
                break
            }
            incr i
            if {$arg eq "--"} {
                break
            }
            set candidates [dict filter $opts key $arg*]
            switch [dict size $candidates] {
                0 {
                    return -code error -level 2 "Unknown option $arg: must be one of [dict keys $opts]"
                }
                1 {
                    dict for {name spec} $candidates {break}
                    set name [string range $name 1 end]
                    dict with spec {} ;# look out
                    if {$type==0} {
                        set value 1
                    } else {
                        if {[llength $argv]<($i+1)} {
                            return -code error -level 2 "Option $name requires a value"
                        }
                        set value [lindex $argv $i]
                        if {$type==2} {
                            set is [lsearch -all -glob $values $value*]
                            switch [llength $is] {
                                1 {
                                    set value [lindex $values $is]
                                }
                                0 {
                                    return -code error -level 2 "Bad $name \"$value\": must be one of $values"
                                }
                                default {
                                    return -code error -level 2 "Ambiguous $name \"$value\": could be any of [lmap i $is {lindex $values $i}]"
                                }
                            }
                        }
                        incr i
                    }
                    {*}$upset $name $value
                }
                default {
                    return -code error -level 2 "Ambiguous option $arg: maybe one of [dict keys $candidates]"
                }
            }
        }
        set argv [lrange $argv $i end]
    }

    proc formatArgspec {argspec} {
        foreach arg $argspec {
            if {[llength $arg]>1} {
                lappend res "?[lindex $arg 0]?"
            } elseif {$arg eq "args"} {
                lappend res "?args ...?"
            } else {
                lappend res $arg
            }
        }
        return [join $res " "]
    }

    proc arguments {argspec} {
        upvar 1 args argv
        for {set i 0} {$i<[llength $argv]} {incr i} {
            if {$i >= [llength $argspec]} {
                return -code error -level 2 "wrong # args: should be \"[lindex [info level -1] 0] [formatArgspec $argspec]\""
            }
            set name [lindex $argspec $i 0]
            if {$name eq "args"} {
                uplevel 1 [list set args [lrange $argv $i end]]
                return
            }
            set value [lindex $argv $i]
#            set test [lindex $argspec $i 2]
#            if {$test != ""} {
#                set valid [uplevel 1 $test $value]
#                if {!$value} {
#                    return -code error -level 2 "Invalid $name \"$value\", must be $test"
#                }
#            }
        }
        # defaults:
        for {} {$i < [llength $argspec]} {incr i} {
            set as [lindex $argspec $i]
            if {[llength $as]==1} {
                if {$as ne "args"} {
                    return -code error -level 2 "wrong # args: should be \"[lindex [info level -1] 0] [formatArgspec $argspec]\""
                }
                upvar 1 args args
                set args [lrange $argv $i end]
                return
            }
            lassign $as name value
#            set test [lindex $argspec $i 2]
#            if {$test != ""} {
#                set valid [uplevel 1 $test $value]
#                if {!$value} {
#                    return -code error -level 2 "Invalid $name \"$value\", must be $test"
#                }
#            }
        }
        uplevel 1 [list set $name $value]
    }

    namespace export options arguments
}

namespace import options::*
<<categories>> Argument Processing} regexp2} CALL {my render {Options and Arguments} {Supporting options like the core commands do is a common itch for Tcl'ers, as evidenced by the number of pages in [Category Argument Processing]!  Here's a version I found recently in an old code folder, dusted off and have adopted as my go-to.  The main features are that its use should be obvious, good error messages are generated and you don't end up writing a [proc] wrapper that takes two or more multi-line arguments.

Here's a demo:
======
proc what {args} {
    options {{-loud} {-colour red green blue black} {-count 5}}
    arguments {this {that {}} args}
    foreach name [info locals] {
        puts "$name = [set $name]"
    }
}
======

And the module, which is hopefully short enough to act as its own documentation (and encouragement to extend!).
======
# commented sections are questionable support for validation of arguments (not opts - conflicts with multi-value form).

namespace eval options {

    proc options {args} {
        while {[llength $args] > 1} {
            if {[string match [lindex $args 0]* -arrayvariable]} {
                set args [lassign $args _ value]
                set name -arrayvariable
                set options($name) $value
            } else {
                error "Unknown option \"[lindex $args 0]\": must be one of -arrayvariable"
            }
        }
        if {[info exists options(-arrayvariable)]} {
            set upset [format {apply {{name value} {
                    uplevel 2 [list set %s(-$name) $value]
            }}} $value]
        } else {
            set upset [format {apply {{name value} {
                    uplevel 2 [list set $name $value]
            }}}]
        }
        # parse optspec
        set spec [lindex $args 0]
        foreach optspec $spec {
            set name [lindex $optspec 0]
            switch [llength $optspec] {
                1 {
                    dict set opts $name type 0 ;# flag
                    {*}$upset [string range $name 1 end] 0
                    #dict set opts $name value 0
                } 
                2 {
                    dict set opts $name type 1 ;# arbitrary value
                    dict set opts $name default [lindex $optspec 1]
                    {*}$upset [string range $name 1 end] [lindex $optspec 1]
                    #dict set opts $name value [lindex $optspec 1]
                }
                default {
                    dict set opts $name type 2 ;# choice
                    dict set opts $name default [lindex $optspec 1]
                    dict set opts $name values [lrange $optspec 1 end]
                    {*}$upset [string range $name 1 end] [lindex $optspec 1]
                }
            }
        }
        # get caller's args
        upvar 1 args argv
        for {set i 0} {$i<[llength $argv]} {} {
            set arg [lindex $argv $i]
            if {![string match -* $arg]} {
                break
            }
            incr i
            if {$arg eq "--"} {
                break
            }
            set candidates [dict filter $opts key $arg*]
            switch [dict size $candidates] {
                0 {
                    return -code error -level 2 "Unknown option $arg: must be one of [dict keys $opts]"
                }
                1 {
                    dict for {name spec} $candidates {break}
                    set name [string range $name 1 end]
                    dict with spec {} ;# look out
                    if {$type==0} {
                        set value 1
                    } else {
                        if {[llength $argv]<($i+1)} {
                            return -code error -level 2 "Option $name requires a value"
                        }
                        set value [lindex $argv $i]
                        if {$type==2} {
                            set is [lsearch -all -glob $values $value*]
                            switch [llength $is] {
                                1 {
                                    set value [lindex $values $is]
                                }
                                0 {
                                    return -code error -level 2 "Bad $name \"$value\": must be one of $values"
                                }
                                default {
                                    return -code error -level 2 "Ambiguous $name \"$value\": could be any of [lmap i $is {lindex $values $i}]"
                                }
                            }
                        }
                        incr i
                    }
                    {*}$upset $name $value
                }
                default {
                    return -code error -level 2 "Ambiguous option $arg: maybe one of [dict keys $candidates]"
                }
            }
        }
        set argv [lrange $argv $i end]
    }

    proc formatArgspec {argspec} {
        foreach arg $argspec {
            if {[llength $arg]>1} {
                lappend res "?[lindex $arg 0]?"
            } elseif {$arg eq "args"} {
                lappend res "?args ...?"
            } else {
                lappend res $arg
            }
        }
        return [join $res " "]
    }

    proc arguments {argspec} {
        upvar 1 args argv
        for {set i 0} {$i<[llength $argv]} {incr i} {
            if {$i >= [llength $argspec]} {
                return -code error -level 2 "wrong # args: should be \"[lindex [info level -1] 0] [formatArgspec $argspec]\""
            }
            set name [lindex $argspec $i 0]
            if {$name eq "args"} {
                uplevel 1 [list set args [lrange $argv $i end]]
                return
            }
            set value [lindex $argv $i]
#            set test [lindex $argspec $i 2]
#            if {$test != ""} {
#                set valid [uplevel 1 $test $value]
#                if {!$value} {
#                    return -code error -level 2 "Invalid $name \"$value\", must be $test"
#                }
#            }
        }
        # defaults:
        for {} {$i < [llength $argspec]} {incr i} {
            set as [lindex $argspec $i]
            if {[llength $as]==1} {
                if {$as ne "args"} {
                    return -code error -level 2 "wrong # args: should be \"[lindex [info level -1] 0] [formatArgspec $argspec]\""
                }
                upvar 1 args args
                set args [lrange $argv $i end]
                return
            }
            lassign $as name value
#            set test [lindex $argspec $i 2]
#            if {$test != ""} {
#                set valid [uplevel 1 $test $value]
#                if {!$value} {
#                    return -code error -level 2 "Invalid $name \"$value\", must be $test"
#                }
#            }
        }
        uplevel 1 [list set $name $value]
    }

    namespace export options arguments
}

namespace import options::*
<<categories>> Argument Processing}} CALL {my revision {Options and Arguments}} CALL {::oo::Obj3142165 process revision/Options+and+Arguments} CALL {::oo::Obj3142163 process}

-errorcode

NONE

-errorinfo

Unknow state transition: LINE -> END
    while executing
"error $msg"
    (class "::Wiki" method "render_wikit" line 6)
    invoked from within
"my render_$default_markup $N $C $mkup_rendering_engine"
    (class "::Wiki" method "render" line 8)
    invoked from within
"my render $name $C"
    (class "::Wiki" method "revision" line 31)
    invoked from within
"my revision $page"
    (class "::Wiki" method "process" line 56)
    invoked from within
"$server process [string trim $uri /]"

-errorline

4