# File kwartz.rb, line 3902
        def gettoken
            @token = '*** debug ***'
            @token_str = '*** debug ***'

            ## ignore space and comment
            while (ch = getchar()) != nil do
                if space?(ch) then    # ignore space
                    next
                end
                if ch == ?\# then     # ignore comment
                    while (ch = getchar()) != ?\n && ch != nil do; end
                    next
                end
                break                 # break if else
            end
                

            ## check token

            ## nil
            if ch == nil then
                @token = @token_str = nil
                return @token
            end

            ## name (variable, macro, ...), or true/false/nil
            if alpha?(ch) then
                s = ch.chr
                s << getchar() while word?(nextchar())
                @token_str = s
                case s
                when 'true'
                    @token = :true
                when 'false'
                    @token = :false
                when 'null', 'nil'
                    @token = :null
                when 'empty'
                    @token = :empty
                else
                    @token = :name
                end
                return @token
            end

            ## number
            if num?(ch) then
                s = ch.chr
                s << getchar() while num?(nextchar())
                if nextchar() == ?. then
                    s << getchar()
                    s << getchar() while num?(nextchar())
                end
                @token_str = s
                @token = :number
                return @token
            end

            
            ## keywords(:if, :while, ...), ':::' and ':'
            if ch == ?\: then
                s = ':'
                if nextchar() == ?\: then
                    getchar()
                    if nextchar() == ?\: then
                        getchar()
                        @token = ':::'
                        @token_str = cutstr(?\n)
                        @token_str.chomp!
                    else
                        @token = '::'
                        @token_str = cutstr(?\n)
                        @token_str.chomp!
                    end
                elsif alpha?(nextchar()) then
                    s << getchar() while alpha?(nextchar())
                    @token_str = s
                    @token = Keywords[s]
                    unless @token != nil
                        raise SyntaxError.new("'#{s}': invalid token.", self)
                    end
                else
                    @token = @token_str = ':'
                end
                return @token
            end
            
            ## string
            if ch == ?' then
                s = ""
                while (ch = getchar()) != ?' do
                    if ch == ?\\ then
                        case ch = getchar()
                        when ?n
                            ch = ?\n
                        end
                    end
                    s << ch
                end
                @token_str = s
                @token = :string
                return @token
            end

            ## other symbol
            case ch
                
            when ?(, ?), ?{, ?}, ?]
                @token = @token_str = ch.chr
                
            when ?[
                if nextchar() == ?: then
                    getchar()
                    @token = @token_str = '[:'
                else
                    @token = @token_str = '['
                end

            when ?.
                case nextchar()
                when ?+, ?<, ?>, ?=, ?!
                    c = getchar()
                    if nextchar() == ?= then
                        getchar()
                        @token = @token_str = ".#{c.chr}="
                    else
                        @token = @token_str = ".#{c.chr}"
                    end
                else
                    @token = @token_str = '.'
                end

            when ?!
                if nextchar() == ?= then
                    getchar()
                    @token = @token_str = '!='
                else
                    @token = @token_str = '!'
                end
                
            when ?<, ?>, ?=
                case nextchar()
                when ?\=
                    getchar()
                    @token = @token_str = ch.chr + '='
                else
                    @token = @token_str = ch.chr
                end

            when ?+, ?-, ?*, ?/, ?%, ?^
                if nextchar() == ?= then
                    getchar()
                    @token = @token_str = ch.chr + '='
                else
                    @token = @token_str = ch.chr
                end

            when ?\\, ??, ?\, , ?"                     #"
                @token = @token_str = ch.chr

            when ?&, ?|
                if nextchar() == ch then
                    getchar()
                    @token = @token_str = ch.chr + ch.chr
                else
                    raise SyntaxError.new("invalid char: #{ch.chr}", self)
                end

            when ?@
                s = ''
                s << getchar() while word?(nextchar())
                case s
                when 'stag', 'cont', 'etag'
                    @token = s.intern
                    @token_str = s
                else
                    @token = :expand2
                    @token_str = s
                    if s.empty? then
                      msg = "'@': macro name required."
                      raise SyntaxError.new(msg)
                    end
                    #raise SyntaxError.new("'@#{s}': invalid token.", self)
                end

            else
                raise SyntaxError.new("invalid char: #{ch.chr}", self)
            end

            return @token
        end


        ## for debug
        def scan_all
            s = ''
            while gettoken() != nil do
                if @token == :name || @token == :number then
                    s << @token_str
                elsif @token == :true || @token == :false || @token == :null || @token == :empty
                    s << @token_str
                elsif @token == :string then
                    s << @token_str.inspect
                #elsif @token == :stag || @token == :cont || @token == :etag then
                #    s << '@' + @token.id2name
                elsif @token == :expand2 then
                    s << '@' + @token_str
                elsif @token.is_a?(Symbol) then
                    s << @token.inspect
                elsif @token == ':::' then
                    s << @token + @token_str
                elsif @token == '::' then
                    s << @token + @token_str
                else
                    s << @token
                end
                s << "\n"
            end
            return s
        end
    end

    ### ------------------------------------------------------------

    module Helper
        def self.compile(pdata_str, plogic_str, lang, toppings)
            compiler = Kwartz::Compiler.new(lang, toppings)
            output = compiler.compile(pdata_str, plogic_str)
            #converter  = Kwartz::DefaultConverter.new(pdata_str)
            #scanner    = Kwartz::Scanner.new(converter.convert() + plogic_str)
            #parser     = Kwartz::Parser.new(scanner)
            #translator = Kwartz::Translator.instance(lang)
            #nodelist   = parser.parse()
            #output     = translator.translate_all(nodelist)
            return output
        end
        
        def self.convert(pdata_str, toppings)
            converter  = Kwartz::DefaultConverter.new(pdata_str, toppings)
            output     = converter.convert()
            return output
        end
        
        def self.translate(plogic_str, lang, toppings)
            unless Kwartz::Translator.registered?(lang)
                raise InvalidLanguageError.new(lang)
            end
            scanner    = Kwartz::Scanner.new(plogic_str, toppings)
            parser     = Kwartz::Parser.new(scanner, toppings)
            translator = Kwartz::Translator.instance(lang, toppings)
            nodelist   = parser.parse()
            output     = translator.translate_all(nodelist)
            return output
        end
        
        def self.parse(plcode_str, toppings)
            scanner    = Kwartz::Scanner.new(plcode_str, toppings)
            parser     = Kwartz::Parser.new(scanner, toppings)
            nodelist   = parser.parse()
            return nodelist
        end

        def self.parse_stmt(plcode_str, toppings)
            scanner    = Kwartz::Scanner.new(plcode_str, toppings)
            parser     = Kwartz::Parser.new(scanner, toppings)
            nodelist   = parser.parse()
            output     = nodelist.print_all()
            return output
        end

        def self.parse_expr(expression_str, toppings)
            scanner = Kwartz::Scanner.new(expression_str, toppings)
            parser     = Kwartz::Parser.new(scanner, toppings)
            #node = parser.parse_expression()
            node = parser.parse_assignment()
            if parser.token then
                msg = ''
                raise Kwartz::SyntaxError.new(msg, scanner)
            end
            output = node.print_all
            return output
        end
        
        def self.scan(pdata_str, toppings)
            scanner    = Kwartz::Scanner.new(pdata_str, toppings)
            output     = scanner.scan_all()
            return output
        end
        
        def self.delete_directives(input_str, attr_name='kd')
            s = input_str.gsub(/\b#{attr_name}=(\".*?\"|[\-\:\#\@\w]+)/, '')
            return s
        end

        def self.fetch(input_str)
            converter  = Kwartz::DefaultConverter.new(input_str, {})
            converter.instance_eval {
                def self.fetch
                    return _fetch()
                end
            }
            s = ''
            while fv = converter.fetch() do
                s << fv.head_text.inspect << " + " << fv.tag_str.inspect << "\n"
            end
            return s
        end
        
        def self.analyze(pdata_str, plogic_str, toppings)
            converter  = Kwartz::DefaultConverter.new(pdata_str)
            scanner    = Kwartz::Scanner.new(converter.convert() + plogic_str)
            parser     = Kwartz::Parser.new(scanner)
            nodelist   = parser.parse()
            analyzer   = Kwartz::Analyzer.new(nodelist)
            analyzer.analyze()
            s = ''
            analyzer.warnings.each do |message|
                s << '*** warning: ' << message << "\n"
            end
            s << "global variables:"
            analyzer.globals.each { |varname| s << ' ' << varname }
            s << "\n"
            s << "local variables: "
            analyzer.locals.each { |varname| s << ' ' << varname }
            s << "\n"
            return s
        end

        def self.do_action(action, input, plogic, lang, toppings)
            output = nil
            case action
            when 'compile'
                output = self.compile(input, plogic, lang, toppings)
            when 'convert'
                output = self.convert(input, toppings)
            when 'translate'
                output = self.translate(input + plogic, lang, toppings)
            when 'parse'
                output = self.parse_stmt(input + plogic, toppings)
            when 'scan'
                output = self.scan(input + plogic, toppings)
            when 'delete_directives'
                attr_name = toppings[:attr_name] || 'kd'
                output = self.delete_directives(input, attr_name)
            when 'fetch'
                output = self.fetch(input)
            when 'analyze'
                output = self.analyze(input, plogic, toppings)
            end
            return output
        end

    end

    module Util
        class CommandOptionError < StandardError
            def initialize(msg)
                super(msg)
            end
        end
        
        def self.parse_argv(argv, single_options=nil, argneed_options=nil)
            options  = {}
            toppings = {}
            return options, toppings if !argv
            
            opttypes = {}
            single_options.each_byte do |ch|
                opttypes[ch] = :single
            end if single_options
            argneed_options.each_byte do |ch|
                opttypes[ch] = :arg_need
            end if argneed_options
            opttypes[?-] = :topping
            
            while argv[0] && argv[0][0] == ?- do
                optstr = argv.shift           # argv.shift.sub(/^-/, '')
                optstr = optstr[1, optstr.length-1]
                
                while optstr && !optstr.empty? do
                    optchar = optstr[0]
                    optstr[0] = ''            # optstr = optstr[1, optstr.length-1]
                    optstr = nil if optstr && optstr.empty?
                    case opttypes[optchar]
                    when :single
                        options[optchar] = true
                    when :arg_need
                        arg = optstr || argv.shift
                        raise CommandOptionError.new("-#{optchar.chr}: argument reguired.") unless arg
                        options[optchar] = arg
                        optstr = nil
                    when :topping
                        if optstr == 'help' then
                            options[?h] = true
                        elsif optstr =~ /^(\w+)(=.*)?/ then
                            key   = $1.intern
                            value = $2
                            if !value || value.empty? then
                                value = true
                            else
                                value.sub!(/^=/, '')
                                value = str2value(value)
                            end
                            toppings[key] = value
                        end
                        optstr = nil
                    else
                        raise CommandOptionError.new("-#{optchar.chr}: invalid option.")
                    end
                end
            end
            return options, toppings
        end

        def self.str2value(str)
            case str
            when /^\d+$/
                value = str.to_i
            when /^true$/i, /^yes$/i
                value = true
            when /^false$/i, /^no$/i
                value = false
            when /^nil$/, /^null$/i
                value = nil
            when /^\d+$/
                value = str.to_i
            when /^(\d*\.\d+|\d+\.\d*)$/
                value = str.to_f
            when /^\/(.*)\/$/
                value = Regexp.new($1)
            when /^'(.*)'$/
                value = eval value
            when /^"(.*)"$/
                value = eval value
            else
                value = str
            end
            return value
        end

    end
end