138 lines
5.6 KiB
Text
138 lines
5.6 KiB
Text
BEGIN
|
|
# implement C-like getchar, where EOF and EOLn are "characters" (-1 and 10 resp.). #
|
|
INT eof = -1, eoln = 10;
|
|
BOOL eof flag := FALSE;
|
|
STRING buf := "";
|
|
INT col := 1;
|
|
INT line := 0;
|
|
on logical file end (stand in, (REF FILE f)BOOL: eof flag := TRUE);
|
|
PROC getchar = INT:
|
|
IF eof flag THEN eof
|
|
ELIF col = UPB buf THEN col +:= 1; eoln
|
|
ELIF col > UPB buf THEN IF line > 0 THEN read(newline) FI;
|
|
line +:= 1;
|
|
read(buf);
|
|
IF eof flag THEN col := 1; eof
|
|
ELSE col := 0; getchar
|
|
FI
|
|
ELSE col +:= 1; ABS buf[col]
|
|
FI;
|
|
PROC nextchar = INT: IF eof flag THEN eof ELIF col >= UPB buf THEN eoln ELSE ABS buf[col+1] FI;
|
|
|
|
PROC is blank = (INT ch) BOOL: ch = 0 OR ch = 9 OR ch = 10 OR ch = 13 OR ch = ABS " ";
|
|
PROC is digit = (INT ch) BOOL: ch >= ABS "0" AND ch <= ABS "9";
|
|
PROC is ident start = (INT ch) BOOL: ch >= ABS "A" AND ch <= ABS "Z" OR
|
|
ch >= ABS "a" AND ch <= ABS "z" OR
|
|
ch = ABS "_";
|
|
PROC is ident = (INT ch) BOOL: is ident start(ch) OR is digit(ch);
|
|
|
|
PROC ident or keyword = (INT start char) VOID:
|
|
BEGIN
|
|
STRING w := REPR start char;
|
|
INT start col = col;
|
|
WHILE is ident (next char) DO w +:= REPR getchar OD;
|
|
IF w = "if" THEN output2("Keyword_if", start col)
|
|
ELIF w = "else" THEN output2("Keyword_else", start col)
|
|
ELIF w = "while" THEN output2("Keyword_while", start col)
|
|
ELIF w = "print" THEN output2("Keyword_print", start col)
|
|
ELIF w = "putc" THEN output2("Keyword_putc", start col)
|
|
ELSE output2("Identifier " + w, start col)
|
|
FI
|
|
END;
|
|
PROC char = VOID:
|
|
BEGIN
|
|
INT start col = col;
|
|
INT ch := getchar;
|
|
IF ch = ABS "'" THEN error("Empty character constant")
|
|
ELIF ch = ABS "\" THEN ch := getchar;
|
|
IF ch = ABS "n" THEN ch := 10
|
|
ELIF ch = ABS "\" THEN SKIP
|
|
ELSE error("Unknown escape sequence. \" + REPR ch)
|
|
FI
|
|
FI;
|
|
IF nextchar /= ABS "'" THEN error("Multi-character constant.") FI;
|
|
getchar;
|
|
output2("Integer " + whole(ch, 0), start col)
|
|
END;
|
|
PROC string = VOID:
|
|
BEGIN
|
|
INT start col = col;
|
|
STRING s := """";
|
|
WHILE INT ch := getchar; ch /= ABS """"
|
|
DO
|
|
IF ch = eoln THEN error("End-of-line while scanning string literal. Closing string character not found before end-of-line.")
|
|
ELIF ch = eof THEN error("End-of-file while scanning string literal. Closing string character not found.")
|
|
ELIF ch = ABS "\" THEN s +:= REPR ch; ch := getchar;
|
|
IF ch /= ABS "\" AND ch /= ABS "n" THEN error("Unknown escape sequence. \" + REPR ch) FI;
|
|
s +:= REPR ch
|
|
ELSE s +:= REPR ch
|
|
FI
|
|
OD;
|
|
output2("String " + s + """", start col)
|
|
END;
|
|
PROC comment = VOID:
|
|
BEGIN
|
|
WHILE INT ch := getchar; NOT (ch = ABS "*" AND nextchar = ABS "/")
|
|
DO IF ch = eof THEN error("End-of-file in comment. Closing comment characters not found.") FI
|
|
OD;
|
|
getchar
|
|
END;
|
|
PROC number = (INT first digit) VOID:
|
|
BEGIN
|
|
INT start col = col;
|
|
INT n := first digit - ABS "0";
|
|
WHILE is digit (nextchar) DO
|
|
INT u := getchar - ABS "0";
|
|
IF LENG n * 10 + LENG u > max int THEN error("Integer too big") FI;
|
|
n := n * 10 + u
|
|
OD;
|
|
IF is ident start (nextchar) THEN error("Invalid number. Starts like a number, but ends in non-numeric characters.") FI;
|
|
output2("Integer " + whole(n, 0), start col)
|
|
END;
|
|
|
|
PROC output = (STRING s) VOID: output2(s, col);
|
|
PROC output2 = (STRING s, INT col) VOID: print((whole(line,-8), whole(col,-8), " ", s, newline));
|
|
|
|
PROC if follows = (CHAR second, STRING longer, shorter) VOID:
|
|
IF nextchar = ABS second
|
|
THEN output(longer); getchar
|
|
ELSE output(shorter)
|
|
FI;
|
|
PROC error = (STRING s)VOID: (put(stand error, ("At ", whole(line,0), ":", whole(col,0), " ", s, new line)); stop);
|
|
PROC unrecognized = (INT char) VOID: error("Unrecognized character " + REPR char);
|
|
PROC double char = (INT first, STRING op) VOID:
|
|
IF nextchar /= first THEN unrecognized(first)
|
|
ELSE output2(op, col-1); getchar
|
|
FI;
|
|
|
|
WHILE INT ch := getchar; ch /= eof
|
|
DO
|
|
IF is blank(ch) THEN SKIP
|
|
ELIF ch = ABS "(" THEN output("LeftParen")
|
|
ELIF ch = ABS ")" THEN output("RightParen")
|
|
ELIF ch = ABS "{" THEN output("LeftBrace")
|
|
ELIF ch = ABS "}" THEN output("RightBrace")
|
|
ELIF ch = ABS ";" THEN output("Semicolon")
|
|
ELIF ch = ABS "," THEN output("Comma")
|
|
ELIF ch = ABS "*" THEN output("Op_multiply")
|
|
ELIF ch = ABS "/" THEN IF next char = ABS "*" THEN comment
|
|
ELSE output("Op_divide")
|
|
FI
|
|
ELIF ch = ABS "%" THEN output("Op_mod")
|
|
ELIF ch = ABS "+" THEN output("Op_add")
|
|
ELIF ch = ABS "-" THEN output("Op_subtract")
|
|
ELIF ch = ABS "<" THEN if follows("=", "Op_lessequal", "Op_less")
|
|
ELIF ch = ABS ">" THEN if follows("=", "Op_greaterequal", "Op_greater")
|
|
ELIF ch = ABS "=" THEN if follows("=", "Op_equal", "Op_assign")
|
|
ELIF ch = ABS "!" THEN if follows("=", "Op_notequal", "Op_not")
|
|
ELIF ch = ABS "&" THEN double char(ch, "Op_and")
|
|
ELIF ch = ABS "|" THEN double char(ch, "Op_or")
|
|
ELIF is ident start (ch) THEN ident or keyword (ch)
|
|
ELIF ch = ABS """" THEN string
|
|
ELIF ch = ABS "'" THEN char
|
|
ELIF is digit(ch) THEN number(ch)
|
|
ELSE unrecognized(ch)
|
|
FI
|
|
OD;
|
|
output("End_Of_Input")
|
|
END
|