
# a translator for the IttyBitty language:
#    given the parse tree and symbol table,
#    translate into C code
#
# Assumed parse tree structure:
#
#    StatementList returns a non-empty array of statements, which is
#       in Program and Block, and referred to below as a StatementsArray
#    Integer values are stored as their text representation
#
# Program --> ['Program',StatementsArray]
#
# Statement --> ['Assign', varname, opname, Expression ]
#           --> ['Print', Value ]
#           --> ['Print', ['Text', ArrayOfWords] ]
#           --> ['If', ['Cond', Value, opname, Value ],
#                      ['Block', StatementsArray]]
#           --> ['While', ['Cond', Value, opname, Value ],
#                         ['Block', StatementsArray]]
#
# Value --> ['Variable', varname]
#       --> ['Integer', value]
#
# Expression --> Value
#            --> [ 'Expression', Value opname Expression ]

# indent is just for making pretty, showns current level of indentation in output code
def tabs(n)
    for i in 1..n do
        print "   "
    end
end

def translate(parsetree, symtable, numtabs=0)
    # translation is carried out through a recursive exploration of
    #    the provided parse (sub)tree

    # bail out if the tree is empty
    if parsetree.length < 1 then
       return
    end

    # the first element of the parse tree should identify the component type,
    #     the set of translation actions will depend on that type
    case parsetree[0]
       when "Program"
          # ['Program',StatementsArray]
          # 1) write the preamble for the program, i.e.
          #      the #include and the start of int main
          # 2) get the list of variables, if any, from the symbol table
          #      and declare them inside main
          # 3) parsetree[1] should be the array of statements,
          #      translate them in order
          # 4) write the closing return and }

          print "#include <stdio.h>\n\n"
          print "int main()\n{\n"

          numtabs += 1
          if symtable.length > 0 then
             tabs(numtabs)
             print "int"
             first = true
             symtable.each do | varname,status |
                if first then
                   first = false
                else
                   print ","
                end
                print " #{varname}"
             end
             print ";\n"
          end

          statements = parsetree[1]
          statements.each do | statement |
             translate(statement, symtable, numtabs)
          end

          tabs(numtabs)
          print "return 0;\n}\n"

       when "Variable"
          # ['Variable', varname]
          # write the varname
          print "#{parsetree[1]}"

       when "Integer"
          # ['Integer', value]
          # write the value
          print "#{parsetree[1]}"

       when "Assign"
          # ['Assign', varname, opname, Expression ]
          # 1) write the varname = opname
          # 2) translate the expression
          # 3) write the ; and newline
          tabs(numtabs)
          print "#{parsetree[1]} #{parsetree[2]} "
          translate(parsetree[3],symtable, numtabs)
          print ";\n"

       when "Print"
          # two possible print cases:
          #    ['Print', Value ]
          #    ['Print', ['Text', ArrayOfWords] ]
          # 1) write the printf("
          # for the ['Print', Value ] case
          #    2) write the %d\n", and the varname or value from the Value array
          # for the ['Print', ['Text', ArrayOfWords] ] case
          #    2) write each word of the array followed by a space,
          #       then write the closing \n"
          # 3) write the closing ); and a newline

          tabs(numtabs)
          print "printf(\"";
          if parsetree[1][0] == 'Text' then
             words = parsetree[1][1]
             words.each do | word |
                print "#{word} "
             end
             print "\\n\""
          else
             print "%d\\n\", "
             print "#{parsetree[1][1]}"
          end
          print ");\n"

       when "While"
          # ['While', ['Cond', Value, opname, Value ], ['Block', StatementsArray]]
          # 1) write the while keyword
          # 2) translate the cond
          # 3) translate the block
          tabs(numtabs)
          print "while "
          translate(parsetree[1], symtable, numtabs)
          print "\n"
          translate(parsetree[2], symtable, numtabs)

       when "If"
          # ['If', ['Cond', Value, opname, Value ], ['Block', StatementsArray]]
          # 1) write the if keyword
          # 2) translate the cond
          # 3) translate the block
          tabs(numtabs)
          print "if "
          translate(parsetree[1], symtable, numtabs)
          print "\n"
          translate(parsetree[2], symtable, numtabs)

       when "Cond"
          # ['Cond', Value, opname, Value ]
          # 1) write the opening (
          # 2) write the varname or value from the first Value
          # 3) write the opname
          # 4) write the varname or value from the second Value
          # 5) write the closing )
          print "(#{parsetree[1][1]} #{parsetree[2]} #{parsetree[3][1]})"

       when "Block"
          # ['Block', StatementsArray]
          # 1) write the opening { and a newline
          # 2) translate each statement in the StatementsArray, in order
          # 3) write the closing } and a newline
          tabs(numtabs)
          print "{\n"
          statements = parsetree[1]
          statements.each do | statement |
             translate(statement, symtable, numtabs+1)
          end
          tabs(numtabs)
          print "}\n"

       when "Expression"
          # [ 'Expression', Value opname Expression ]
          # 1) write the varname or value from the Value array
          # 2) write the opname
          # 3) translate the Expression
          print "#{parsetree[1][1]} #{parsetree[2]} "
          translate(parsetree[3], symtable, numtabs)

       else
          puts "Error: unknown program component encountered in (sub)tree:"
          puts "   #{parsetree}"
    end
end

