// BTRN1.bcpl - BCPL Compiler -- Trans, Part 1.
// Copyright Xerox Corporation 1980
//  Last modified on Sun 29 Oct 72 0438.48 by  jec.

//  last modified by Butterfield, January 25, 1979  3:19 PM
//  - Trans, ignore END - 1/25/79

//	Trans		Translate a piece of AE-Tree into OCODE.

get "btrnx"		//  Declarations for Trans.

let Trans(x) be
 [  if x eq 0 return
    let lab = 0 // For RESULTIS, DOCASE
    switchon H1!x into
     [  case LET:
	    TransLET(x)
	    return

  	case EXT:
  	case MANIFEST:
	case STATIC:
	case STRUCTURE:
	 [  let S = SSP
	    let SB = SwitchBlock
	    SwitchBlock = false
	    Trans(H2!x)
	    unless S eq SSP do TRNreport(-1)
	    SwitchBlock = SB
	    return
	  ]

	case LINE:
	    Curline = H2!x
	    if SWList do [ if SWOcode do WW($*n); WriteLine(Curline) ]
	    test FrameLevel eq 0 then Out2(LINE, Curline) or Out2(PLINE, Curline)
	    Trans(H3!x)
	    return

	case END:
	    Trans(H3!x)
	    return

	case ASS:
	    unless Assign(H2!x, H3!x) eq 0 do TRNreport(3)   //  Expression list too short.
	    return

	case RTAP:
	    Transcall(x)
	    return

	case GOTO:
	    Load(H2!x)
	    Out1(GOTO)
	    SSP = SSP - 1
	    return

	case COLON:
	    Complentry(H5!x, H4!x)
	    Out2(STACK, SSP)
	    Trans(H3!x)
	    return

	case IF:
	case UNLESS:
	 [  let L = Nextparam()
	    Jumpcond(H2!x, H1!x eq UNLESS, L)
	    Trans(H3!x)
	    Complab(L)
	    return
	  ]

	case TEST:
	 [  let L, M = Nextparam(), Nextparam()
	    Jumpcond(H2!x, false, L)
	    Trans(H3!x)
	    Compjump(M)
	    Complab(L)
	    Trans(H4!x)
	    Complab(M)
	    return
	  ]

	case BREAK:
	    unless RepeatBlock do TRNreport(4)
	    if Breaklabel eq 0 do Breaklabel = Nextparam()
	    Compjump(Breaklabel)
	    return

	case LOOP:
	    unless RepeatBlock do TRNreport(4)
	    if Looplabel eq 0 do Looplabel = Nextparam()
	    Compjump(Looplabel)
	    return

	case RETURN:
	    unless RoutineBody do TRNreport(5)
	    Out1(RTRN)
	    return

  	case FINISH:
	case ABORT:
	    Out1(H1!x)
	    return

	case DOCASE: // Same action as RESULTIS, jump to different place
	    unless Endcaselabel do [ TRNreport(6); return ] // out of context
	    unless Docaselabel do Docaselabel = Nextparam()
	    lab = Docaselabel
	case RESULTIS:
	    unless lab do lab = Resultlabel
	    Load(H2!x)
	    unless lab do TRNreport(5) // Occurs for RESULTIS only.
	    Out2P(RES, lab)
	    SSP = SSP - 1
	    return

	case WHILE:
	case UNTIL:
	 [  let L = Nextparam()   //  Label for the beginning of the block, to jump back to.
	    let LL, BL = Looplabel, Breaklabel
	    and RB = RepeatBlock
	    RepeatBlock = true   //  break and loop are now legal.
	    Looplabel = Nextparam()
	    Breaklabel = 0
	    Compjump(Looplabel)   //  Jump to the test.
	    Complab(L)
	    Trans(H3!x)
	    Complab(Looplabel)
	    Jumpcond(H2!x, H1!x eq WHILE, L)
	    unless Breaklabel eq 0 do [ Complab(Breaklabel); Out2(STACK, SSP) ]
	    Breaklabel, Looplabel = BL, LL
	    RepeatBlock = RB
	    return
	  ]

	case REPEAT:
	case REPEATWHILE:
	case REPEATUNTIL:
	 [  let L = Nextparam()   //  Label for the beginning of the block.
	    let LL, BL = Looplabel, Breaklabel
	    and RB = RepeatBlock
	    RepeatBlock = true
	    Breaklabel = 0
	    Looplabel = H1!x eq REPEAT ? L, 0
	    Complab(L)
	    Trans(H2!x)
	    test  H1!x eq REPEAT
	    ifso  Compjump(L)
	    ifnot
	     [  unless Looplabel eq 0 do Complab(Looplabel)
		Jumpcond(H3!x, H1!x eq REPEATWHILE, L) 
	      ]
	    unless Breaklabel eq 0 do [ Complab(Breaklabel); Out2(STACK, SSP) ]
	    Looplabel, Breaklabel = LL, BL
	    RepeatBlock = RB
	    return
	  ]

	case CASE:
	 [  unless SwitchBlock do TRNreport(7)
	    if CaseP + CaseN > CaseT do [ TRNreport(8); return ]   //  Too many cases.
	    let L = Nextparam()   //  The label for this piece of code.
	    Casetable!CaseP = GetConst(H2+x)   //  The case constant.
	    Casetable!(CaseP+1) = L
	    Casetable!(CaseP+2) = H4!x   //  The line number.
	    CaseP = CaseP + CaseN
	    Complab(L)   //  Place the label.
	    Trans(H3!x)
	    return
	  ]

	case CASETO:   //  Case label with limits.
	 [  unless SwitchBlock do TRNreport(7)   //  Must be in a switch block.
	    let M = Nextparam()   //  The label the cases refer to.
	    let L, U = GetConst(H2+x), GetConst(H3+x)   //  The limits.
	    let N = U - L + 1   //  Number of cases to generate.
	    unless N > 0 do [ TRNreport(12); return ]   //  Oops.
	    unless CaseP + CaseN*N le CaseT do [ TRNreport(8); return ]
	    for k = 0 to N-1 do   //  Place the cases.
	     [  Casetable! CaseP    = L + k   //  The value.
		Casetable!(CaseP+1) = M       //  The label.
		Casetable!(CaseP+2) = H5!x    //  The line number.
		CaseP = CaseP + CaseN
	      ]
	    Complab(M)   //  Place the label.
	    Trans(H4!x)
	    return
	  ]

	case DEFAULT:
	    unless SwitchBlock do TRNreport(7)   //  Not in a switch block.
	    unless Defaultlabel eq 0 do TRNreport(10)   //  default used twice.
	    Defaultlabel = Nextparam()
	    Complab(Defaultlabel)
	    Trans(H2!x)
	    return

	case ENDCASE:
	    if Endcaselabel eq 0 do [ TRNreport(6); return ]   //  Not in a switch block.
	    Compjump(Endcaselabel)
	    return

	case SWITCHON:
	    Transswitch(x)
	    return

	case FOR:
	 [  let LL, BL = Looplabel, Breaklabel
	    and RB, SB = RepeatBlock, SwitchBlock
	    Looplabel, Breaklabel = 0, 0
	    RepeatBlock, SwitchBlock = true, false
	    let L = Nextparam()   //  Label for the test.
	    and M = Nextparam()   //  Label for the body.
	    let S1 = SSP   //  The stack position into which the controlled variable goes.
	    let d = H2!x; H2!d = S1	//  Declare the controlled variable
	    Load(H3!x)   //  Load the lower limit into the stack.
	    Out1(NEWLOCAL); OutL(H1!d & NameMask)
	    let Stepsize = H5!x eq 0 ? 1, GetConst(H5+x)   //  The increment.
	    let S2 = SSP   //  The stack position for the upper limit.
	    and Limit = nil   //  The value of the upper limit, if it is a constant.
	    Limit = CheckConst(H4+x)   //  Optimize the upper limit.
	    test  Limit > 0   //  Is its value known at compile time?
	    ifso  S2, Limit = -1, rv Limit   //  Yes, so set S2 < 0 as a switch.
	    ifnot [ Load(H4!x)   //  No, so load it into stack position S2.
		    Out1(NEWLOCAL); OutL(0) ]
	    Out1(STORE)
	    Compjump(L)   //  Jump to the test.
	    Complab(M)   //  The label for the body.
	    Trans(H6!x)		//  ...
	    unless Looplabel eq 0 do Complab(Looplabel)
	    Out2(LP, S1); OutL(d!0 & NameMask)   //  Load the controlled variable.
	    Out2(LN, Stepsize); OutL(0)
	    Out1(PLUS)
	    Out2(SP, S1); OutL(d!0 & NameMask)   //  Add the step size and store back.
	    Complab(L)   //  Label the test.
	    Out2(LP, S1); OutL(d!0 & NameMask)   //  Load the controlled variable.
	    test S2 > 0 ifso Out2(LP, S2) ifnot Out2(LN, Limit);   OutL(0)
	    Out1(Stepsize > 0 ? LE, GE); Out2P(JT, M)   //  The appropriate test.
	    unless Breaklabel eq 0 do Complab(Breaklabel)
	    SSP = S1   //  Put it back.
	    Out2(STACK, SSP)
	    Breaklabel, Looplabel = BL, LL
	    RepeatBlock, SwitchBlock = RB, SB
	    return
	  ]

	case SEQ:
	    Trans(H2!x)
	    Trans(H3!x)
	    return

///*DCS* conditional compilation
	case COMPILETEST:
	    Trans(H2!x)
	    return

	default:
	    TRNreport(-2)   //  Error report.
      ]
  ]