#!/usr/bin/env perl

use strict;
use warnings;

my (%token, @order, @keywords);

{
    my $keywords;
    my @const;
    push @const, <<_END_;
package token

const(
    _ Token = iota
_END_

    for (split m/\n/, <<_END_) {
ILLEGAL
EOF
COMMENT
KEYWORD

STRING
BOOLEAN
NULL
NUMBER
IDENTIFIER

PLUS                            +
MINUS                           -
MULTIPLY                        *
SLASH                           /
REMAINDER                       %

AND                             &
OR                              |
EXCLUSIVE_OR                    ^
SHIFT_LEFT                      <<
SHIFT_RIGHT                     >>
UNSIGNED_SHIFT_RIGHT            >>>
AND_NOT                         &^

ADD_ASSIGN                     +=
SUBTRACT_ASSIGN                -=
MULTIPLY_ASSIGN                *=
QUOTIENT_ASSIGN                /=
REMAINDER_ASSIGN               %=

AND_ASSIGN                     &=
OR_ASSIGN                      |=
EXCLUSIVE_OR_ASSIGN            ^=
SHIFT_LEFT_ASSIGN              <<=
SHIFT_RIGHT_ASSIGN             >>=
UNSIGNED_SHIFT_RIGHT_ASSIGN    >>>=
AND_NOT_ASSIGN                 &^=

LOGICAL_AND                    &&
LOGICAL_OR                     ||
INCREMENT                      ++
DECREMENT                      --

EQUAL                          ==
STRICT_EQUAL                   ===
LESS                           <
GREATER                        >
ASSIGN                         =
NOT                            !

BITWISE_NOT                    ~

NOT_EQUAL                      !=
STRICT_NOT_EQUAL               !==
LESS_OR_EQUAL                  <=
GREATER_OR_EQUAL               <=

LEFT_PARENTHESIS               (
LEFT_BRACKET                   [
LEFT_BRACE                     {
COMMA                          ,
PERIOD                         .

RIGHT_PARENTHESIS              )
RIGHT_BRACKET                  ]
RIGHT_BRACE                    }
SEMICOLON                      ;
COLON                          :
QUESTION_MARK                  ?

firstKeyword
IF
IN
DO

VAR
FOR
NEW
TRY

THIS
ELSE
CASE
VOID
WITH

WHILE
BREAK
CATCH
THROW

RETURN
TYPEOF
DELETE
SWITCH

DEFAULT
FINALLY

FUNCTION
CONTINUE
DEBUGGER

INSTANCEOF
lastKeyword
_END_
        chomp;

        next if m/^\s*#/;

        my ($name, $symbol) = m/(\w+)\s*(\S+)?/;

        if (defined $symbol) {
            push @order, $name;
            push @const, "$name // $symbol";
            $token{$name} = $symbol;
        } elsif (defined $name) {
            $keywords ||= $name eq 'firstKeyword';

            push @const, $name;
            #$const[-1] .= " Token = iota" if 2 == @const;
            if ($name =~ m/^([A-Z]+)/) {
                push @keywords, $name if $keywords;
                push @order, $name;
                if ($token{SEMICOLON}) {
                    $token{$name} = lc $1;
                } else {
                    $token{$name} = $name;
                }
            }
        } else {
            push @const, "";
        }

    }
    push @const, ")";
    print join "\n", @const, "";
}

{
    print <<_END_;

var token2string = [...]string{
_END_
    for my $name (@order) {
        print "$name: \"$token{$name}\",\n";
    }
    print <<_END_;
}
_END_

    print <<_END_;

var keywordTable = map[string]_keyword{
_END_
    for my $name (@keywords) {
        print <<_END_
			"@{[ lc $name ]}": _keyword{
				token: $name,
			},
_END_
    }

    for my $name (qw/
        const
        class
        enum
        export
        extends
        import
        super
        /) {
        print <<_END_
			"$name": _keyword{
				token: KEYWORD,
                futureKeyword: true,
			},
_END_
    }

    for my $name (qw/
        implements
        interface
        let
        package
        private
        protected
        public
        static
        /) {
        print <<_END_
			"$name": _keyword{
				token: KEYWORD,
                futureKeyword: true,
                strict: true,
			},
_END_
    }

    print <<_END_;
}
_END_
}
