from __future__ import print_function
import sys, shlex, operator

tk_EOI, tk_Mul, tk_Div, tk_Mod, tk_Add, tk_Sub, tk_Negate, tk_Not, tk_Lss, tk_Leq, tk_Gtr, \
tk_Geq, tk_Eql, tk_Neq, tk_Assign, tk_And, tk_Or, tk_If, tk_Else, tk_While, tk_Print,      \
tk_Putc, tk_Lparen, tk_Rparen, tk_Lbrace, tk_Rbrace, tk_Semi, tk_Comma, tk_Ident,          \
tk_Integer, tk_String = range(31)

nd_Ident, nd_String, nd_Integer, nd_Sequence, nd_If, nd_Prtc, nd_Prts, nd_Prti, nd_While, \
nd_Assign, nd_Negate, nd_Not, nd_Mul, nd_Div, nd_Mod, nd_Add, nd_Sub, nd_Lss, nd_Leq,     \
nd_Gtr, nd_Geq, nd_Eql, nd_Neq, nd_And, nd_Or = range(25)

# must have same order as above
Tokens = [
    ["EOI"             , False, False, False, -1, -1        ],
    ["*"               , False, True,  False, 13, nd_Mul    ],
    ["/"               , False, True,  False, 13, nd_Div    ],
    ["%"               , False, True,  False, 13, nd_Mod    ],
    ["+"               , False, True,  False, 12, nd_Add    ],
    ["-"               , False, True,  False, 12, nd_Sub    ],
    ["-"               , False, False, True,  14, nd_Negate ],
    ["!"               , False, False, True,  14, nd_Not    ],
    ["<"               , False, True,  False, 10, nd_Lss    ],
    ["<="              , False, True,  False, 10, nd_Leq    ],
    [">"               , False, True,  False, 10, nd_Gtr    ],
    [">="              , False, True,  False, 10, nd_Geq    ],
    ["=="              , False, True,  False,  9, nd_Eql    ],
    ["!="              , False, True,  False,  9, nd_Neq    ],
    ["="               , False, False, False, -1, nd_Assign ],
    ["&&"              , False, True,  False,  5, nd_And    ],
    ["||"              , False, True,  False,  4, nd_Or     ],
    ["if"              , False, False, False, -1, nd_If     ],
    ["else"            , False, False, False, -1, -1        ],
    ["while"           , False, False, False, -1, nd_While  ],
    ["print"           , False, False, False, -1, -1        ],
    ["putc"            , False, False, False, -1, -1        ],
    ["("               , False, False, False, -1, -1        ],
    [")"               , False, False, False, -1, -1        ],
    ["{"               , False, False, False, -1, -1        ],
    ["}"               , False, False, False, -1, -1        ],
    [";"               , False, False, False, -1, -1        ],
    [","               , False, False, False, -1, -1        ],
    ["Ident"           , False, False, False, -1, nd_Ident  ],
    ["Integer literal" , False, False, False, -1, nd_Integer],
    ["String literal"  , False, False, False, -1, nd_String ]
    ]

all_syms = {"End_of_input"   : tk_EOI,     "Op_multiply"    : tk_Mul,
            "Op_divide"      : tk_Div,     "Op_mod"         : tk_Mod,
            "Op_add"         : tk_Add,     "Op_subtract"    : tk_Sub,
            "Op_negate"      : tk_Negate,  "Op_not"         : tk_Not,
            "Op_less"        : tk_Lss,     "Op_lessequal"   : tk_Leq,
            "Op_greater"     : tk_Gtr,     "Op_greaterequal": tk_Geq,
            "Op_equal"       : tk_Eql,     "Op_notequal"    : tk_Neq,
            "Op_assign"      : tk_Assign,  "Op_and"         : tk_And,
            "Op_or"          : tk_Or,      "Keyword_if"     : tk_If,
            "Keyword_else"   : tk_Else,    "Keyword_while"  : tk_While,
            "Keyword_print"  : tk_Print,   "Keyword_putc"   : tk_Putc,
            "LeftParen"      : tk_Lparen,  "RightParen"     : tk_Rparen,
            "LeftBrace"      : tk_Lbrace,  "RightBrace"     : tk_Rbrace,
            "Semicolon"      : tk_Semi,    "Comma"          : tk_Comma,
            "Identifier"     : tk_Ident,   "Integer"        : tk_Integer,
            "String"         : tk_String}

Display_nodes = ["Identifier", "String", "Integer", "Sequence", "If", "Prtc", "Prts",
    "Prti", "While", "Assign", "Negate", "Not", "Multiply", "Divide", "Mod", "Add",
    "Subtract", "Less", "LessEqual", "Greater", "GreaterEqual", "Equal", "NotEqual",
    "And", "Or"]

TK_NAME         = 0
TK_RIGHT_ASSOC  = 1
TK_IS_BINARY    = 2
TK_IS_UNARY     = 3
TK_PRECEDENCE   = 4
TK_NODE         = 5

input_file = None
err_line   = None
err_col    = None
tok        = None
tok_text   = None

#*** show error and exit
def error(msg):
    print("(%d, %d) %s" % (int(err_line), int(err_col), msg))
    exit(1)

#***
def gettok():
    global err_line, err_col, tok, tok_text, tok_other
    line = input_file.readline()
    if len(line) == 0:
        error("empty line")

    line_list = shlex.split(line, False, False)
    # line col Ident var_name
    # 0    1   2     3

    err_line = line_list[0]
    err_col  = line_list[1]
    tok_text = line_list[2]

    tok = all_syms.get(tok_text)
    if tok == None:
        error("Unknown token %s" % (tok_text))

    tok_other = None
    if tok in [tk_Integer, tk_Ident, tk_String]:
        tok_other = line_list[3]

class Node:
    def __init__(self, node_type, left = None, right = None, value = None):
        self.node_type  = node_type
        self.left  = left
        self.right = right
        self.value = value

#***
def make_node(oper, left, right = None):
    return Node(oper, left, right)

#***
def make_leaf(oper, n):
    return Node(oper, value = n)

#***
def expect(msg, s):
    if tok == s:
        gettok()
        return
    error("%s: Expecting '%s', found '%s'" % (msg, Tokens[s][TK_NAME], Tokens[tok][TK_NAME]))

#***
def expr(p):
    x = None

    if tok == tk_Lparen:
        x = paren_expr()
    elif tok in [tk_Sub, tk_Add]:
        op = (tk_Negate if tok == tk_Sub else tk_Add)
        gettok()
        node = expr(Tokens[tk_Negate][TK_PRECEDENCE])
        x = (make_node(nd_Negate, node) if op == tk_Negate else node)
    elif tok == tk_Not:
        gettok()
        x = make_node(nd_Not, expr(Tokens[tk_Not][TK_PRECEDENCE]))
    elif tok == tk_Ident:
        x = make_leaf(nd_Ident, tok_other)
        gettok()
    elif tok == tk_Integer:
        x = make_leaf(nd_Integer, tok_other)
        gettok()
    else:
        error("Expecting a primary, found: %s" % (Tokens[tok][TK_NAME]))

    while Tokens[tok][TK_IS_BINARY] and Tokens[tok][TK_PRECEDENCE] >= p:
        op = tok
        gettok()
        q = Tokens[op][TK_PRECEDENCE]
        if not Tokens[op][TK_RIGHT_ASSOC]:
            q += 1

        node = expr(q)
        x = make_node(Tokens[op][TK_NODE], x, node)

    return x

#***
def paren_expr():
    expect("paren_expr", tk_Lparen)
    node = expr(0)
    expect("paren_expr", tk_Rparen)
    return node

#***
def stmt():
    t = None

    if tok == tk_If:
        gettok()
        e = paren_expr()
        s = stmt()
        s2 = None
        if tok == tk_Else:
            gettok()
            s2 = stmt()
        t = make_node(nd_If, e, make_node(nd_If, s, s2))
    elif tok == tk_Putc:
        gettok()
        e = paren_expr()
        t = make_node(nd_Prtc, e)
        expect("Putc", tk_Semi)
    elif tok == tk_Print:
        gettok()
        expect("Print", tk_Lparen)
        while True:
            if tok == tk_String:
                e = make_node(nd_Prts, make_leaf(nd_String, tok_other))
                gettok()
            else:
                e = make_node(nd_Prti, expr(0))

            t = make_node(nd_Sequence, t, e)
            if tok != tk_Comma:
                break
            gettok()
        expect("Print", tk_Rparen)
        expect("Print", tk_Semi)
    elif tok == tk_Semi:
        gettok()
    elif tok == tk_Ident:
        v = make_leaf(nd_Ident, tok_other)
        gettok()
        expect("assign", tk_Assign)
        e = expr(0)
        t = make_node(nd_Assign, v, e)
        expect("assign", tk_Semi)
    elif tok == tk_While:
        gettok()
        e = paren_expr()
        s = stmt()
        t = make_node(nd_While, e, s)
    elif tok == tk_Lbrace:
        gettok()
        while tok != tk_Rbrace and tok != tk_EOI:
            t = make_node(nd_Sequence, t, stmt())
        expect("Lbrace", tk_Rbrace)
    elif tok == tk_EOI:
        pass
    else:
        error("Expecting start of statement, found: %s" % (Tokens[tok][TK_NAME]))

    return t

#***
def parse():
    t = None
    gettok()
    while True:
        t = make_node(nd_Sequence, t, stmt())
        if tok == tk_EOI or t == None:
            break
    return t

def prt_ast(t):
    if t == None:
        print(";")
    else:
        print("%-14s" % (Display_nodes[t.node_type]), end='')
        if t.node_type in [nd_Ident, nd_Integer]:
            print("%s" % (t.value))
        elif t.node_type == nd_String:
            print("%s" %(t.value))
        else:
            print("")
            prt_ast(t.left)
            prt_ast(t.right)

#*** main driver
input_file = sys.stdin
if len(sys.argv) > 1:
    try:
        input_file = open(sys.argv[1], "r", 4096)
    except IOError as e:
        error(0, 0, "Can't open %s" % sys.argv[1])
t = parse()
prt_ast(t)


λ 'as. impmapM (λ 'a. print qq{Argument: §(a)\n}) as


package main
import (
	"fmt"
	"os"
)

func main() {
	for i, x := range os.Args[1:] {
		fmt.Printf("the argument #%d is %s\n", i, x)
	}
}


PROCEDURE Main()

   LOCAL i

   FOR i := 1 TO PCount()
      ? "argument", hb_ntos( i ), "=", hb_PValue( i )
   NEXT

   RETURN


# Single line comment

#* Multi
   Line
   Comment *#


 # Comments in mget scripts are prefixed with a hash symbol
 ls   # comments may appear at the end of a line


/* This is a comment. */
/* So is this
   multiline comment.
 */


######################################################################
#
# The Rosetta Code parser in Ratfor 77.
#
#
# Ratfor 77 is a preprocessor for FORTRAN 77; therefore we do not have
# recursive calls available. For printing the flattened tree, I use an
# ordinary non-recursive implementation of the tree traversal. The
# mutually recursive parser itself is more difficult to handle; for
# that, I implement a tiny, FORTH-like token processor that supports
# recursive calls.
#
# How to deal with input is another problem. I use formatted input,
# treating each line as a (regrettably fixed length) array of type
# CHARACTER. It is a very simple method, and leaves the input in a
# form convenient for the necessary processing (given that the input
# is not formatted in columns).
#
#
# On a POSIX platform, the program can be compiled with f2c and run
# somewhat as follows:
#
#    ratfor77 parse-in-ratfor.r > parse-in-ratfor.f
#    f2c -C -Nc40 parse-in-ratfor.f
#    cc parse-in-ratfor.c -lf2c
#    ./a.out < compiler-tests/primes.lex
#
# With gfortran, a little differently:
#
#    ratfor77 parse-in-ratfor.r > parse-in-ratfor.f
#    gfortran -fcheck=all -std=legacy parse-in-ratfor.f
#    ./a.out < compiler-tests/primes.lex
#
#
# I/O is strictly from default input and to default output, which, on
# POSIX systems, usually correspond respectively to standard input and
# standard output.
#
#---------------------------------------------------------------------

# Parameters that you can adjust.

define(LINESZ, 256)             # Size of an input line.
define(STRNSZ, 4096)            # Size of the string pool.
define(NODSSZ, 4096)            # Size of the nodes pool.
define(DSTKSZ, 4096)            # Size of the data stack.
define(PSTKSZ, 4096)            # Size of the precedence stack.
define(XSTKSZ, 4096)            # Size of the execution stack.

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

define(TOKSZ, 5) # Size of a lexical token, in integers.
define(ILN, 1)   # Index for line number.
define(ICN, 2)   # Index for column number.
define(ITK, 3)   # Index for token number.
define(ITV, 4)   # Index for the string pool index of the token value.
define(ITN, 5)   # Index for the length of the token value.

define(NODESZ, 3)
define(NTAG,   1)               # Index for the tag.
                                # For an internal node --
define(NLEFT,  2)               #   Index for the left node.
define(NRIGHT, 3)               #   Index for the right node.
                                # For a leaf node --
define(NITV,   2)               #   Index for the string pool index.
define(NITN,   3)               #   Length of the value.

define(NIL, -1)                 # Nil node.

define(TKELSE,  0)
define(TKIF,    1)
define(TKPRNT,  2)
define(TKPUTC,  3)
define(TKWHIL,  4)
define(TKMUL,   5)
define(TKDIV,   6)
define(TKMOD,   7)
define(TKADD,   8)
define(TKSUB,   9)
define(TKNEG,  10)
define(TKLT,   11)
define(TKLE,   12)
define(TKGT,   13)
define(TKGE,   14)
define(TKEQ,   15)
define(TKNE,   16)
define(TKNOT,  17)
define(TKASGN, 18)
define(TKAND,  19)
define(TKOR,   20)
define(TKLPAR, 21)
define(TKRPAR, 22)
define(TKLBRC, 23)
define(TKRBRC, 24)
define(TKSEMI, 25)
define(TKCMMA, 26)
define(TKID,   27)
define(TKINT,  28)
define(TKSTR,  29)
define(TKEOI,  30)

define(NDID,    0)
define(NDSTR,   1)
define(NDINT,   2)
define(NDSEQ,   3)
define(NDIF,    4)
define(NDPRTC,  5)
define(NDPRTS,  6)
define(NDPRTI,  7)
define(NDWHIL,  8)
define(NDASGN,  9)
define(NDNEG,  10)
define(NDNOT,  11)
define(NDMUL,  12)
define(NDDIV,  13)
define(NDMOD,  14)
define(NDADD,  15)
define(NDSUB,  16)
define(NDLT,   17)
define(NDLE,   18)
define(NDGT,   19)
define(NDGE,   20)
define(NDEQ,   21)
define(NDNE,   22)
define(NDAND,  23)
define(NDOR,   24)

subroutine string (src, isrc, nsrc, strngs, istrng, i, n)

  # Store a string in the string pool.

  implicit none

  character src(*)         # Source string.
  integer isrc, nsrc       # Index and length of the source substring.
  character strngs(STRNSZ) # The string pool.
  integer istrng           # The string pool's next slot.
  integer i, n             # Index and length within the string pool.

  integer j

  if (STRNSZ < istrng + nsrc)
    {
      write (*, '(''string pool exhausted'')')
      stop
    }
  for (j = 0; j < nsrc; j = j + 1)
    strngs(istrng + j) = src(isrc + j)
  i = istrng
  n = nsrc
  istrng = istrng + nsrc
end

subroutine astnod (node, nodes, inodes, i)

  # Store a node in the nodes pool.

  implicit none

  integer node(NODESZ)
  integer nodes(NODESZ, NODSSZ)
  integer inodes
  integer i

  integer j

  if (NODSSZ < inodes + 1)
    {
      write (*, '(''node pool exhausted'')')
      stop
    }
  i = inodes
  inodes = inodes + 1
  for (j = 1; j <= NODESZ; j = j + 1)
    nodes(j, i) = node(j)
end

function issp (c)

  # Is a character a space character?

  implicit none

  character c
  logical issp

  integer ic

  ic = ichar (c)
  issp = (ic == 32 || (9 <= ic && ic <= 13))
end

function skipsp (str, i, imax)

  # Skip past spaces in a string.

  implicit none

  character str(*)
  integer i
  integer imax
  integer skipsp

  logical issp

  logical done

  skipsp = i
  done = .false.
  while (!done)
    {
      if (imax <= skipsp)
        done = .true.
      else if (!issp (str(skipsp)))
        done = .true.
      else
        skipsp = skipsp + 1
    }
end

function skipns (str, i, imax)

  # Skip past non-spaces in a string.

  implicit none

  character str(*)
  integer i
  integer imax
  integer skipns

  logical issp

  logical done

  skipns = i
  done = .false.
  while (!done)
    {
      if (imax <= skipns)
        done = .true.
      else if (issp (str(skipns)))
        done = .true.
      else
        skipns = skipns + 1
    }
end

function trimrt (str, n)

  # Find the length of a string, if one ignores trailing spaces.

  implicit none

  character str(*)
  integer n
  integer trimrt

  logical issp

  logical done

  trimrt = n
  done = .false.
  while (!done)
    {
      if (trimrt == 0)
        done = .true.
      else if (!issp (str(trimrt)))
        done = .true.
      else
        trimrt = trimrt - 1
    }
end

function mktok (str, i, n)

  # Convert a substring to a token integer.

  implicit none

  character str(*)
  integer i
  integer n
  integer mktok

  character*16 tokstr(0:30)
  character*16 test
  integer j
  logical done

  data tokstr / 'Keyword_else    ', _
                'Keyword_if      ', _
                'Keyword_print   ', _
                'Keyword_putc    ', _
                'Keyword_while   ', _
                'Op_multiply     ', _
                'Op_divide       ', _
                'Op_mod          ', _
                'Op_add          ', _
                'Op_subtract     ', _
                'Op_negate       ', _
                'Op_less         ', _
                'Op_lessequal    ', _
                'Op_greater      ', _
                'Op_greaterequal ', _
                'Op_equal        ', _
                'Op_notequal     ', _
                'Op_not          ', _
                'Op_assign       ', _
                'Op_and          ', _
                'Op_or           ', _
                'LeftParen       ', _
                'RightParen      ', _
                'LeftBrace       ', _
                'RightBrace      ', _
                'Semicolon       ', _
                'Comma           ', _
                'Identifier      ', _
                'Integer         ', _
                'String          ', _
                'End_of_input    ' /

  test = '                '
  for (j = 0; j < n; j = j + 1)
    test(j + 1 : j + 1) = str(i + j)

  j = 0
  done = .false.
  while (!done)
    {
      if (TKEOI < j)
        {
          write (*, '(''unrecognized token'')')
          stop
        }
      else if (test == tokstr(j))
        done = .true.
      else
        j = j + 1
    }

  mktok = j
end

function mkint (str, i, n)

  # Convert a unsigned integer substring to an integer.

  implicit none

  character str(*)
  integer i
  integer n
  integer mkint

  integer j

  mkint = 0
  for (j = 0; j < n; j = j + 1)
    mkint = (10 * mkint) + (ichar (str(i + j)) - 48)
end

subroutine rdtok (strngs, istrng, blank, linno, colno, tokno, _
                  itkval, ntkval)

  # Read a token from the input.

  implicit none

  character strngs(STRNSZ)      # The string pool.
  integer istrng                # The string pool's next slot.
  logical blank                 # Is the line blank?
  integer linno                 # The line number.
  integer colno                 # The column number.
  integer tokno                 # The token number.
  integer itkval, ntkval        # Token value as a string.

  integer skipsp, skipns, trimrt
  integer mkint, mktok

  character line(LINESZ)
  character*20 fmt
  integer n, i, j

  # Read a line of text as an array of characters.
  write (fmt, '(''('', I10, ''A1)'')') LINESZ
  read (*, fmt) line

  n = trimrt (line, LINESZ)
  blank = (n == 0)

  if (!blank)
    {
      i = skipsp (line, 1, n + 1)
      j = skipns (line, i, n + 1)
      linno = mkint (line, i, j - i)

      i = skipsp (line, j, n + 1)
      j = skipns (line, i, n + 1)
      colno = mkint (line, i, j - i)

      i = skipsp (line, j, n + 1)
      j = skipns (line, i, n + 1)
      tokno = mktok (line, i, j - i)

      i = skipsp (line, j, n + 1)
      j = n + 1
      call string (line, i, j - i, strngs, istrng, itkval, ntkval)
    }
end

subroutine gettok (strngs, istrng, tok)

  # Get the next token.

  implicit none

  character strngs(STRNSZ)      # The string pool.
  integer istrng                # The string pool's next slot.
  integer tok(TOKSZ)

  integer linno, colno, tokno, itkval, ntkval
  logical blank

  blank = .true.
  while (blank)
    call rdtok (strngs, istrng, blank, linno, colno, tokno, _
                itkval, ntkval)
  tok(ILN) = linno
  tok(ICN) = colno
  tok(ITK) = tokno
  tok(ITV) = itkval
  tok(ITN) = ntkval
end

function accept (strngs, istrng, curtok, tokno)

  implicit none

  character strngs(STRNSZ)      # The string pool.
  integer istrng                # The string pool's next slot.
  integer curtok(TOKSZ)
  integer tokno
  logical accept

  accept = (curtok(ITK) == tokno)
  if (accept)
    call gettok (strngs, istrng, curtok)
end

subroutine expect (strngs, istrng, curtok, tokno)

  implicit none

  character strngs(STRNSZ)      # The string pool.
  integer istrng                # The string pool's next slot.
  integer curtok(TOKSZ)
  integer tokno

  logical accept

  if (!accept (strngs, istrng, curtok, tokno))
    {
      # This is not the same message as printed by the reference C
      # implementation. You can change that, if you wish.
      write (*, 100) curtok(ILN), curtok(ICN)
100   format ('unexpected token at line ', I5, ', column ', I5)
      stop
    }
end

function prec (tokno)

  # Precedence.

  implicit none

  integer tokno
  integer prec

  if (tokno == TKMUL || tokno == TKDIV || tokno == TKMOD)
    prec = 13
  else if (tokno == TKADD || tokno == TKSUB)
    prec = 12
  else if (tokno == TKNEG || tokno == TKNOT)
    prec = 14
  else if (tokno == TKLT || tokno == TKLE || _
           tokno == TKGT || tokno == TKGE)
    prec = 10
  else if (tokno == TKEQ || tokno == TKNE)
    prec = 9
  else if (tokno == TKAND)
    prec = 5
  else if (tokno == TKOR)
    prec = 4
  else
    prec = -1
end

function isbin (tokno)

  # Is an operation binary?

  implicit none

  integer tokno
  logical isbin

  isbin = (tokno == TKADD || _
           tokno == TKSUB || _
           tokno == TKMUL || _
           tokno == TKDIV || _
           tokno == TKMOD || _
           tokno == TKLT  || _
           tokno == TKLE  || _
           tokno == TKGT  || _
           tokno == TKGE  || _
           tokno == TKEQ  || _
           tokno == TKNE  || _
           tokno == TKAND || _
           tokno == TKOR)
end

function rtassc (tokno)

  # Is an operation right associative?

  implicit none

  integer tokno
  logical rtassc

  # None of the current operators is right associative.
  rtassc = .false.
end

function opernt (tokno)

  # Return the node tag for a binary operator.

  implicit none

  integer tokno
  integer opernt

  if (tokno == TKMUL)
    opernt = NDMUL
  else if (tokno == TKDIV)
    opernt = NDDIV
  else if (tokno == TKMOD)
    opernt = NDMOD
  else if (tokno == TKADD)
    opernt = NDADD
  else if (tokno == TKSUB)
    opernt = NDSUB
  else if (tokno == TKNEG)
    opernt = NDNEG
  else if (tokno == TKNOT)
    opernt = NDNOT
  else if (tokno == TKLT)
    opernt = NDLT
  else if (tokno == TKLE)
    opernt = NDLE
  else if (tokno == TKGT)
    opernt = NDGT
  else if (tokno == TKGE)
    opernt = NDGE
  else if (tokno == TKEQ)
    opernt = NDEQ
  else if (tokno == TKNE)
    opernt = NDNE
  else if (tokno == TKAND)
    opernt = NDAND
  else if (tokno == TKOR)
    opernt = NDOR
  else
    {
      write (*, '(''unrecognized binary operator'')')
      stop
    }
end

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

subroutine prtast (strngs, nodes, i, dstack)

  # Print a tree in flattened format.

  implicit none

  character strngs(*)
  integer nodes(NODESZ, NODSSZ)
  integer i
  integer dstack(DSTKSZ)

  integer j
  integer k
  integer n
  integer q, r
  integer tag

  character*80 fmt

  dstack(1) = i
  j = 2
  while (j != 1)
    {
      j = j - 1
      k = dstack(j)
      if (k < 1)
        write (*, '('';'')')
      else
        {
          tag = nodes(NTAG, k)
          if (tag == NDID)
            {
              n = nodes(NITN, k)
              write (fmt, '(''("Identifier ", '', I5, ''A)'')') n
              q = nodes(NITV, k)
              write (*, fmt) (strngs(r), r = q, q + n - 1)
            }
          else if (tag == NDINT)
            {
              n = nodes(NITN, k)
              write (fmt, '(''("Integer ", '', I5, ''A)'')') n
              q = nodes(NITV, k)
              write (*, fmt) (strngs(r), r = q, q + n - 1)
            }
          else if (tag == NDSTR)
            {
              n = nodes(NITN, k)
              write (fmt, '(''("String ", '', I5, ''A)'')') n
              q = nodes(NITV, k)
              write (*, fmt) (strngs(r), r = q, q + n - 1)
            }
          else
            {
              if (tag == NDSEQ)
                write (*, '(''Sequence'')')
              else if (tag == NDIF)
                write (*, '(''If'')')
              else if (tag == NDPRTC)
                write (*, '(''Prtc'')')
              else if (tag == NDPRTS)
                write (*, '(''Prts'')')
              else if (tag == NDPRTI)
                write (*, '(''Prti'')')
              else if (tag == NDWHIL)
                write (*, '(''While'')')
              else if (tag == NDASGN)
                write (*, '(''Assign'')')
              else if (tag == NDNEG)
                write (*, '(''Negate'')')
              else if (tag == NDNOT)
                write (*, '(''Not'')')
              else if (tag == NDMUL)
                write (*, '(''Multiply'')')
              else if (tag == NDDIV)
                write (*, '(''Divide'')')
              else if (tag == NDMOD)
                write (*, '(''Mod'')')
              else if (tag == NDADD)
                write (*, '(''Add'')')
              else if (tag == NDSUB)
                write (*, '(''Subtract'')')
              else if (tag == NDLT)
                write (*, '(''Less'')')
              else if (tag == NDLE)
                write (*, '(''LessEqual'')')
              else if (tag == NDGT)
                write (*, '(''Greater'')')
              else if (tag == NDGE)
                write (*, '(''GreaterEqual'')')
              else if (tag == NDEQ)
                write (*, '(''Equal'')')
              else if (tag == NDNE)
                write (*, '(''NotEqual'')')
              else if (tag == NDAND)
                write (*, '(''And'')')
              else if (tag == NDOR)
                write (*, '(''Or'')')
              else
                {
                  write (*, '(''unrecognized node type'')')
                  stop
                }
              if (DSTKSZ - 2 < n)
                {
                  write (*, '(''node stack overflow'')')
                  stop
                }
              dstack(j) = nodes(NRIGHT, k)
              dstack(j + 1) = nodes(NLEFT, k)
              j = j + 2
            }
        }
    }
end

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

# A tiny recursive language. Each instruction is two integers,
# although the second integer may be XPAD. XLOCs are named by
# integers.

define(XPAD,     0)             # "Padding"

define(XLOC,    10)             # "Jump or call location"
define(XJUMP,   20)             # "Jump to a place"
define(XJUMPT,  30)             # "Jump to a place, if true"
define(XJUMPF,  40)             # "Jump to a place, if false"
define(XCALL,   50)             # "Call a subprogram"
define(XRET,    60)             # "Return from a subprogram"

define(XPUSH,  110)             # "Push an immediate value"
define(XSWAP,  120)             # "Swap top two stack entries"

define(XLT,    200)             # "Less than?"
define(XADDI,  210)             # "Add immediate."

define(XPPUSH, 610)             # "Push top to precedence stack"
define(XPCOPY, 620)             # "Copy top of prec stack to top"
define(XPDROP, 630)             # "Drop top of precedence stack"

define(XGETTK, 710)             # "Get the next token"
define(XTOKEQ, 720)             # "Token equals the argument?"
define(XEXPCT, 730)             # "Expect token"
define(XACCPT, 740)             # "Accept token"

define(XTOK,   810)             # "Push the token number"
define(XBINOP, 820)             # "Is top a binary operator?"
define(XRASSC, 830)             # "Is top a right associative op?"
define(XPREC,  840)             # "Precedence of token no. on top"
define(XOPER,  850)             # "Operator for token no. on top"

define(XINTND, 970)             # "Make internal node"
define(XOPND,  975)             # "Make internal node for operator"
define(XLEFND, 980)             # "Make leaf node"
define(XNILND, 985)             # "Make nil node"

define(XERROR, 1010)            # "Error"
define(XRWARN, 1020)            # "Unused right associative branch"

define(XPING,  2010)    # Print a ping message (for debugging)
define(XPRTND, 2020)    # Print node at stack top (for debugging)
define(XPRTTP, 2030)    # Print stack top as integer (for debugging)
define(XPRTTK, 2040)    # Print the current token (for debugging)
define(XPRTP,  2050)    # Print the current precedence (for debugging)
define(XPRTST, 2060)    # Print the whole data stack (for debugging)

# Call and jump locations in our program:
define(CSTMT,  1000)            # stmt
define(STMT01, 1010)
define(STMT02, 1020)
define(STMT03, 1030)
define(STMT04, 1040)
define(STMT05, 1050)
define(STMT06, 1060)
define(STMT07, 1070)
define(STMT08, 1080)
define(STMT09, 1090)
define(STMT10, 1100)
define(STMT11, 1110)
define(STMT12, 1120)
define(STMT13, 1130)
define(STMT14, 1140)
define(STMT15, 1150)
define(CPEXPR, 2000)            # paren_expr
define(CEXPR,  3000)            # expr
define(EXPR01, 3010)
define(EXPR02, 3020)
define(EXPR03, 3030)
define(EXPR04, 3040)
define(EXPR05, 3050)
define(EXPR06, 3060)
define(EXPR10, 3100)
define(EXPR11, 3110)
define(EXPR12, 3120)
define(EXPR13, 3130)
define(PARS01, 4010)            # parse

# Error numbers.
define(EXSTMT, 100)             # "expecting start of statement"
define(EXPRIM, 200)             # "expecting a primary"

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

subroutine ld(code, i, instr1, instr2)

  implicit none

  integer code(*)
  integer i
  integer instr1, instr2

  code(i) = instr1
  code(i + 1) = instr2
  i = i + 2
end

subroutine ldcode (code)

  # Load the code that is in the recursive language. The array
  # allocated to hold the code must be large enough, but we do not
  # check.

  implicit none

  integer code(*)
  integer i

  i = 1

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

  # The main loop.
  call ld (code, i, XNILND, XPAD) # Nil node for start of sequence.
  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XLOC, PARS01) # Top of loop
  call ld (code, i, XCALL, CSTMT)
  call ld (code, i, XINTND, NDSEQ)
  call ld (code, i, XTOKEQ, TKEOI) # End_of_input
  call ld (code, i, XJUMPF, PARS01) # Loop unless end of input.
  call ld (code, i, XRET, XPAD)

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

  call ld (code, i, XLOC, CEXPR)  # Start of "expr"
  call ld (code, i, XPPUSH, XPAD) # Push the precedence argument.

  call ld (code, i, XTOKEQ, TKLPAR) # LeftParen
  call ld (code, i, XJUMPF, EXPR01)

  # "( ... )"
  call ld (code, i, XCALL, CPEXPR)
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR01)

  call ld (code, i, XACCPT, TKSUB) # Op_subtract
  call ld (code, i, XJUMPF, EXPR02)

  # Unary minus
  call ld (code, i, XPUSH, TKNEG)
  call ld (code, i, XPREC, XPAD)
  call ld (code, i, XCALL, CEXPR)  # expr <--
  call ld (code, i, XNILND, XPAD)  # expr nil <--
  call ld (code, i, XINTND, NDNEG)
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR02)

  call ld (code, i, XACCPT, TKADD) # Op_add
  call ld (code, i, XJUMPF, EXPR03)

  # Unary plus
  call ld (code, i, XPUSH, TKNEG)
  call ld (code, i, XPREC, XPAD)
  call ld (code, i, XCALL, CEXPR)  # expr <--
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR03)

  call ld (code, i, XACCPT, TKNOT) # Op_not
  call ld (code, i, XJUMPF, EXPR04)

  # "!"
  call ld (code, i, XPUSH, TKNOT)
  call ld (code, i, XPREC, XPAD)
  call ld (code, i, XCALL, CEXPR)  # expr <--
  call ld (code, i, XNILND, XPAD)  # expr nil <--
  call ld (code, i, XINTND, NDNOT)
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR04)

  call ld (code, i, XTOKEQ, TKID) # Identifier
  call ld (code, i, XJUMPF, EXPR05)

  # Identifier
  call ld (code, i, XLEFND, NDID)
  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR05)

  call ld (code, i, XTOKEQ, TKINT) # Integer
  call ld (code, i, XJUMPF, EXPR06)

  # Integer.
  call ld (code, i, XLEFND, NDINT)
  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XJUMP, EXPR10)

  call ld (code, i, XLOC, EXPR06)

  call ld (code, i, XERROR, EXPRIM)

  call ld (code, i, XLOC, EXPR10) # Top of precedence climbing loop

  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XBINOP, XPAD)
  call ld (code, i, XJUMPF, EXPR11) # Exit loop, if not a binary op.

  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XPREC, XPAD)    # curtok_prec <--
  call ld (code, i, XPCOPY, XPAD)   # curtok_prec p <--
  call ld (code, i, XLT, XPAD)      # (curtok_prec < p)? <--
  call ld (code, i, XJUMPT, EXPR11) # Exit loop if true.

  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XOPER, XPAD) # x op <--
  call ld (code, i, XSWAP, XPAD) # op x <--

  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XRASSC, XPAD)
  call ld (code, i, XJUMPT, EXPR12)

  # Left associative.
  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XPREC, XPAD)
  call ld (code, i, XADDI, 1)   # op x q:=(q + 1) <--
  call ld (code, i, XJUMP, EXPR13)

  call ld (code, i, XLOC, EXPR12)

  # Right associative. (Currently an unused branch.)
  call ld (code, i, XRWARN, XPAD) # Warn about unused branch.
  call ld (code, i, XTOK, XPAD)
  call ld (code, i, XPREC, XPAD) # op x q <--

  call ld (code, i, XLOC, EXPR13)

  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XCALL, CEXPR) # op x expr(q) <--
  call ld (code, i, XOPND, XPAD)  # new_x <--

  call ld (code, i, XJUMP, EXPR10) # Continue looping.

  call ld (code, i, XLOC, EXPR11) # Loop exit.

  call ld (code, i, XPDROP, XPAD) # Drop the precedence argument.
  call ld (code, i, XRET, XPAD)   # End of "expr"

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

  call ld (code, i, XLOC, CPEXPR) # Start of "paren_expr"
  call ld (code, i, XEXPCT, TKLPAR)
  call ld (code, i, XPUSH, 0)
  call ld (code, i, XCALL, CEXPR)
  call ld (code, i, XEXPCT, TKRPAR)
  call ld (code, i, XRET, XPAD)

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

  call ld (code, i, XLOC, CSTMT)  # Start of "stmt"

  call ld (code, i, XACCPT, TKIF) # Keyword_if
  call ld (code, i, XJUMPF, STMT01)

  # "if (...) then ... else ..."
  call ld (code, i, XCALL, CPEXPR)  # Get the paren expr ("if (...)").
  call ld (code, i, XCALL, CSTMT)   # Get the "then" clause.
  call ld (code, i, XACCPT, TKELSE) # Keyword_else
  call ld (code, i, XJUMPF, STMT02)
  call ld (code, i, XCALL, CSTMT) # Get the "else" clause.
  call ld (code, i, XJUMP, STMT03)
  call ld (code, i, XLOC, STMT02)
  call ld (code, i, XNILND, XPAD) # The "else" statement is nil.
  call ld (code, i, XLOC, STMT03)
  call ld (code, i, XINTND, NDIF) # ("If" pred ("If" then else))
  call ld (code, i, XINTND, NDIF)
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT01)

  call ld (code, i, XACCPT, TKPUTC) # Keyword_putc
  call ld (code, i, XJUMPF, STMT04)

  # "putc (...);"
  call ld (code, i, XCALL, CPEXPR) # Get the paren expr.
  call ld (code, i, XNILND, XPAD)
  call ld (code, i, XINTND, NDPRTC) # ("Prtc" expr nil)
  call ld (code, i, XEXPCT, TKSEMI) # Expect ";"
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT04)

  call ld (code, i, XACCPT, TKPRNT) # Keyword_print
  call ld (code, i, XJUMPF, STMT05)

  # "print(... , ... , ...);"
  call ld (code, i, XEXPCT, TKLPAR) # Expect "("
  call ld (code, i, XNILND, XPAD)   # nil for start of sequence
  call ld (code, i, XLOC, STMT08)   # Top of loop
  call ld (code, i, XTOKEQ, TKSTR)
  call ld (code, i, XJUMPT, STMT06)
  call ld (code, i, XPUSH, 0)
  call ld (code, i, XCALL, CEXPR)
  call ld (code, i, XNILND, XPAD)
  call ld (code, i, XINTND, NDPRTI) # ("Prti" expr nil)
  call ld (code, i, XJUMP, STMT07)
  call ld (code, i, XLOC, STMT06)
  call ld (code, i, XLEFND, NDSTR)
  call ld (code, i, XNILND, XPAD)
  call ld (code, i, XINTND, NDPRTS) # ("Prts" ("String" ...) nil)
  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XLOC, STMT07)
  call ld (code, i, XINTND, NDSEQ)  # ("Sequence" ... ...)
  call ld (code, i, XACCPT, TKCMMA) # Comma
  call ld (code, i, XJUMPT, STMT08) # Loop if comma.
  call ld (code, i, XEXPCT, TKRPAR) # Expect ")"
  call ld (code, i, XEXPCT, TKSEMI) # Expect ";"
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT05)

  call ld (code, i, XACCPT, TKSEMI) # Semicolon
  call ld (code, i, XJUMPF, STMT09)

  # Accept a lone ";".
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT09)

  call ld (code, i, XTOKEQ, TKID) # Identifier
  call ld (code, i, XJUMPF, STMT10)

  # "identifier = expr;"
  call ld (code, i, XLEFND, NDID) # ("Identifier" ...)
  call ld (code, i, XGETTK, XPAD)
  call ld (code, i, XEXPCT, TKASGN)
  call ld (code, i, XPUSH, 0)
  call ld (code, i, XCALL, CEXPR)
  call ld (code, i, XINTND, NDASGN) # ("Assign" ("Identifier" ...) expr)
  call ld (code, i, XEXPCT, TKSEMI)
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT10)

  call ld (code, i, XACCPT, TKWHIL) # While
  call ld (code, i, XJUMPF, STMT11)

  # "while (...) ..."
  call ld (code, i, XCALL, CPEXPR)
  call ld (code, i, XCALL, CSTMT)
  call ld (code, i, XINTND, NDWHIL) # ("While" pred stmt)
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT11)

  call ld (code, i, XACCPT, TKLBRC) # LeftBrace
  call ld (code, i, XJUMPF, STMT12)

  # "{ ... }"
  call ld (code, i, XNILND, XPAD)   # nil for start of sequence
  call ld (code, i, XLOC, STMT13)   # Top of loop
  call ld (code, i, XTOKEQ, TKEOI)
  call ld (code, i, XJUMPT, STMT14)
  call ld (code, i, XTOKEQ, TKRBRC)
  call ld (code, i, XJUMPT, STMT14)
  call ld (code, i, XCALL, CSTMT)
  call ld (code, i, XINTND, NDSEQ)  # ("Sequence" ... ...)
  call ld (code, i, XJUMP, STMT13)  # Loop again.
  call ld (code, i, XLOC, STMT14)   # Loop exit
  call ld (code, i, XEXPCT, TKRBRC) # Expect ";".
  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT12)

  call ld (code, i, XTOKEQ, TKEOI) # End_of_input
  call ld (code, i, XJUMPF, STMT15)

  call ld (code, i, XRET, XPAD)

  call ld (code, i, XLOC, STMT15)

  call ld (code, i, XERROR, EXSTMT) # "expecting start of stmt"

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

end

subroutine dtpush (dstack, idstck, x)

  # Push to the data stack.

  implicit none

  integer dstack(DSTKSZ)
  integer idstck
  integer x

  if (DSTKSZ < idstck)
    {
      write (*, '(''node stack exhausted'')')
      stop
    }
  dstack(idstck) = x
  idstck = idstck + 1
end

function dtpop (dstack, idstck)

  # Pop from the data stack.

  implicit none

  integer dstack(DSTKSZ)
  integer idstck
  integer dtpop

  if (DSTKSZ < idstck)
    {
      write (*, '(''node stack exhausted'')')
      stop
    }
  idstck = idstck - 1
  dtpop = dstack(idstck)
end

subroutine ppush (pstack, ipstck, x)

  # Push to the precedence stack.

  implicit none

  integer pstack(PSTKSZ)
  integer ipstck
  integer x

  if (PSTKSZ < ipstck)
    {
      write (*, '(''precedence stack exhausted'')')
      stop
    }
  pstack(ipstck) = x
  ipstck = ipstck + 1
end

function ppop (pstack, ipstck)

  # Pop from the precedence stack.

  implicit none

  integer pstack(PSTKSZ)
  integer ipstck
  integer ppop

  if (PSTKSZ < ipstck)
    {
      write (*, '(''precedence stack exhausted'')')
      stop
    }
  ipstck = ipstck - 1
  ppop = pstack(ipstck)
end

function ipfind (code, loc)

  # Find a location.

  implicit none

  integer code(*)
  integer loc
  integer ipfind

  integer i

  i = 1
  while (code(i) != XLOC || code(i + 1) != loc)
    i = i + 2
  ipfind = i
end

subroutine ippush (xstack, ixstck, ip)

  # Push the instruction pointer.

  implicit none

  integer xstack(XSTKSZ)
  integer ixstck
  integer ip

  if (XSTKSZ < ixstck)
    {
      write (*, '(''recursive call stack exhausted'')')
      stop
    }
  xstack(ixstck) = ip
  ixstck = ixstck + 1
end

function ippop (xstack, ixstck)

  # Pop an instruction pointer value.

  implicit none

  integer xstack(XSTKSZ)
  integer ixstck
  integer ippop

  if (ixstck == 1)
    {
      write (*, '(''recursive call stack underflow'')')
      stop
    }
  ixstck = ixstck - 1
  ippop = xstack(ixstck)
end

function logl2i (u)

  # Convert LOGICAL to INTEGER.

  implicit none

  logical u
  integer logl2i

  if (u)
    logl2i = 1
  else
    logl2i = 0
end

subroutine recurs (strngs, istrng,
                   nodes, inodes, _
                   dstack, idstck, _
                   pstack, ipstck, _
                   xstack, ixstck, _
                   code, ip)

  implicit none

  character strngs(STRNSZ)      # String pool.
  integer istrng                # String pool's next slot.
  integer nodes(NODESZ, NODSSZ) # Node pool
  integer inodes                # Node pool's next slot.
  integer dstack(DSTKSZ)        # Data stack.
  integer idstck                # Data stack pointer.
  integer pstack(PSTKSZ)        # Precedence stack.
  integer ipstck                # Precedence stack pointer.
  integer xstack(XSTKSZ)        # Execution stack.
  integer ixstck                # Execution stack pointer.
  integer code(*)               # Code in the recursive language.
  integer ip                    # Instruction pointer.

  integer prec
  integer opernt
  integer logl2i
  integer dtpop
  integer ppop
  integer ippop
  integer ipfind
  logical accept
  logical isbin
  logical rtassc

  integer curtok(TOKSZ)
  integer node(NODESZ)
  integer curprc                # Current precedence value.
  integer i, j
  logical done

  curprc = 0
  done = .false.
  while (.not. done)
    {
      if (code(ip) == XLOC)
        {
          ip = ip + 2
        }
      else if (code(ip) == XJUMP)
        {
          ip = ipfind (code, code(ip + 1))
        }
      else if (code(ip) == XJUMPT)
        {
          i = dtpop (dstack, idstck)
          if (i != 0)
            ip = ipfind (code, code(ip + 1))
          else
            ip = ip + 2
        }
      else if (code(ip) == XJUMPF)
        {
          i = dtpop (dstack, idstck)
          if (i == 0)
            ip = ipfind (code, code(ip + 1))
          else
            ip = ip + 2
        }
      else if (code(ip) == XCALL)
        {
          call ippush (xstack, ixstck, ip + 2)
          ip = ipfind (code, code(ip + 1))
        }
      else if (code(ip) == XRET)
        {
          if (ixstck == 1)
            done = .true.
          else
            ip = ippop (xstack, ixstck)
        }
      else if (code(ip) == XINTND)
        {
          node(NRIGHT) = dtpop (dstack, idstck)
          node(NLEFT) = dtpop (dstack, idstck)
          node(NTAG) = code(ip + 1)
          call astnod (node, nodes, inodes, i)
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XOPND)
        {
          node(NRIGHT) = dtpop (dstack, idstck)
          node(NLEFT) = dtpop (dstack, idstck)
          node(NTAG) = dtpop (dstack, idstck)
          call astnod (node, nodes, inodes, i)
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XLEFND)
        {
          node(NITV) = curtok(ITV)
          node(NITN) = curtok(ITN)
          node(NTAG) = code(ip + 1)
          call astnod (node, nodes, inodes, i)
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XNILND)
        {
          call dtpush (dstack, idstck, NIL)
          ip = ip + 2
        }
      else if (code(ip) == XGETTK)
        {
          call gettok (strngs, istrng, curtok)
          ip = ip + 2
        }
      else if (code(ip) == XTOKEQ)
        {
          i = logl2i (curtok(ITK) == code(ip + 1))
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XEXPCT)
        {
          call expect (strngs, istrng, curtok, code(ip + 1))
          ip = ip + 2
        }
      else if (code(ip) == XACCPT)
        {
          i = logl2i (accept (strngs, istrng, curtok, code(ip + 1)))
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XSWAP)
        {
          i = dtpop (dstack, idstck)
          j = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, i)
          call dtpush (dstack, idstck, j)
          ip = ip + 2
        }
      else if (code(ip) == XLT)
        {
          j = dtpop (dstack, idstck)
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, logl2i (i < j))
          ip = ip + 2
        }
      else if (code(ip) == XADDI)
        {
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, i + code(ip + 1))
          ip = ip + 2
        }
      else if (code(ip) == XPPUSH)
        {
          i = dtpop (dstack, idstck)
          call ppush (pstack, ipstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XPCOPY)
        {
          i = ppop (pstack, ipstck)
          call ppush (pstack, ipstck, i)
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XPDROP)
        {
          i = ppop (pstack, ipstck)
          ip = ip + 2
        }
      else if (code(ip) == XBINOP)
        {
          i = dtpop (dstack, idstck)
          i = logl2i (isbin (i))
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XRASSC)
        {
          i = dtpop (dstack, idstck)
          i = logl2i (rtassc (i))
          call dtpush (dstack, idstck, i)
          ip = ip + 2
        }
      else if (code(ip) == XPREC)
        {
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, prec (i))
          ip = ip + 2
        }
      else if (code(ip) == XOPER)
        {
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, opernt (i))
          ip = ip + 2
        }
      else if (code(ip) == XTOK)
        {
          call dtpush (dstack, idstck, curtok(ITK))
          ip = ip + 2
        }
      else if (code(ip) == XPUSH)
        {
          call dtpush (dstack, idstck, code(ip + 1))
          ip = ip + 2
        }
      else if (code(ip) == XERROR)
        {
          if (code(ip + 1) == EXSTMT)
            {
              write (*, 1000) curtok(ILN), curtok(ICN)
1000          format ('expected start of statement at line ', _
                      I5, ', column ', I5)
            }
          else if (code(ip + 1) == EXPRIM)
            {
              write (*, 1010) curtok(ILN), curtok(ICN)
1010          format ('expected a primary at line ', _
                      I5, ', column ', I5)
            }
          else
            {
              write (*, 2000) curtok(ILN), curtok(ICN)
2000          format ('syntax error at line ', _
                      I5, ', column ', I5)
            }
          stop
        }
      else if (code(ip) == XRWARN)
        {
          write (*, 3000)
3000      format ('executing supposedly unused ', _
                  '"right associative" operator branch')
          ip = ip + 2
        }
      else if (code(ip) == XPING)
        {
          write (*, '(''ping'')')
          ip = ip + 2
        }
      else if (code(ip) == XPRTND)
        {
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, i)
          call prtast (strngs, nodes, i, dstack)
          ip = ip + 2
        }
      else if (code(ip) == XPRTTP)
        {
          i = dtpop (dstack, idstck)
          call dtpush (dstack, idstck, i)
          write (*, '(''top = '', I20)') i
          ip = ip + 2
        }
      else if (code(ip) == XPRTTK)
        {
          write (*, '(''curtok ='', 5(1X, I5))') curtok
          ip = ip + 2
        }
      else if (code(ip) == XPRTP)
        {
          write (*, '(''curprc = '', I2)') curprc
          ip = ip + 2
        }
      else if (code(ip) == XPRTST)
        {
          write (*, '(''dstack ='', 100000(1X, I5))') _
                (dstack(i), i = 1, idstck - 1)
          ip = ip + 2
        }
      else
        {
          write (*, '(''illegal instruction'')')
          stop
        }
    }
end

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

program parse

  implicit none

  character strngs(STRNSZ)      # String pool.
  integer istrng                # String pool's next slot.
  integer nodes(NODESZ, NODSSZ) # Node pool
  integer inodes                # Node pool's next slot.
  integer dstack(DSTKSZ)        # Node stack.
  integer idstck                # Node stack pointer.
  integer pstack(PSTKSZ)        # Precedence stack.
  integer ipstck                # Precedence stack pointer.
  integer xstack(XSTKSZ)        # Execution stack.
  integer ixstck                # Execution stack pointer.
  integer code(1000)            # Recursive code.
  integer ip                    # Instruction pointer.

  integer i

  integer dtpop

  istrng = 1
  inodes = 1
  idstck = 1
  ipstck = 1
  ixstck = 1

  call ldcode (code)
  ip = 1

  call recurs (strngs, istrng, nodes, inodes, _
               dstack, idstck, pstack, ipstck, _
               xstack, ixstck, code, ip)
  i = dtpop (dstack, idstck)
  call prtast (strngs, nodes, i, dstack)
end

######################################################################


DO i = 2, 100 ! 1 is HicEst.exe
  EDIT(Text=$CMD_LINE, SePaRators='-"', ITeM=i, IF ' ', EXit, ENDIF, Parse=cmd, GetPosition=position)
  IF(position > 0) WRITE(Messagebox) cmd
ENDDO


procedure main(arglist)
every write(!arglist)
end


//This is a comment.
//This is other comment.

/* This is a comment too. */

/* This is a
multi-line
comment */


Comment Stew.

This is a comment.
The other comment is a loop, but you can name it anything (single word only).
You can also name ingredients as comments
This is pseudocode.

Ingredients.
Ingredient list

Method.
Methods.
SingleWordCommentOne the Ingredient.
Methods.
SingleWordCommentTwo until SingleWordCommentOned.
Methods.


package xyz.hyperreal.rosettacodeCompiler

import scala.io.Source

object SyntaxAnalyzer {

  val symbols =
    Map[String, (PrefixOperator, InfixOperator)](
      "Op_or"           -> (null, InfixOperator(10, LeftAssoc, BranchNode("Or", _, _))),
      "Op_and"          -> (null, InfixOperator(20, LeftAssoc, BranchNode("And", _, _))),
      "Op_equal"        -> (null, InfixOperator(30, LeftAssoc, BranchNode("Equal", _, _))),
      "Op_notequal"     -> (null, InfixOperator(30, LeftAssoc, BranchNode("NotEqual", _, _))),
      "Op_less"         -> (null, InfixOperator(40, LeftAssoc, BranchNode("Less", _, _))),
      "Op_lessequal"    -> (null, InfixOperator(40, LeftAssoc, BranchNode("LessEqual", _, _))),
      "Op_greater"      -> (null, InfixOperator(40, LeftAssoc, BranchNode("Greater", _, _))),
      "Op_greaterequal" -> (null, InfixOperator(40, LeftAssoc, BranchNode("GreaterEqual", _, _))),
      "Op_add"          -> (PrefixOperator(30, identity), InfixOperator(50, LeftAssoc, BranchNode("Add", _, _))),
      "Op_minus" -> (PrefixOperator(70, BranchNode("Negate", _, TerminalNode)), InfixOperator(
        50,
        LeftAssoc,
        BranchNode("Subtract", _, _))),
      "Op_multiply" -> (null, InfixOperator(60, LeftAssoc, BranchNode("Multiply", _, _))),
      "Op_divide"   -> (null, InfixOperator(60, LeftAssoc, BranchNode("Divide", _, _))),
      "Op_mod"      -> (null, InfixOperator(60, RightAssoc, BranchNode("Mod", _, _))),
      "Op_not"      -> (PrefixOperator(70, BranchNode("Not", _)), null),
      "LeftParen"   -> null,
      "RightParen"  -> null
    )

  def apply = new SyntaxAnalyzer(symbols)

  abstract class Node
  case class LeafNode(name: String, value: String)                            extends Node
  case class BranchNode(name: String, left: Node, right: Node = TerminalNode) extends Node
  case object TerminalNode                                                    extends Node

  abstract class Assoc
  case object LeftAssoc  extends Assoc
  case object RightAssoc extends Assoc

  abstract class Operator
  case class InfixOperator(prec: Int, assoc: Assoc, compute: (Node, Node) => Node) extends Operator
  case class PrefixOperator(prec: Int, compute: Node => Node)                      extends Operator

}

class SyntaxAnalyzer(symbols: Map[String, (SyntaxAnalyzer.PrefixOperator, SyntaxAnalyzer.InfixOperator)]) {
  import SyntaxAnalyzer.{BranchNode, InfixOperator, LeafNode, LeftAssoc, Node, PrefixOperator, TerminalNode}

  def fromStdin = fromSource(Source.stdin)

  def fromString(src: String) = fromSource(Source.fromString(src))

  def fromSource(s: Source) = {
    val tokens = ((s.getLines map (_.trim.split(" +", 4)) map {
      case Array(line, col, name) =>
        symbols get name match {
          case None | Some(null) => SimpleToken(line.toInt, col.toInt, name)
          case Some(operators)   => OperatorToken(line.toInt, col.toInt, name, operators)
        }
      case Array(line, col, name, value) => ValueToken(line.toInt, col.toInt, name, value)
    }) toStream)

    flatten(parse(tokens))
  }

  def flatten(n: Node): Unit =
    n match {
      case TerminalNode          => println(";")
      case LeafNode(name, value) => println(s"$name $value")
      case BranchNode(name, left, right) =>
        println(name)
        flatten(left)
        flatten(right)
    }

  def parse(toks: Stream[Token]) = {
    var cur = toks

    def next = cur = cur.tail

    def token = cur.head

    def consume = {
      val res = token

      next
      res
    }

    def accept(name: String) =
      if (token.name == name) {
        next
        true
      } else
        false

    def expect(name: String, error: String = null) =
      if (token.name != name)
        sys.error(if (error eq null) s"expected $name, found ${token.name}" else s"$error: $token")
      else
        next

    def expression(minPrec: Int): Node = {
      def infixOperator = token.asInstanceOf[OperatorToken].operators._2

      def isInfix = token.isInstanceOf[OperatorToken] && infixOperator != null

      var result =
        consume match {
          case SimpleToken(_, _, "LeftParen") =>
            val result = expression(0)

            expect("RightParen", "expected closing parenthesis")
            result
          case ValueToken(_, _, name, value)                         => LeafNode(name, value)
          case OperatorToken(_, _, _, (prefix, _)) if prefix ne null => prefix.compute(expression(prefix.prec))
          case OperatorToken(_, _, _, (_, infix)) if infix ne null =>
            sys.error(s"expected a primitive expression, not an infix operator: $token")
        }

      while (isInfix && infixOperator.prec >= minPrec) {
        val InfixOperator(prec, assoc, compute) = infixOperator
        val nextMinPrec                         = if (assoc == LeftAssoc) prec + 1 else prec

        next
        result = compute(result, expression(nextMinPrec))
      }

      result
    }

    def parenExpression = {
      expect("LeftParen")

      val e = expression(0)

      expect("RightParen")
      e
    }

    def statement: Node = {
      var stmt: Node = TerminalNode

      if (accept("Keyword_if"))
        stmt = BranchNode("If",
                          parenExpression,
                          BranchNode("If", statement, if (accept("Keyword_else")) statement else TerminalNode))
      else if (accept("Keyword_putc")) {
        stmt = BranchNode("Prtc", parenExpression)
        expect("Semicolon")
      } else if (accept("Keyword_print")) {
        expect("LeftParen")

        do {
          val e =
            if (token.name == "String")
              BranchNode("Prts", LeafNode("String", consume.asInstanceOf[ValueToken].value))
            else
              BranchNode("Prti", expression(0))

          stmt = BranchNode("Sequence", stmt, e)
        } while (accept("Comma"))

        expect("RightParen")
        expect("Semicolon")
      } else if (token.name == "Semicolon")
        next
      else if (token.name == "Identifier") {
        val ident = LeafNode("Identifier", consume.asInstanceOf[ValueToken].value)

        expect("Op_assign")
        stmt = BranchNode("Assign", ident, expression(0))
        expect("Semicolon")
      } else if (accept("Keyword_while"))
        stmt = BranchNode("While", parenExpression, statement)
      else if (accept("LeftBrace")) {
        while (token.name != "RightBrace" && token.name != "End_of_input") {
          stmt = BranchNode("Sequence", stmt, statement)
        }

        expect("RightBrace")
      } else
        sys.error(s"syntax error: $token")

      stmt
    }

    var tree: Node = TerminalNode

    do {
      tree = BranchNode("Sequence", tree, statement)
    } while (token.name != "End_of_input")

    expect("End_of_input")
    tree
  }

  abstract class Token {
    val line: Int;
    val col: Int;
    val name: String
  }

  case class SimpleToken(line: Int, col: Int, name: String)                                               extends Token
  case class ValueToken(line: Int, col: Int, name: String, value: String)                                 extends Token
  case class OperatorToken(line: Int, col: Int, name: String, operators: (PrefixOperator, InfixOperator)) extends Token

}


public class Arguments {
  public static void main(String[] args) {
     System.out.println("There are " + args.length + " arguments given.");
     for(int i = 0; i < args.length; i++)
        System.out.println("The argument #" + (i+1) + " is " + args[i] + " and is at index " + i);
  }
}


process.argv.forEach((val, index) => {
  console.log(`${index}: ${val}`);
});


$ARGS.positional contains an array of the positional arguments as JSON strings


Start = /* This is a multi-
           line comment     */ 17 // This is a single-line comment


;; This is a comment
(defn foo []
  123) ; also a comment


* an asterisk in 7th column comments the line out


(import (scheme base)
        (scheme process-context)
        (scheme write))

(define *names* (list (cons 'Op_add 'Add)
                      (cons 'Op_subtract 'Subtract)
                      (cons 'Op_multiply 'Multiply)
                      (cons 'Op_divide 'Divide)
                      (cons 'Op_mod 'Mod)
                      (cons 'Op_not 'Not)
                      (cons 'Op_equal 'Equal)
                      (cons 'Op_notequal 'NotEqual)
                      (cons 'Op_or 'Or)
                      (cons 'Op_and 'And)
                      (cons 'Op_less 'Less)
                      (cons 'Op_lessequal 'LessEqual)
                      (cons 'Op_greater 'Greater)
                      (cons 'Op_greaterequal 'GreaterEqual)))

(define (retrieve-name type)
  (let ((res (assq type *names*)))
    (if res
      (cdr res)
      (error "Unknown type name"))))

;; takes a vector of tokens
(define (parse tokens) ; read statements, until hit end of tokens
  (define posn 0)
  (define (peek-token)
    (vector-ref tokens posn))
  (define (get-token)
    (set! posn (+ 1 posn))
    (vector-ref tokens (- posn 1)))
  (define (match type)
    (if (eq? (car (vector-ref tokens posn)) type)
      (set! posn (+ 1 posn))
      (error "Could not match token type" type)))
  ; make it easier to read token parts
  (define type car)
  (define value cadr)
  ;
  ;; left associative read of one or more items with given separators
  (define (read-one-or-more reader separators)
    (let loop ((lft (reader)))
      (let ((next (peek-token)))
        (if (memq (type next) separators)
          (begin (match (type next))
                 (loop (list (retrieve-name (type next)) lft (reader))))
          lft))))
  ;
  ;; read one or two items with given separator
  (define (read-one-or-two reader separators)
    (let* ((lft (reader))
           (next (peek-token)))
      (if (memq (type next) separators)
        (begin (match (type next))
               (list (retrieve-name (type next)) lft (reader)))
        lft)))
  ;
  (define (read-primary)
    (let ((next (get-token)))
      (case (type next)
        ((Identifier Integer)
         next)
        ((LeftParen)
         (let ((v (read-expr)))
           (match 'RightParen)
           v))
        ((Op_add) ; + sign is ignored
         (read-primary))
        ((Op_not)
         (list 'Not (read-primary) '()))
        ((Op_subtract)
         (list 'Negate (read-primary) '()))
        (else
          (error "Unknown primary type")))))
  ;
  (define (read-multiplication-expr) ; *
    (read-one-or-more read-primary '(Op_multiply Op_divide Op_mod)))
  ;
  (define (read-addition-expr) ; *
    (read-one-or-more read-multiplication-expr '(Op_add Op_subtract)))
  ;
  (define (read-relational-expr) ; ?
    (read-one-or-two read-addition-expr
                     '(Op_less Op_lessequal Op_greater Op_greaterequal)))
  ;
  (define (read-equality-expr) ; ?
    (read-one-or-two read-relational-expr '(Op_equal Op_notequal)))
  ;
  (define (read-and-expr) ; *
    (read-one-or-more read-equality-expr '(Op_and)))
  ;
  (define (read-expr) ; *
    (read-one-or-more read-and-expr '(Op_or)))
  ;
  (define (read-prt-list)
    (define (read-print-part)
      (if (eq? (type (peek-token)) 'String)
        (list 'Prts (get-token) '())
        (list 'Prti (read-expr) '())))
    ;
    (do ((tok (read-print-part) (read-print-part))
         (rec '() (list 'Sequence rec tok)))
      ((not (eq? (type (peek-token)) 'Comma))
       (list 'Sequence rec tok))
      (match 'Comma)))
  ;
  (define (read-paren-expr)
    (match 'LeftParen)
    (let ((v (read-expr)))
      (match 'RightParen)
      v))
  ;
  (define (read-stmt)
    (case (type (peek-token))
      ((SemiColon)
       '())
      ((Identifier)
       (let ((id (get-token)))
         (match 'Op_assign)
         (let ((ex (read-expr)))
           (match 'Semicolon)
           (list 'Assign id ex))))
      ((Keyword_while)
       (match 'Keyword_while)
       (let* ((expr (read-paren-expr))
              (stmt (read-stmt)))
         (list 'While expr stmt)))
      ((Keyword_if)
       (match 'Keyword_if)
       (let* ((expr (read-paren-expr))
              (then-part (read-stmt))
              (else-part (if (eq? (type (peek-token)) 'Keyword_else)
                           (begin (match 'Keyword_else)
                                  (read-stmt))
                           '())))
         (list 'If expr (list 'If then-part else-part))))
      ((Keyword_print)
       (match 'Keyword_print)
       (match 'LeftParen)
       (let ((v (read-prt-list)))
         (match 'RightParen)
         (match 'Semicolon)
         v))
      ((Keyword_putc)
       (match 'Keyword_putc)
       (let ((v (read-paren-expr)))
         (match 'Semicolon)
         (list 'Putc v '())))
      ((LeftBrace)
       (match 'LeftBrace)
       (let ((v (read-stmts)))
         (match 'RightBrace)
         v))
      (else
        (error "Unknown token type for statement" (type (peek-token))))))
  ;
  (define (read-stmts)
    (do ((sequence (list 'Sequence '() (read-stmt))
                   (list 'Sequence sequence (read-stmt))))
      ((memq (type (peek-token)) '(End_of_input RightBrace))
       sequence)))
  ;
  (let ((res (read-stmts)))
    (match 'End_of_input)
    res))

;; reads tokens from file, parses and returns the AST
(define (parse-file filename)
  (define (tokenise line)
    (let ((port (open-input-string line)))
      (read port) ; discard line
      (read port) ; discard col
      (let* ((type (read port)) ; read type as symbol
             (val (read port))) ; check for optional value
        (if (eof-object? val)
          (list type)
          (list type val)))))
  ;
  (with-input-from-file
    filename
    (lambda ()
      (do ((line (read-line) (read-line))
           (toks '() (cons (tokenise line) toks)))
        ((eof-object? line)
         (parse (list->vector (reverse toks))))))))

;; Output the AST in flattened format
(define (display-ast ast)
  (cond ((null? ast)
         (display ";\n"))
        ((= 2 (length ast))
         (display (car ast))
         (display #\tab)
         (write (cadr ast)) ; use write to preserve " " on String
         (newline))
        (else
          (display (car ast)) (newline)
          (display-ast (cadr ast))
          (display-ast (cadr (cdr ast))))))

;; read from filename passed on command line
(if (= 2 (length (command-line)))
  (display-ast (parse-file (cadr (command-line))))
  (display "Error: provide program filename\n"))


using Printf

prog = Base.basename(Base.source_path())

println(prog, "'s command-line arguments are:")
for s in ARGS
    println("    ", s)
end


fun main(args: Array<String>) {
     println("There are " + args.size + " arguments given.")
     args.forEachIndexed { i, a -> println("The argument #${i+1} is $a and is at index $i") }
}


#!/usr/bin/lasso9

iterate($argv) => {
  stdoutnl("Argument " + loop_count + ": " + loop_value)
}


$ ./bin/lfe -pa ebin/ -c "alpha beta" -h "gamma"


# one line comment

### multi
line
comment ###


As ColdFusion's grammar is based around HTML syntax, commenting is similar to HTML.
<!--- This is a comment.  Nothing in this tag can be seen by the end user.
       Note the three-or-greater dashes to open and close the tag. --->
<!--  This is an HTML comment.  Any HTML between the opening and closing of the tag will be ignored, but any ColdFusion code will still run.
       Note that in the popular FuseBox framework for ColdFusion, the circuit.xml files require that you use this style of comment. -->


;;;; This code implements the foo and bar functions

;;; The foo function calls bar on the first argument and multiplies the result by the second.
;;; The arguments are two integers
(defun foo (a b)
   ;; Call bar and multiply
   (* (bar a) ; Calling bar
      b))

;;; The bar function simply adds 3 to the argument
(defun bar (n)
   (+ n 3))


(* Comments (* can nest *)
   and they can span multiple lines.
 *)


import "/dynamic" for Enum, Struct, Tuple
import "/fmt" for Fmt
import "/ioutil" for FileUtil

var tokens = [
    "EOI",
    "Mul",
    "Div",
    "Mod",
    "Add",
    "Sub",
    "Negate",
    "Not",
    "Lss",
    "Leq",
    "Gtr",
    "Geq",
    "Eql",
    "Neq",
    "Assign",
    "And",
    "Or",
    "If",
    "Else",
    "While",
    "Print",
    "Putc",
    "Lparen",
    "Rparen",
    "Lbrace",
    "Rbrace",
    "Semi",
    "Comma",
    "Ident",
    "Integer",
    "String"
]

var Token = Enum.create("Token", tokens)

var nodes = [
    "Ident",
    "String",
    "Integer",
    "Sequence",
    "If",
    "Prtc",
    "Prts",
    "Prti",
    "While",
    "Assign",
    "Negate",
    "Not",
    "Mul",
    "Div",
    "Mod",
    "Add",
    "Sub",
    "Lss",
    "Leq",
    "Gtr",
    "Geq",
    "Eql",
    "Neq",
    "And",
    "Or"
]

var Node = Enum.create("Node", nodes)

// 'text' field represents ident ot string literal or integer value
var TokS = Struct.create("TokS", ["tok", "errLn", "errCol", "text"])

var Tree = Struct.create("Tree", ["nodeType", "left", "right", "value"])

// dependency: Ordered by tok, must remain in same order as Token enum constants
var Atr = Tuple.create("Atr", ["text", "enumText", "tok", "rightAssociative", "isBinary",
                               "isUnary", "precedence", "nodeType"])

var atrs = [
    Atr.new("EOI", "End_of_input", Token.EOI, false, false, false, -1, -1),
    Atr.new("*", "Op_multiply", Token.Mul, false, true, false, 13, Node.Mul),
    Atr.new("/", "Op_divide", Token.Div, false, true, false, 13, Node.Div),
    Atr.new("\%", "Op_mod", Token.Mod, false, true, false, 13, Node.Mod),
    Atr.new("+", "Op_add", Token.Add, false, true, false, 12, Node.Add),
    Atr.new("-", "Op_subtract", Token.Sub, false, true, false, 12, Node.Sub),
    Atr.new("-", "Op_negate", Token.Negate, false, false, true, 14, Node.Negate),
    Atr.new("!", "Op_not", Token.Not, false, false, true, 14, Node.Not),
    Atr.new("<", "Op_less", Token.Lss, false, true, false, 10, Node.Lss),
    Atr.new("<=", "Op_lessequal", Token.Leq, false, true, false, 10, Node.Leq),
    Atr.new(">", "Op_greater", Token.Gtr, false, true, false, 10, Node.Gtr),
    Atr.new(">=", "Op_greaterequal", Token.Geq, false, true, false, 10, Node.Geq),
    Atr.new("==", "Op_equal", Token.Eql, false, true, false, 9, Node.Eql),
    Atr.new("!=", "Op_notequal", Token.Neq, false, true, false, 9, Node.Neq),
    Atr.new("=", "Op_assign", Token.Assign, false, false, false, -1, Node.Assign),
    Atr.new("&&", "Op_and", Token.And, false, true, false, 5, Node.And),
    Atr.new("||", "Op_or", Token.Or, false, true, false, 4, Node.Or),
    Atr.new("if", "Keyword_if", Token.If, false, false, false, -1, Node.If),
    Atr.new("else", "Keyword_else", Token.Else, false, false, false, -1, -1),
    Atr.new("while", "Keyword_while", Token.While, false, false, false, -1, Node.While),
    Atr.new("print", "Keyword_print", Token.Print, false, false, false, -1, -1),
    Atr.new("putc", "Keyword_putc", Token.Putc, false, false, false, -1, -1),
    Atr.new("(", "LeftParen", Token.Lparen, false, false, false, -1, -1),
    Atr.new(")", "RightParen", Token.Rparen, false, false, false, -1, -1),
    Atr.new("{", "LeftBrace", Token.Lbrace, false, false, false, -1, -1),
    Atr.new("}", "RightBrace", Token.Rbrace, false, false, false, -1, -1),
    Atr.new(";", "Semicolon", Token.Semi, false, false, false, -1, -1),
    Atr.new(",", "Comma", Token.Comma, false, false, false, -1, -1),
    Atr.new("Ident", "Identifier", Token.Ident, false, false, false, -1, Node.Ident),
    Atr.new("Integer literal", "Integer", Token.Integer, false, false, false, -1, Node.Integer),
    Atr.new("String literal", "String", Token.String, false, false, false, -1, Node.String),
]

var displayNodes = [
    "Identifier", "String", "Integer", "Sequence", "If", "Prtc", "Prts", "Prti",
    "While", "Assign", "Negate", "Not", "Multiply", "Divide", "Mod", "Add",
    "Subtract", "Less", "LessEqual", "Greater", "GreaterEqual", "Equal",
    "NotEqual", "And", "Or"
]

var token = TokS.new(0, 0, 0, "")

var reportError = Fn.new { |eline, ecol, msg| Fiber.abort("(%(eline):%(ecol)) error : %(msg)") }

// return internal version of name
var getEnum = Fn.new { |name|
    for (atr in atrs) {
        if (atr.enumText == name) return atr.tok
    }
    reportError.call(0, 0, "Unknown token %(name)")
}

var lines = []
var lineCount = 0
var lineNum = 0

var getTok = Fn.new {
    var tok = TokS.new(0, 0, 0, "")
    if (lineNum < lineCount) {
        var line = lines[lineNum].trimEnd(" \t")
        lineNum = lineNum + 1
        var fields = line.split(" ").where { |s| s != "" }.toList
        // [ ]*{lineno}[ ]+{colno}[ ]+token[ ]+optional
        tok.errLn = Num.fromString(fields[0])
        tok.errCol = Num.fromString(fields[1])
        tok.tok = getEnum.call(fields[2])
        var le = fields.count
        if (le == 4) {
            tok.text = fields[3]
        } else if (le > 4) {
            var idx = line.indexOf("\"")
            tok.text = line[idx..-1]
        }
    }
    return tok
}

var makeNode = Fn.new { |nodeType, left, right| Tree.new(nodeType, left, right, "") }

var makeLeaf = Fn.new { |nodeType, value| Tree.new(nodeType, null, null, value) }

var expect = Fn.new { |msg, s|
    if (token.tok == s) {
        token = getTok.call()
        return
    }
    reportError.call(token.errLn, token.errCol,
        Fmt.swrite("$s: Expecting '$s', found '$s'", msg, atrs[s].text, atrs[token.tok].text))
}

var parenExpr  // forward reference

var expr // recursive function
expr = Fn.new { |p|
    var x
    var node
    var t = token.tok
    if (t == Token.Lparen) {
        x = parenExpr.call()
    } else if (t == Token.Sub || t == Token.Add) {
        var op = t
        token = getTok.call()
        node = expr.call(atrs[Token.Negate].precedence)
        if (op == Token.Sub) {
            x = makeNode.call(Node.negate, node, null)
        } else {
            x = node
        }
    } else if (t == Token.Not) {
        token = getTok.call()
        x = makeNode.call(Node.Not, expr.call(atrs[Token.Not].precedence), null)
    } else if (t == Token.Ident) {
        x = makeLeaf.call(Node.Ident, token.text)
        token = getTok.call()
    } else if (t == Token.Integer) {
        x = makeLeaf.call(Node.Integer, token.text)
        token = getTok.call()
    } else {
        reportError.call(token.errLn, token.errCol,
            Fmt.swrite("Expecting a primary, found: $s", atrs[token.tok].text))
    }

    while (atrs[token.tok].isBinary && atrs[token.tok].precedence >= p) {
        var op = token.tok
        token = getTok.call()
        var q = atrs[op].precedence
        if (!atrs[op].rightAssociative) q = q + 1
        node = expr.call(q)
        x = makeNode.call(atrs[op].nodeType, x, node)
    }
    return x
}

parenExpr = Fn.new {
    expect.call("parenExpr", Token.Lparen)
    var t = expr.call(0)
    expect.call("parenExpr", Token.Rparen)
    return t
}

var stmt // recursive function
stmt = Fn.new {
    var t
    var v
    var e
    var s
    var s2
    var tt = token.tok
    if (tt == Token.If) {
        token = getTok.call()
        e = parenExpr.call()
        s = stmt.call()
        s2 = null
        if (token.tok == Token.Else) {
            token = getTok.call()
            s2 = stmt.call()
        }
        t = makeNode.call(Node.If, e, makeNode.call(Node.If, s, s2))
    } else if (tt == Token.Putc) {
        token = getTok.call()
        e = parenExpr.call()
        t = makeNode.call(Node.Prtc, e, null)
        expect.call("Putc", Token.Semi)
    } else if (tt == Token.Print) { // print '(' expr {',' expr} ')'
        token = getTok.call()
        expect.call("Print", Token.Lparen)
        while (true) {
            if (token.tok == Token.String) {
                e = makeNode.call(Node.Prts, makeLeaf.call(Node.String, token.text), null)
                token = getTok.call()
            } else {
                e = makeNode.call(Node.Prti, expr.call(0), null)
            }
            t = makeNode.call(Node.Sequence, t, e)
            if (token.tok != Token.Comma) break
            expect.call("Print", Token.Comma)
        }
        expect.call("Print", Token.Rparen)
        expect.call("Print", Token.Semi)
    } else if (tt == Token.Semi) {
        token = getTok.call()
    } else if (tt == Token.Ident) {
        v = makeLeaf.call(Node.Ident, token.text)
        token = getTok.call()
        expect.call("assign", Token.Assign)
        e = expr.call(0)
        t = makeNode.call(Node.Assign, v, e)
        expect.call("assign", Token.Semi)
    } else if (tt == Token.While) {
        token = getTok.call()
        e = parenExpr.call()
        s = stmt.call()
        t = makeNode.call(Node.While, e, s)
    } else if (tt == Token.Lbrace) { // {stmt}
        expect.call("Lbrace", Token.Lbrace)
        while (token.tok != Token.Rbrace && token.tok != Token.EOI) {
            t = makeNode.call(Node.Sequence, t, stmt.call())
        }
        expect.call("Lbrace", Token.Rbrace)
    } else if (tt == Token.EOI) {
        // do nothing
    } else {
        reportError.call(token.errLn, token.errCol,
            Fmt.Swrite("expecting start of statement, found '$s'", atrs[token.tok].text))
    }
    return t
}

var parse = Fn.new {
    var t
    token = getTok.call()
    while (true) {
        t = makeNode.call(Node.Sequence, t, stmt.call())
        if (!t || token.tok == Token.EOI) break
    }
    return t
}

var prtAst  // recursive function
prtAst = Fn.new { |t|
    if (!t) {
        System.print(";")
    } else {
        Fmt.write("$-14s ", displayNodes[t.nodeType])
        if (t.nodeType == Node.Ident || t.nodeType == Node.Integer || t.nodeType == Node.String) {
            System.print(t.value)
        } else {
            System.print()
            prtAst.call(t.left)
            prtAst.call(t.right)
        }
    }
}

lines = FileUtil.readLines("source.txt")
lineCount = lines.count
prtAst.call(parse.call())


put the commandline
-- "-c alpha beta -h gamma"


argc , nl  # number of arguments (including command itself)
0         # argument
dup arg dup 0 = || ,t  1 + repeat
drop


print( "Program name:", arg[0] )

print "Arguments:"
for i = 1, #arg do
    print( i," ", arg[i] )
end


# currently, Crystal only supports single-line comments

# This is a doc comment. Any line *directly* above (no blank lines) a module, class, or method is considered a doc comment
# Doc comments are used to generate documentation with `crystal docs`
class Foo
end


void main() {
    // A single line comment.

    /* This is a simple C-style comment that can't be nested.
    Comments mostly work similar to C, newlines are irrelevant.
    */

    /+ This is a nestable comment
      /+ See?
      +/
    +/

    /// Documentation single line comment.

    /**
    Simple C-style documentation comment.
    */

    /++
    Nestable documenttion comment.
    +/
}


// This is a single line comment, which lasts until the end of the line. The Dart linter prefers this one.

/* This is also a valid single line comment. Unlike the first one, this one terminates after one of these -> */

/*
  You can use the syntax above to make multi line comments as well.
  Like this!
*/

/// These are doc comments. You can use dartdoc to generate doc pages for your classes with these.
///
/// Formatting [variable] and [function] names like so allows dartdoc to link to the documentation for those entities.


[Making and discarding a string acts like a comment] sz


const std = @import("std");

pub const NodeValue = union(enum) {
    integer: i32,
    string: []const u8,

    fn fromToken(token: Token) ?NodeValue {
        if (token.value) |value| {
            switch (value) {
                .integer => |int| return NodeValue{ .integer = int },
                .string => |str| return NodeValue{ .string = str },
            }
        } else {
            return null;
        }
    }
};

pub const Tree = struct {
    left: ?*Tree,
    right: ?*Tree,
    typ: NodeType,
    value: ?NodeValue = null,
};

pub const ParserError = error{
    OutOfMemory,
    ExpectedNotFound,
} || std.fmt.ParseIntError;

pub const Parser = struct {
    token_it: LexerOutputTokenizer,
    curr: Token,
    allocator: std.mem.Allocator,

    const Self = @This();

    pub fn init(allocator: std.mem.Allocator, str: []const u8) Self {
        return Self{
            .token_it = LexerOutputTokenizer.init(str),
            .curr = Token{ .line = 0, .col = 0, .typ = .unknown },
            .allocator = allocator,
        };
    }

    fn makeNode(self: *Self, typ: NodeType, left: ?*Tree, right: ?*Tree) !*Tree {
        const result = try self.allocator.create(Tree);
        result.* = Tree{ .left = left, .right = right, .typ = typ };
        return result;
    }

    fn makeLeaf(self: *Self, typ: NodeType, value: ?NodeValue) !*Tree {
        const result = try self.allocator.create(Tree);
        result.* = Tree{ .left = null, .right = null, .typ = typ, .value = value };
        return result;
    }

    pub fn parse(self: *Self) ParserError!?*Tree {
        try self.next();
        var result: ?*Tree = null;
        while (true) {
            const stmt = try self.parseStmt();
            result = try self.makeNode(.sequence, result, stmt);
            if (self.curr.typ == .eof) break;
        }
        return result;
    }

    /// Classic "Recursive descent" statement parser.
    fn parseStmt(self: *Self) ParserError!?*Tree {
        var result: ?*Tree = null;
        switch (self.curr.typ) {
            .kw_print => {
                try self.next();
                try self.expect(.left_paren);
                // Parse each print's argument as an expression delimited by commas until we reach
                // a closing parens.
                while (true) {
                    var expr: ?*Tree = null;
                    if (self.curr.typ == .string) {
                        expr = try self.makeNode(
                            .prts,
                            try self.makeLeaf(.string, NodeValue.fromToken(self.curr)),
                            null,
                        );
                        try self.next();
                    } else {
                        expr = try self.makeNode(.prti, try self.parseExpr(0), null);
                    }
                    result = try self.makeNode(.sequence, result, expr);
                    if (self.curr.typ != .comma) break;
                    try self.next();
                }
                try self.expect(.right_paren);
                try self.expect(.semicolon);
            },
            .kw_putc => {
                try self.next();
                result = try self.makeNode(.prtc, try self.parseParenExpr(), null);
                try self.expect(.semicolon);
            },
            .kw_while => {
                try self.next();
                const expr = try self.parseParenExpr();
                result = try self.makeNode(.kw_while, expr, try self.parseStmt());
            },
            .kw_if => {
                try self.next();
                const expr = try self.parseParenExpr();
                const if_stmt = try self.parseStmt();
                const else_stmt = blk: {
                    if (self.curr.typ == .kw_else) {
                        try self.next();
                        break :blk try self.parseStmt();
                    } else {
                        break :blk null;
                    }
                };
                const stmt_node = try self.makeNode(.kw_if, if_stmt, else_stmt);
                // If-statement uses `.kw_if` node for both first node with `expr` on the left
                // and statements on the right and also `.kw_if` node which goes to the right
                // and contains both if-branch and else-branch.
                result = try self.makeNode(.kw_if, expr, stmt_node);
            },
            .left_brace => {
                try self.next();
                while (self.curr.typ != .right_brace and self.curr.typ != .eof) {
                    result = try self.makeNode(.sequence, result, try self.parseStmt());
                }
                try self.expect(.right_brace);
            },
            .identifier => {
                const identifer = try self.makeLeaf(.identifier, NodeValue.fromToken(self.curr));
                try self.next();
                try self.expect(.assign);
                const expr = try self.parseExpr(0);
                result = try self.makeNode(.assign, identifer, expr);
                try self.expect(.semicolon);
            },
            .semicolon => try self.next(),
            else => {
                std.debug.print("\nSTMT: UNKNOWN {}\n", .{self.curr});
                std.os.exit(1);
            },
        }
        return result;
    }

    /// "Precedence climbing" expression parser.
    fn parseExpr(self: *Self, precedence: i8) ParserError!?*Tree {
        var result: ?*Tree = null;
        switch (self.curr.typ) {
            .left_paren => {
                result = try self.parseParenExpr();
            },
            .subtract => {
                try self.next();
                const metadata = NodeMetadata.find(.negate);
                const expr = try self.parseExpr(metadata.precedence);
                result = try self.makeNode(.negate, expr, null);
            },
            .not => {
                try self.next();
                const metadata = NodeMetadata.find(.not);
                const expr = try self.parseExpr(metadata.precedence);
                result = try self.makeNode(.not, expr, null);
            },
            .add => {
                try self.next();
                result = try self.parseExpr(precedence);
            },
            .integer, .identifier => {
                const node_type = NodeMetadata.find(self.curr.typ).node_type;
                result = try self.makeLeaf(node_type, NodeValue.fromToken(self.curr));
                try self.next();
            },
            else => {
                std.debug.print("\nEXPR: UNKNOWN {}\n", .{self.curr});
                std.os.exit(1);
            },
        }

        var curr_metadata = NodeMetadata.find(self.curr.typ);
        while (curr_metadata.binary and curr_metadata.precedence >= precedence) {
            const new_precedence =
                if (curr_metadata.right_associative)
                curr_metadata.precedence
            else
                curr_metadata.precedence + 1;
            try self.next();
            const sub_expr = try self.parseExpr(new_precedence);
            result = try self.makeNode(curr_metadata.node_type, result, sub_expr);
            curr_metadata = NodeMetadata.find(self.curr.typ);
        }
        return result;
    }

    fn parseParenExpr(self: *Self) ParserError!?*Tree {
        try self.expect(.left_paren);
        const result = try self.parseExpr(0);
        try self.expect(.right_paren);
        return result;
    }

    fn next(self: *Self) ParserError!void {
        const token = try self.token_it.next();
        if (token) |tok| {
            self.curr = tok;
        } else {
            self.curr = Token{ .line = 0, .col = 0, .typ = .unknown };
        }
    }

    fn expect(self: *Self, token_type: TokenType) ParserError!void {
        if (self.curr.typ != token_type) {
            const expected_str = NodeMetadata.find(token_type).token_str;
            const found_str = NodeMetadata.find(self.curr.typ).token_str;
            std.debug.print(
                "({d}, {d}) error: Expecting '{s}', found '{s}'\n",
                .{ self.curr.line, self.curr.col, expected_str, found_str },
            );
            return ParserError.ExpectedNotFound;
        }
        try self.next();
    }
};

pub fn parse(allocator: std.mem.Allocator, str: []const u8) !?*Tree {
    var parser = Parser.init(allocator, str);
    return try parser.parse();
}

pub fn main() !void {
    var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
    defer arena.deinit();
    const allocator = arena.allocator();

    var arg_it = std.process.args();
    _ = try arg_it.next(allocator) orelse unreachable; // program name
    const file_name = arg_it.next(allocator);
    // We accept both files and standard input.
    var file_handle = blk: {
        if (file_name) |file_name_delimited| {
            const fname: []const u8 = try file_name_delimited;
            break :blk try std.fs.cwd().openFile(fname, .{});
        } else {
            break :blk std.io.getStdIn();
        }
    };
    defer file_handle.close();
    const input_content = try file_handle.readToEndAlloc(allocator, std.math.maxInt(usize));

    const result: ?*Tree = try parse(allocator, input_content);
    const result_str = try astToFlattenedString(allocator, result);
    _ = try std.io.getStdOut().write(result_str);
}

const NodeMetadata = struct {
    token_type: TokenType,
    right_associative: bool,
    binary: bool,
    unary: bool,
    precedence: i8,
    node_type: NodeType,
    token_str: []const u8,

    const self = [_]NodeMetadata{
        .{ .token_type = .multiply, .right_associative = false, .binary = true, .unary = false, .precedence = 13, .node_type = .multiply, .token_str = "*" },
        .{ .token_type = .divide, .right_associative = false, .binary = true, .unary = false, .precedence = 13, .node_type = .divide, .token_str = "/" },
        .{ .token_type = .mod, .right_associative = false, .binary = true, .unary = false, .precedence = 13, .node_type = .mod, .token_str = "%" },
        .{ .token_type = .add, .right_associative = false, .binary = true, .unary = false, .precedence = 12, .node_type = .add, .token_str = "+" },
        .{ .token_type = .subtract, .right_associative = false, .binary = true, .unary = false, .precedence = 12, .node_type = .subtract, .token_str = "-" },
        .{ .token_type = .negate, .right_associative = false, .binary = false, .unary = true, .precedence = 14, .node_type = .negate, .token_str = "-" },
        .{ .token_type = .less, .right_associative = false, .binary = true, .unary = false, .precedence = 10, .node_type = .less, .token_str = "<" },
        .{ .token_type = .less_equal, .right_associative = false, .binary = true, .unary = false, .precedence = 10, .node_type = .less_equal, .token_str = "<=" },
        .{ .token_type = .greater, .right_associative = false, .binary = true, .unary = false, .precedence = 10, .node_type = .greater, .token_str = ">" },
        .{ .token_type = .greater_equal, .right_associative = false, .binary = true, .unary = false, .precedence = 10, .node_type = .greater_equal, .token_str = ">=" },
        .{ .token_type = .equal, .right_associative = false, .binary = true, .unary = false, .precedence = 9, .node_type = .equal, .token_str = "=" },
        .{ .token_type = .not_equal, .right_associative = false, .binary = true, .unary = false, .precedence = 9, .node_type = .not_equal, .token_str = "!=" },
        .{ .token_type = .not, .right_associative = false, .binary = false, .unary = true, .precedence = 14, .node_type = .not, .token_str = "!" },
        .{ .token_type = .assign, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .assign, .token_str = "=" },
        .{ .token_type = .bool_and, .right_associative = false, .binary = true, .unary = false, .precedence = 5, .node_type = .bool_and, .token_str = "&&" },
        .{ .token_type = .bool_or, .right_associative = false, .binary = true, .unary = false, .precedence = 4, .node_type = .bool_or, .token_str = "||" },
        .{ .token_type = .left_paren, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "(" },
        .{ .token_type = .right_paren, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = ")" },
        .{ .token_type = .left_brace, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "{" },
        .{ .token_type = .right_brace, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "}" },
        .{ .token_type = .semicolon, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = ";" },
        .{ .token_type = .comma, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "," },
        .{ .token_type = .kw_if, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .kw_if, .token_str = "if" },
        .{ .token_type = .kw_else, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "else" },
        .{ .token_type = .kw_while, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .kw_while, .token_str = "while" },
        .{ .token_type = .kw_print, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "print" },
        .{ .token_type = .kw_putc, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "putc" },
        .{ .token_type = .identifier, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .identifier, .token_str = "Identifier" },
        .{ .token_type = .integer, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .integer, .token_str = "Integer literal" },
        .{ .token_type = .string, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .string, .token_str = "String literal" },
        .{ .token_type = .eof, .right_associative = false, .binary = false, .unary = false, .precedence = -1, .node_type = .unknown, .token_str = "End of line" },
    };

    pub fn find(token_type: TokenType) NodeMetadata {
        for (self) |metadata| {
            if (metadata.token_type == token_type) return metadata;
        } else {
            unreachable;
        }
    }
};

pub const NodeType = enum {
    unknown,
    identifier,
    string,
    integer,
    sequence,
    kw_if,
    prtc,
    prts,
    prti,
    kw_while,
    assign,
    negate,
    not,
    multiply,
    divide,
    mod,
    add,
    subtract,
    less,
    less_equal,
    greater,
    greater_equal,
    equal,
    not_equal,
    bool_and,
    bool_or,

    pub fn toString(self: NodeType) []const u8 {
        return switch (self) {
            .unknown => "UNKNOWN",
            .identifier => "Identifier",
            .string => "String",
            .integer => "Integer",
            .sequence => "Sequence",
            .kw_if => "If",
            .prtc => "Prtc",
            .prts => "Prts",
            .prti => "Prti",
            .kw_while => "While",
            .assign => "Assign",
            .negate => "Negate",
            .not => "Not",
            .multiply => "Multiply",
            .divide => "Divide",
            .mod => "Mod",
            .add => "Add",
            .subtract => "Subtract",
            .less => "Less",
            .less_equal => "LessEqual",
            .greater => "Greater",
            .greater_equal => "GreaterEqual",
            .equal => "Equal",
            .not_equal => "NotEqual",
            .bool_and => "And",
            .bool_or => "Or",
        };
    }
};

fn astToFlattenedString(allocator: std.mem.Allocator, tree: ?*Tree) ![]const u8 {
    var result = std.ArrayList(u8).init(allocator);
    var writer = result.writer();
    try treeToString(allocator, writer, tree);
    return result.items;
}

pub const TokenType = enum {
    unknown,
    multiply,
    divide,
    mod,
    add,
    subtract,
    negate,
    less,
    less_equal,
    greater,
    greater_equal,
    equal,
    not_equal,
    not,
    assign,
    bool_and,
    bool_or,
    left_paren,
    right_paren,
    left_brace,
    right_brace,
    semicolon,
    comma,
    kw_if,
    kw_else,
    kw_while,
    kw_print,
    kw_putc,
    identifier,
    integer,
    string,
    eof,

    const from_string_map = std.ComptimeStringMap(TokenType, .{
        .{ "Op_multiply", .multiply },
        .{ "Op_divide", .divide },
        .{ "Op_mod", .mod },
        .{ "Op_add", .add },
        .{ "Op_subtract", .subtract },
        .{ "Op_negate", .negate },
        .{ "Op_less", .less },
        .{ "Op_lessequal", .less_equal },
        .{ "Op_greater", .greater },
        .{ "Op_greaterequal", .greater_equal },
        .{ "Op_equal", .equal },
        .{ "Op_notequal", .not_equal },
        .{ "Op_not", .not },
        .{ "Op_assign", .assign },
        .{ "Op_and", .bool_and },
        .{ "Op_or", .bool_or },
        .{ "LeftParen", .left_paren },
        .{ "RightParen", .right_paren },
        .{ "LeftBrace", .left_brace },
        .{ "RightBrace", .right_brace },
        .{ "Semicolon", .semicolon },
        .{ "Comma", .comma },
        .{ "Keyword_if", .kw_if },
        .{ "Keyword_else", .kw_else },
        .{ "Keyword_while", .kw_while },
        .{ "Keyword_print", .kw_print },
        .{ "Keyword_putc", .kw_putc },
        .{ "Identifier", .identifier },
        .{ "Integer", .integer },
        .{ "String", .string },
        .{ "End_of_input", .eof },
    });

    pub fn fromString(str: []const u8) TokenType {
        return from_string_map.get(str).?;
    }
};

pub const TokenValue = union(enum) {
    integer: i32,
    string: []const u8,
};

pub const Token = struct {
    line: usize,
    col: usize,
    typ: TokenType = .unknown,
    value: ?TokenValue = null,
};

const TreeToStringError = error{OutOfMemory};

fn treeToString(
    allocator: std.mem.Allocator,
    writer: std.ArrayList(u8).Writer,
    tree: ?*Tree,
) TreeToStringError!void {
    if (tree) |t| {
        _ = try writer.write(try std.fmt.allocPrint(
            allocator,
            "{s}",
            .{t.typ.toString()},
        ));
        switch (t.typ) {
            .string, .identifier => _ = try writer.write(try std.fmt.allocPrint(
                allocator,
                "   {s}\n",
                .{t.value.?.string},
            )),
            .integer => _ = try writer.write(try std.fmt.allocPrint(
                allocator,
                "   {d}\n",
                .{t.value.?.integer},
            )),
            else => {
                _ = try writer.write(try std.fmt.allocPrint(
                    allocator,
                    "\n",
                    .{},
                ));
                try treeToString(allocator, writer, t.left);
                try treeToString(allocator, writer, t.right);
            },
        }
    } else {
        _ = try writer.write(try std.fmt.allocPrint(
            allocator,
            ";\n",
            .{},
        ));
    }
}

pub const LexerOutputTokenizer = struct {
    it: std.mem.SplitIterator(u8),

    const Self = @This();

    pub fn init(str: []const u8) Self {
        return Self{ .it = std.mem.split(u8, str, "\n") };
    }

    pub fn next(self: *Self) std.fmt.ParseIntError!?Token {
        if (self.it.next()) |line| {
            if (line.len == 0) return null;
            var tokens_it = std.mem.tokenize(u8, line, " ");
            const lineNumber = try std.fmt.parseInt(usize, tokens_it.next().?, 10);
            const colNumber = try std.fmt.parseInt(usize, tokens_it.next().?, 10);
            const typ_text = tokens_it.next().?;
            const typ = TokenType.fromString(typ_text);
            const pre_value_index = tokens_it.index;
            const value = tokens_it.next();
            var token = Token{ .line = lineNumber, .col = colNumber, .typ = typ };
            if (value) |val| {
                const token_value = blk: {
                    switch (typ) {
                        .string, .identifier => {
                            tokens_it.index = pre_value_index;
                            break :blk TokenValue{ .string = tokens_it.rest() };
                        },
                        .integer => break :blk TokenValue{ .integer = try std.fmt.parseInt(i32, val, 10) },
                        else => unreachable,
                    }
                };
                token.value = token_value;
            }
            return token;
        } else {
            return null;
        }
    }
};

fn stringToTokenList(allocator: std.mem.Allocator, str: []const u8) !std.ArrayList(Token) {
    var result = std.ArrayList(Token).init(allocator);
    var lexer_output_it = LexerOutputTokenizer.init(str);
    while (try lexer_output_it.next()) |token| {
        try result.append(token);
    }
    return result;
}


Module Checkit {
      Document a$ = {
      Module Global A {
            Show
            Read a$="nothing", x=0
            Print a$, x
            A$=Key$
      }
      A: End
      }
      Dir temporary$
      Save.doc a$, "program.gsb"
      \\ open if gsb extension is register to m2000.exe
      Win quote$(dir$+"program.gsb")
      \\ +txt is a switch for interpreter to use string comparison as text (not binary)
      \\ so we can send switches and commands before the program loading
      Win appdir$+"m2000.exe", quote$(dir$+"program.gsb +txt : Data {Hello}, 100")
      \\ no coma after name (we can use "program.gsb" for  names with spaces)
      Use program.gsb  "From Use statement", 200
      \\ delete file
      Wait 5000
      Dos "del "+quote$(dir$+"program.gsb");
      \\ open directory
      Rem : Win temporary$
}
Checkit


:- module cmd_line_args.
:- interface.

:- import_module io.
:- pred main(io::di, io::uo) is det.

:- implementation.
:- import_module int, list, string.

main(!IO) :-
    io.progname("", ProgName, !IO),
    io.format("This program is named %s.\n", [s(ProgName)], !IO),
    io.command_line_arguments(Args, !IO),
    list.foldl2(print_arg, Args, 1, _, !IO).

:- pred print_arg(string::in, int::in, int::out, io::di, io::uo) is det.

print_arg(Arg, ArgNum, ArgNum + 1, !IO) :-
    io.format("the argument #%d is %s\n", [i(ArgNum), s(Arg)], !IO).