#!/bin/sh

awk -F: '
  function parse(op,x,  xlen,i,c,word) {
    xlen = 0
    word = ""
    for (i = 1;i <= length(op);++i) {
      c = substr(op,i,1)
      if (match(c,/^[a-zA-Z0-9_]*$/)) {
        word = word c
        continue
      }
      if (length(word) > 0) {
        ++xlen
        x[xlen] = word
        word = ""
      }
      if (c == " ") continue
      if (c == "\t") continue
      if (c == "\r") continue
      ++xlen
      x[xlen] = c
    }
    if (length(word) > 0) {
      ++xlen
      x[xlen] = word
      word = ""
    }
    return xlen
  }
  function dosyntaxline() {
    mlen = split($0,m)
    if (mlen == 0) return

    if (m[1] == "") {
      if (m[2] == "") {
        readingfromuser = 1
      } else if (m[2] == "name") {
        type = m[3]
        for (j = 4;j <= mlen;++j) {
          if (m[j]) {
            ++regnum[type]
            reg[type"#"regnum[type]"+0"] = m[j]
            reg[type"#"regnum[type]"+4"] = m[j]
          }
        }
      } else if (m[2] == "caller") {
        type = m[3]
        for (j = 4;j <= mlen;++j) {
          if (m[j]) {
            ++callernum[type]
            caller[type"#"callernum[type]] = m[j]
            }
        }
      } else if (m[2] == "stackbytes") {
        stackbytes[m[3]] = m[4]
      } else if (m[2] == "leftbytes") {
        leftbytes = m[3]
      } else if (m[2] == "rightbytes") {
        rightbytes = m[3]
      } else if (m[2] == "stackalign") {
        stackalign = m[3]
      } else if (m[2] == "stackname") {
        stacknameleft = m[3]
        stacknameright = m[4]
      } else if (m[2] == "stack256name") {
        stack256name = 1
        stack256nameleft = m[3]
        stack256nameright = m[4]
      } else if (m[2] == "stack512name") {
        stack512name = 1
        stack512nameleft = m[3]
        stack512nameright = m[4]
      } else if (m[2] == "stackargname") {
        stackargnameleft = m[3]
        stackargnameright = m[4]
      }
      return
    }

    key = "op"
    for (j = 2;j <= mlen;++j)
      if (match(m[j],/^modify\//))
        key = key","m[j]
        # XXX sort modifications here and below

    xlen = parse(m[1],x)
    for (j = 1;j <= xlen;++j)
      if (match(x[j],/[A-Za-z0-9_]/))
        key = key" x"
      else
        key = key" "x[j]
    opnum[key] = opnum[key] + 1
    op[key,opnum[key]] = $0
  }
  function douserline(line) {
    flen = split(line,f)

    if (f[1] == "error") {
      print "error"
      print ".error"
      print line
      return
    }

    if (f[1] == "comment")
      print "# "line

    if (f[1] != "op")
      return

    key = "op"
    split("",xreg)
    for (j = 3;j <= flen;++j) {
      z = f[j]
      if (match(z,/^</)) {
        split(z,y,/[=]/)
        split(y[1],target,/[#]/)
        xreg[target[1]] = y[2]
      } else if (match(z,/^>/)) {
        split(z,y,/[=]/)
        split(y[1],target,/[#]/)
        xreg[target[1]] = y[2]
      } else if (match(z,/^modify\//)) {
        key = key","z
      }
    }

    xlen = parse(f[2],x)

    for (j = 1;j <= xlen;++j)
      if (match(x[j],/[A-Za-z0-9_]/))
        key = key" x"
      else
        key = key" "x[j]
    if (key in opnum) {
      for (i = 1;i <= opnum[key];++i) {
        split("",mv)
        mlen = split(op[key,i],m)
        for (j = 2;j <= mlen;++j) {
          z = m[j]
          if (match(z,/^>.=/)) {
            sub(/^>/,"",z)
            split(z,y,/[=#]/)
            mv["V" y[1]] = y[2]
          } else if (match(z,/^<.=/)) {
            sub(/^</,"",z)
            split(z,y,/[=#]/)
            mv["V" y[1]] = y[2]
          } else if (match(z,/^var\/.=/)) {
            sub(/^var\//,"",z)
            split(z,y,/[=#]/)
            mv["N" y[1]] = y[2]
          } else if (match(z,/^#.$/)) {
            sub(/^#/,"",z)
            mv["N" z] = ""
          } else if (match(z,/^@.$/)) {
            sub(/^@/,"",z)
            mv["N" z] = ""
          } else if (match(z,/^enter\/.$/)) {
            sub(/^enter\//,"",z)
            mv["N" z] = ""
          } else if (match(z,/^input\/.$/)) {
            sub(/^input\//,"",z)
            mv["V" z] = ""
            mv["N" z] = ""
          } else if (match(z,/^caller\/.$/)) {
            sub(/^caller\//,"",z)
            mv["V" z] = ""
            mv["N" z] = ""
          } else if (match(z,/^output\/.$/)) {
            sub(/^output\//,"",z)
            mv["V" z] = ""
            mv["N" z] = ""
          }
        }
        split("",vmap)
        ylen = parse(m[1],y)
        if (xlen != ylen) continue
        flagok = 1
        for (j = 1;j <= xlen;++j) {
          if (x[j] in vtype && "V" y[j] in mv && mv["V" y[j]] == vtype[x[j]]) {
            vmap[y[j]] = x[j]
            continue
          }
          if (x[j] in vtype && "V" y[j] in mv && mv["V" y[j]] == "") {
            vmap[y[j]] = x[j]
            continue
          }
          if (!(x[j] in vtype) && "N" y[j] in mv) {
            vmap[y[j]] = x[j]
            continue
          }
          if (!(x[j] in vtype) && y[j] == x[j]) {
            vmap[y[j]] = x[j]
            continue
          }
          flagok = 0
          break
        }
        if (flagok) {
          print ""
          print "# "f[2]
          for (j = 2;j <= mlen;++j) {
            z = m[j]
            if (match(z,/^var\/.=/)) {
              sub(/^var\//,"",z)
              split(z,y,/[=#]/)
              vtype[vmap[y[1]]] = y[2]
            } else if (match(z,/^asm\//)) {
              sub(/^asm\//,"",z)
              output1="# "
              output2="# "
              output3=""
              stackshift = 0
              for (k = 1;k <= length(z);++k) {
                if (substr(z,k,6) == "!colon") {
                  output1 = output1 ":"
                  output2 = output2 ":"
                  output3 = output3 ":"
                  k += 5
                  continue
                }
                if (substr(z,k,7) == "!shift4") {
                  stackshift = 4
                  k += 6
                  continue
                }
                if (substr(z,k,6) == "!frame") {
                  output1 = output1 (leftbytes + stackused)
                  output2 = output2 (leftbytes + stackused)
                  output3 = output3 (leftbytes + stackused)
                  k += 5
                  continue
                }
                if (k <= length(z) - 1) {
                  if (substr(z,k,1) == "<") {
                    output1 = output1 "<" vmap[substr(z,k + 1,1)] "=" xreg["<" vmap[substr(z,k + 1,1)]]
                    output2 = output2 "<" vmap[substr(z,k + 1,1)] "=" reg[xreg["<" vmap[substr(z,k + 1,1)]]"+"stackshift]
                    output3 = output3 reg[xreg["<" vmap[substr(z,k + 1,1)]]"+"stackshift]
                    ++k
                    continue
                  } else if (substr(z,k,1) == ">") {
                    output1 = output1 ">" vmap[substr(z,k + 1,1)] "=" xreg[">" vmap[substr(z,k + 1,1)]]
                    output2 = output2 ">" vmap[substr(z,k + 1,1)] "=" reg[xreg[">" vmap[substr(z,k + 1,1)]]"+"stackshift]
                    output3 = output3 reg[xreg[">" vmap[substr(z,k + 1,1)]]"+"stackshift]
                    ++k
                    continue
                  } else if (substr(z,k,1) == "#") {
                    output1 = output1 vmap[substr(z,k + 1,1)]
                    output2 = output2 vmap[substr(z,k + 1,1)]
                    output3 = output3 vmap[substr(z,k + 1,1)]
                    ++k
                    continue
                  }
                }
                output1 = output1 substr(z,k,1)
                output2 = output2 substr(z,k,1)
                output3 = output3 substr(z,k,1)
              }
              if (output1 != output2) print output1
              if (output2 != "# "output3) print output2
              print output3
            }
          }
          return
        }
      }
    }

    print "error"
    print ".error"
    print "error:unknown instruction:"f[2]
  }
  {
    if (readingfromuser) {
      ++linenum
      line[linenum] = $0
    } else {
      dosyntaxline()
    }
  }
  function stackname(i) {
    if (stack512name && (i >= 384)) {
      return stack512nameleft (i - 512) stack512nameright
    }
    if (stack256name && (i >= 128)) {
      return stack256nameleft (i - 256) stack256nameright
    }
    return stacknameleft i stacknameright
  }
  END {
    for (l = 1;l <= linenum;++l) {
      flen = split(line[l],f)
      if (f[1] == "op") {
        for (j = 3;j <= flen;++j) {
          z = f[j]
          if (match(z,/^[<>]/)) {
            split(z,y,/[=]/)
            split(y[2],t,/#/)
            if (t[2] > maxused[t[1]])
              maxused[t[1]] = t[2]
            if (t[2] < minused[t[1]])
              minused[t[1]] = t[2]
          }
        }
      }
    }
    stackused = 0
    for (type in stackbytes) {
      for (l = 1;l <= maxused[type];++l) {
        ++regnum[type]
        reg[type"#"regnum[type]"+0"] = stackname(stackused)
        reg[type"#"regnum[type]"+4"] = stackname(stackused + 4)
        stackused += stackbytes[type]
      }
      if (stackalign)
        while (stackused % stackalign != 0)
          stackused += stackbytes[type]
    }
    argpos = rightbytes
    for (type in stackbytes) {
      for (l = -1;l >= minused[type];--l) {
        reg[type"#"l"+0"] = stackargnameleft argpos stackargnameright
        reg[type"#"l"+4"] = stackargnameleft (argpos+4) stackargnameright
        argpos += stackbytes[type]
      }
    }
    for (l = 1;l <= linenum;++l)
      douserline(line[l])
  }
'
