// BCAE3.bcpl - BCPL Compiler -- Part 3 of CAE.
// Copyright Xerox Corporation 1980
//  Last modified on Sun 15 Oct 72 1929.16 by  jec.
//  Swinehart, 5-10-77: docase exp

//	Rdef		Read a definition, returning the node thus produced.
//	Rcom		Read a command.
//	Rselect		Read a switchon block.

get "bcaex"

let Rdef() = valof
 [  let A, B = 0, 0
    and Thisline = LinePtr()
    let N = Rnamelist()
    let M = 0
    switchon Symb into   //  What comes after the name list?
     [  case RBRA:   //  A name.  It is a function or routine definition.
	    unless (H1!N & TypeMask) eq LOCAL do
		[ CAEskip(15); goto DefErr ]
	    H1!N = (H1!N & NameMask) + LABEL
	    Nextsymb()
	    if Symb eq NAME % Symb eq NIL do A = Rnamelist()   //  Formal params
	    if Symb eq SEMICOLON do Nextsymb()
	    if Symb ne RKET do
	     [	unless Symb eq NUMARGS do
		  [ CAEskip(15); goto DefErr ]
		Nextsymb()
		M = Rnamelist()
		unless (H1!M & TypeMask) eq LOCAL do
		  [ CAEskip(15); goto DefErr ]
	      ]
	    unless Symb eq RKET do
		[ CAEskip(15); goto DefErr ]
	    Nextsymb()
	    if Symb eq BE do   //  It"s a routine definition.
	     [  Nextsymb()
		B = Rcom(Rcom)
		A = List7(RTDEF, N, A, B, 0, 0, M)   
		endcase
	      ]
	    if Symb eq ASS do   //  It"s a function definition.
	     [  Nextsymb()
		B = Rexp(0)
		A = List7(FNDEF, N, A, B, 0, 0, M)   
		endcase
	      ]
	    CAEskip(15)

	DefErr:
	    B = Rcom(Rcom)
	    A = List6(RTDEF, N, A, B, 0, 0)
	    endcase

	case ASS: 
	    Nextsymb()
	    A = Rexp(0)
	    A = List4(VALDEF, N, A, 0)   //  Extra cell to hold count in Trans.
	    endcase

	default:
	    CAEskip(17)

      ]

    A = List3(LINE, Thisline, A)
    while Symb eq SEMICOLON do Nextsymb()
    if Symb eq AND do
     [  Nextsymb()
	if Symb eq LET do Nextsymb()
	B = Rdef()
	resultis List3(AND, A, B)
      ]
    resultis A
  ]

and Rcom(Nextfn) = valof
 [  let A, B, C, Op = nil, nil, nil, nil
    let Thisline = LinePtr()

    switchon Symb into
     [  default:
	    resultis 0

	case NAME: case NUMBER: case STRINGCONST: case CHARCONST:
	case NOT:
	case SELECTON:
	case NIL: case TRUE: case FALSE:
	case LV: case RV:
	case RBRA: case VALOF: case PLUS: case MINUS:
	case OFFSET: case SIZE:
	    // these are all the symbols which can start an expression
	    A = Rexp(0)
	    if Symb eq ASS do
	     [  Nextsymb()
		B = Rexp(0)
		A = List3(ASS, A, B)
		endcase
	      ]
	    if Symb eq COLON do
	     [  unless (A & NameBit) ne 0 do CAEreport(18)
		A = List2((A & NameMask) + LABEL, 0)
		Nextsymb()
		B = Nextfn(Nextfn)
		A = List5(COLON, A, B, 0, 0)   //  Last cells hold label numbers
		endcase
	      ]
	    if H1!A eq FNAP do
		[ H1!A = RTAP; endcase ]
	    CAEskip(19)
	    endcase

	case GOTO:
	case RESULTIS:
	case DOCASE:
	    Op = Symb
	    Nextsymb()
	    A = Rexp(0)
	    A = List2(Op, A)
	    endcase

	case IF: case UNLESS:
	case WHILE: case UNTIL:
	    Op = Symb
	    Nextsymb()
	    A = Rexp(0)
	    while Symb eq SEMICOLON do Nextsymb()
	    test Symb eq DO
	    then Nextsymb()
	    or   CAEskip(20)
	    B = Rcom(Rcom)
	    A = List3(Op, A, B)
	    endcase

	case TEST:
	    Nextsymb()
	    A = Rexp(0)
	    while Symb eq SEMICOLON do Nextsymb()
	    test Symb eq IFNOT
	    then
	     [  Nextsymb()
		C = Rcom(Rcom)
		while Symb eq SEMICOLON do Nextsymb()
		test Symb eq IFSO 
		then Nextsymb()
		or   CAEskip(20)
		B = Rcom(Rcom)
	      ]
	    or test Symb eq DO % Symb eq IFSO
	    then
	     [  Nextsymb()
		B = Rcom(Rcom)
		while Symb eq SEMICOLON do Nextsymb()
		test Symb eq OR % Symb eq IFNOT 
		then Nextsymb()
		or   CAEskip(20)
		C = Rcom(Rcom)
	      ]
	    or  [ CAEskip(20)
		  A, B = 0, 0
		]
	    A = List4(TEST, A, B, C)
	    endcase

	case FOR:
	 [  Nextsymb()
	    A, B = 0, 0
	    let L, U, S = 0, 0, 0
	    unless Symb eq NAME do
	       [ CAEskip(21); goto ForErr ]
	    A = List2(V!0 + LOCAL, 0)
	    Nextsymb()
	    unless Symb eq ASS do
		[ CAEskip(21); goto ForErr ]
	    Nextsymb()
	    L = Rexp(0)
	    test Symb eq BY
	    then
	     [  Nextsymb()
		S = Rexp(0)
		unless Symb eq TO do
		  [ CAEskip(22); goto ForErr ]
		Nextsymb()
		U = Rexp(0)
	      ]
	    or
	     [  unless Symb eq TO do
		  [ CAEskip(22); goto ForErr ]
		Nextsymb()
		U = Rexp(0)
		if Symb eq BY do [ Nextsymb(); S = Rexp(0) ]
	      ]
	ForErr:
	    while Symb eq SEMICOLON do Nextsymb()
	    test Symb eq DO
	    then Nextsymb()
	    or   CAEskip(20)
	    B = Rcom(Rcom)
	    A = List6(FOR, A, L, U, S, B)
	    endcase
	 ]

	case ENDCASE:
	case LOOP: case BREAK:
	case FINISH: case ABORT:
	case RETURN:
	    A = List1(Symb)
	    Nextsymb()
	    endcase

	case SWITCHON:
	    Op = Symb
	    Nextsymb()
	    A = Rexp(0)
	    while Symb eq SEMICOLON do Nextsymb()
	    test Symb eq INTO 
	    then Nextsymb()
	    or   CAEskip(11)
	    while Symb eq SEMICOLON do Nextsymb()
	    unless Symb eq SECTBRA do CAEskip(12)
	    B = Rblock()
	    A = List3(Op, A, B)
	    endcase

	case CASE:
	    Nextsymb()
	    A = Rexp(0)
	    test Symb eq TO   //  Case label with limits?
	    then [ Nextsymb(); C = Rexp(0) ]
	    or   C = 0
	    test Symb eq COLON
	    then Nextsymb()
	    or   CAEskip(23)
	    B = Nextfn(Nextfn)
	    A = C eq 0 ? List4(CASE, A, B, Thisline), List5(CASETO, A, C, B, Thisline)
	    endcase

	case DEFAULT:
	    Nextsymb()
	    test Symb eq COLON
	    then Nextsymb()
	    or   CAEskip(23)
	    A = Nextfn(Nextfn)
	    A = List2(DEFAULT, A)
	    endcase

	case SECTBRA:
	    A = Rblock()
	    endcase

	case DO: case OR:
	case IFSO: case IFNOT:
	case INTO:
	    CAEreport(25)
	    Nextsymb()
	    A = Nextfn(Nextfn)
	    endcase
      ]

  L:
    switchon Symb into
     [  default: 
	    resultis List3(LINE, Thisline, A)

	case REPEAT:
	    Nextsymb()
	    A = List2(REPEAT, A)
	    goto L

	case REPEATWHILE:
	case REPEATUNTIL:
	 [  Op = Symb
	    Nextsymb()
	    B = Rexp(0)
	    A = List3(Op, A, B)
	    goto L
	  ]
      ]
  ]

and Rselect(ExpOK) = valof
 [  let a, b = nil, nil
    let ThisLine = LinePtr()
    if Symb eq DEFAULT do
     [  Nextsymb()
	    test Symb eq COLON
	    then Nextsymb()
	    or   CAEskip(23)
	a = Rselect(true)
	resultis List3(LINE, ThisLine, List2(DEFAULT, a))
      ]
    if Symb eq CASE do
     [  let c = 0
	Nextsymb()
	a = Rexp(0)
	if Symb eq TO do [ Nextsymb(); c = Rexp(0) ]   //  Case label with limits.
	    test Symb eq COLON
	    then Nextsymb()
	    or   CAEskip(23)
	b = Rselect(true)
	a = c eq 0 ? List4(CASE, a, b, ThisLine), List5(CASETO, a, c, b, ThisLine)
	resultis List3(LINE, ThisLine, a)
      ]
    unless ExpOK do CAEreport(24)
    a = Rexp(0)
    a = List2(RESULTIS, a)
    unless Symb eq SEMICOLON resultis List3(LINE, ThisLine, a)
    Nextsymb()
    b = Rselect(false)
    a = List3(SEQ, a, b)
    resultis List3(LINE, ThisLine, a)
  ]