##
# \file
#
# \author W T Schueller
#
# \brief The lexer
#
# The lexer scans input for known tokens.
# \ref parser.tab.tcl lists the known tokens.
#
namespace eval yy {

######
# Begin autogenerated fickle (version 2.04) routines.
# Although fickle itself is protected by the GNU Public License (GPL)
# all user-supplied functions are protected by their respective
# author's license.  See http://mini.net/tcl/fickle for other details.
######

# If yywrap() returns false (zero), then it is assumed that the
# function has gone ahead and set up yyin to point to another input
# file, and scanning continues.  If it returns true (non-zero), then
# the scanner terminates, returning 0 to its caller.  Note that in
# either case, the start condition remains unchanged; it does not
# revert to INITIAL.
#   -- from the flex(1) man page
proc yywrap {} {
    return 1
}

# ECHO copies yytext to the scanner's output if no arguments are
# given.  The scanner writes its ECHO output to the yyout global
# (default, stdout), which may be redefined by the user simply by
# assigning it to some other channel.
#   -- from the flex(1) man page
proc ECHO {{s ""}} {
    variable yyout
    variable yytext
    if {$s == ""} {
        puts -nonewline $yyout $yytext
    } else {
        puts -nonewline $yyout $s
    }
}

# YY_FLUSH_BUFFER flushes the scanner's internal buffer so that the
# next time the scanner attempts to match a token, it will first
# refill the buffer using YY_INPUT.
#   -- from the flex(1) man page
proc YY_FLUSH_BUFFER {} {
    variable yy_buffer ""
    variable yy_index 0
    variable yy_done 0
}

# yyrestart(new_file) may be called to point yyin at the new input
# file.  The switch-over to the new file is immediate (any previously
# buffered-up input is lost).  Note that calling yyrestart with yyin
# as an argument thus throws away the current input buffer and
# continues scanning the same input file.
#   -- from the flex(1) man page
proc yyrestart {new_file} {
    variable yyin $new_file
    YY_FLUSH_BUFFER
}

# The nature of how it gets its input can be controlled by defining
# the YY_INPUT macro.  YY_INPUT's calling sequence is
# "YY_INPUT(buf,result,max_size)".  Its action is to place up to
# max_size characters in the character array buf and return in the
# integer variable result either the number of characters read or the
# constant YY_NULL (0 on Unix systems) to indicate EOF.  The default
# YY_INPUT reads from the global file-pointer "yyin".
#   -- from the flex(1) man page
proc YY_INPUT {buf result max_size} {
    upvar $result ret_val
    upvar $buf new_data
    variable yyin
    if {$yyin != ""} {
        set new_data [read $yyin $max_size]
        set ret_val [string length $new_data]
    } else {
        set new_data ""
        set ret_val 0
    }
}

# yy_scan_string sets up input buffers for scanning in-memory
# strings instead of files.  Note that switching input sources does
# not change the start condition.
#   -- from the flex(1) man page
proc yy_scan_string {str} {
    variable yy_buffer
    append yy_buffer $str
    variable yyin ""
}

# unput(c) puts the character c back onto the input stream.  It will
# be the next character scanned.
#   -- from the flex(1) man page
proc unput {c} {
    variable yy_buffer
    variable yy_index
    set s [string range $yy_buffer 0 [expr {$yy_index - 1}]]
    append s $c
    set yy_buffer [append s [string range $yy_buffer $yy_index end]]
}

# Returns all but the first n characters of the current token back to
# the input stream, where they will be rescanned when the scanner
# looks for the next match.  yytext and yyleng are adjusted
# appropriately.
#   -- from the flex(1) man page
proc yyless {n} {
    variable yy_buffer
    variable yy_index
    variable yytext
    variable yyleng
    set s [string range $yy_buffer 0 [expr {$yy_index - 1}]]
    append s [string range $yytext $n end]
    set yy_buffer [append s [string range $yy_buffer $yy_index end]]
    set yytext [string range $yytext 0 [expr {$n - 1}]]
    set yyleng [string length $yytext]
}

# input() reads the next character from the input stream.
#   -- from the flex(1) man page
proc input {} {
    variable yy_buffer
    variable yy_index
    variable yy_done
    if {[string length $yy_buffer] - $yy_index < 1024} {
       set new_buffer_size 0
       if {$yy_done == 0} {
           YY_INPUT new_buffer new_buffer_size 1024
           append yy_buffer $new_buffer
           if {$new_buffer_size == 0} {
               set yy_done 1
           }
       }
       if $yy_done {
           if {[yywrap] == 0} {
               return [input]
           } elseif {[string length $yy_buffer] - $yy_index == 0} {
               return {}
           }
        }
    }
    set c [string index $yy_buffer $yy_index]
    incr yy_index
    return $c
}

# Pushes the current start condition onto the top of the start
# condition stack and switches to new_state as though you had used
# BEGIN new_state.
#   -- from the flex(1) man page
proc yy_push_state {new_state} {
    variable yy_state_stack
    lappend yy_state_stack $new_state
}

# Pops off the top of the state stack; if the stack is now empty, then
# pushes the state "INITIAL".
#   -- from the flex(1) man page
proc yy_pop_state {} {
    variable yy_state_stack
    set yy_state_stack [lrange $yy_state_stack 0 end-1]
    if {$yy_state_stack == ""} {
        yy_push_state INITIAL
    }
}

# Returns the top of the stack without altering the stack's contents.
#   -- from the flex(1) man page
proc yy_top_state {} {
    variable yy_state_stack
    return [lindex $yy_state_stack end]
}

# BEGIN followed by the name of a start condition places the scanner
# in the corresponding start condition. . . .Until the next BEGIN
# action is executed, rules with the given start condition will be
# active and rules with other start conditions will be inactive.  If
# the start condition is inclusive, then rules with no start
# conditions at all will also be active.  If it is exclusive, then
# only rules qualified with the start condition will be active.
#   -- from the flex(1) man page
proc BEGIN {new_state {prefix yy}} {
    variable ${prefix}_state_stack
    eval set ${prefix}_state_stack [lrange \$${prefix}_state_stack 0 end-1]
    eval lappend ${prefix}_state_stack $new_state
}

# initialize values used by the lexer
set yytext {}
set yyleng 0
set yy_buffer {}
set yy_index 0
set yy_done 0
set yylval ""
set yy_state_stack {}
BEGIN INITIAL
array set yy_state_table {INITIAL 1 INSIDE_BRACES 0}
set yylineno 1
if {![info exists yyin]} {
    set yyin "stdin"
}
if {![info exists yyout]} {
    set yyout "stdout"
}

######
# autogenerated yylex function created by fickle
######

# Whenever yylex() is called, it scans tokens from the global input
# file yyin (which defaults to stdin).  It continues until it either
# reaches an end-of-file (at which point it returns the value 0) or
# one of its actions executes a return statement.
#   -- from the flex(1) man page
proc yylex {} {
    variable yy_buffer
    variable yy_index
    variable yy_done
    variable yy_flex_debug
    variable yyleng
    variable yy_state_table
    variable yytext
    variable yylval
    while {1} {
        set yy_current_state [yy_top_state]
        if {[string length $yy_buffer] - $yy_index < 1024} {
            if {$yy_done == 0} {
                set yy_new_buffer ""
                YY_INPUT yy_new_buffer yy_buffer_size 1024
                append yy_buffer $yy_new_buffer
                if {$yy_buffer_size == 0 && \
                        [string length $yy_buffer] - $yy_index == 0} {
                    set yy_done 1
                }
            }
            if $yy_done {
                if {[yywrap] == 0} {
                    set yy_done 0
                    continue
                } elseif {[string length $yy_buffer] - $yy_index == 0} {
                    break
                }
            }            
        }
        set yyleng 0
        set yy_matched_rule -1
        # rule 0: {WS}+
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A((([ \t]|(\\\n)))+)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 0
        }
        # rule 1: {WS}*\n(\n|{WS})*
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A((([ \t]|(\\\n)))*\n(\n|(([ \t]|(\\\n))))*)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 1
        }
        # rule 2: {WS}*\;{WS}*
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A((([ \t]|(\\\n)))*\;(([ \t]|(\\\n)))*)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 2
        }
        # rule 3: [\[]
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A([\[])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 3
        }
        # rule 4: [\]]
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A([\]])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 4
        }
        # rule 5: [\#]([^\[ \t\n;\\]|\\.)*
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A([\#]([^\[ \t\n;\\]|\\.)*)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 5
        }
        # rule 6: [\"]([^\"\\]|\\.|\\\n|\n)*[\"]
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A([\"]([^\"\\]|\\.|\\\n|\n)*[\"])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 6
        }
        # rule 7: [\{]
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A([\{])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 7
        }
        # rule 8: <INSIDE_BRACES>([^\{\}\\]|\\.|\n|\\\n)*
        if {$yy_current_state == "INSIDE_BRACES" && \
                [regexp -start $yy_index -indices -line  -- {\A(([^\{\}\\]|\\.|\n|\\\n)*)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 8
        }
        # rule 9: <INSIDE_BRACES>[\{]
        if {$yy_current_state == "INSIDE_BRACES" && \
                [regexp -start $yy_index -indices -line  -- {\A([\{])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 9
        }
        # rule 10: <INSIDE_BRACES>[\}]
        if {$yy_current_state == "INSIDE_BRACES" && \
                [regexp -start $yy_index -indices -line  -- {\A([\}])} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 10
        }
        # rule 11: ([^\#\{\[\]\" \t\n;\\]|\\.)([^\[\] \t\n;\\]|\\.)*
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A(([^\#\{\[\]\" \t\n;\\]|\\.)([^\[\] \t\n;\\]|\\.)*)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 11
        }
        # rule 12: <INSIDE_BRACES>.|\n
        if {$yy_current_state == "INSIDE_BRACES" && \
                [regexp -start $yy_index -indices -line  -- {\A(.|\n)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 12
        }
        # rule 13: .|\n
        if {$yy_state_table($yy_current_state) && \
                [regexp -start $yy_index -indices -line  -- {\A(.|\n)} $yy_buffer yy_match] > 0 && \
                [lindex $yy_match 1] - $yy_index + 1 > $yyleng} {
            set yytext [string range $yy_buffer $yy_index [lindex $yy_match 1]]
            set yyleng [string length $yytext]
            set yy_matched_rule 13
        }
        if {$yy_matched_rule == -1} {
            set yytext [string index $yy_buffer $yy_index]
            set yyleng 1
        }
        incr yy_index $yyleng
        # workaround for Tcl's circumflex behavior
        if {[string index $yytext end] == "\n"} {
            set yy_buffer [string range $yy_buffer $yy_index end]
            set yy_index 0
        }
        set numlines [expr {[llength [split $yytext "\n"]] - 1}]
        switch -- $yy_matched_rule {
            0 {
set yylval $yytext
    variable WORDSEP
    return $WORDSEP
            }
            1 {
set yylval $yytext
    variable NEWLINE
    return $NEWLINE
            }
            2 {
set yylval $yytext
    variable SEMICOLON
    return $SEMICOLON
            }
            3 {
set yylval $yytext
    variable OBRACKET
    return $OBRACKET
            }
            4 {
set yylval $yytext
    variable CBRACKET
    return $CBRACKET
            }
            5 {
set yylval $yytext
    variable HSTRING
    return $HSTRING
            }
            6 {
set yylval $yytext
    variable QSTRING
    return $QSTRING
            }
            7 {
variable braces_level
    set braces_level 1
    set yylval $yytext
    BEGIN INSIDE_BRACES
            }
            8 {
append yylval $yytext
            }
            9 {
variable braces_level
    append yylval $yytext
    incr braces_level
            }
            10 {
variable braces_level
    append yylval $yytext
    incr braces_level -1
    if { $braces_level == 0 } then {
        BEGIN INITIAL
        variable BSTRING
        return $BSTRING
    }
            }
            11 {
set yylval $yytext
    variable STRING
    return $STRING
            }
            12 -
            13 {
yyerror "Character unknown or not allowed here."
            }
            default
                { ECHO }
        }
        variable yylineno
        incr yylineno $numlines
    }
    return 0
}
######
# end autogenerated fickle functions
######

##
# \brief (Re)start the lexer
#
# \param string string to parse
#
proc reset { string } {
    variable yylineno 1
    yyrestart ""
    ##
    # \todo workaround for the lexer not resetting the state stack bug
    variable yy_state_stack
    set yy_state_stack [ list INITIAL ]
    # end of work around
    yy_scan_string $string
    return
}
##
# \brief Lexer wrapper
#
# \return list of token, next state, contents
#
proc lex { } {
    variable token
    variable yylval
    set r [ yylex ]
    if { $r == 0 } then {
        return [ list 0 "" "" ]
    } else {
        return [ list $token($r) [ yy_top_state ] $yylval]
    }
}
##
# \brief token mapping, for human readable token names
#
variable token
array set token { }
set src_dir [ file dirname [ info script ] ]
set fp [ open [ file join $src_dir "parser.tab.tcl" ] ]
set tab [ read $fp ]
close $fp
foreach i [ split $tab "\n" ] {
    if { [ string match "set *" $i ] } then {
        set token([ lindex $i 2 ]) [ string trim [ lindex $i 1 ] ":" ]
    }
}
# end of namespace eval yy
}
