IMPLEMENTATION MODULE Transpiler; FROM FIO IMPORT WriteNBytes, StdOut; FROM SYSTEM IMPORT ADR, ADDRESS; FROM Terminal IMPORT Write, WriteLn, WriteString; FROM Lexer IMPORT Lexer, LexerToken, LexerCurrent, LexerLex, LexerKind; TYPE PTranspilerContext = POINTER TO TranspilerContext; TranspilerContext = RECORD Indentation: CARDINAL END; (* Calls LexerLex() but skips the comments. *) PROCEDURE TranspilerLex(ALexer: PLexer): LexerToken; VAR Result: LexerToken; BEGIN Result := LexerLex(ALexer); WHILE Result.Kind = lexerKindComment DO Result := LexerLex(ALexer) END; RETURN Result END TranspilerLex; (* Write a semicolon followed by a newline. *) PROCEDURE WriteSemicolon(); BEGIN WriteString(';'); WriteLn() END WriteSemicolon; PROCEDURE TranspileImport(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString('FROM '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(' IMPORT '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindSemicolon DO WriteString(', '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END; WriteSemicolon(); Token := TranspilerLex(ALexer) END TranspileImport; PROCEDURE TranspileImportPart(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN Token := LexerCurrent(ALexer); WHILE Token.Kind = lexerKindFrom DO TranspileImport(AContext, ALexer); Token := LexerCurrent(ALexer) END; WriteLn() END TranspileImportPart; PROCEDURE TranspileConstant(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' '); Token := LexerCurrent(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(' = '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteSemicolon() END TranspileConstant; PROCEDURE TranspileConstantPart(AContext: PTranspilerContext; ALexer: PLexer): BOOLEAN; VAR Token: LexerToken; Result: BOOLEAN; BEGIN Token := LexerCurrent(ALexer); Result := Token.Kind = lexerKindConst; IF Result THEN WriteString('CONST'); WriteLn(); Token := TranspilerLex(ALexer); WHILE Token.Kind = lexerKindIdentifier DO TranspileConstant(AContext, ALexer); Token := TranspilerLex(ALexer) END END; RETURN Result END TranspileConstantPart; PROCEDURE TranspileModule(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindDefinition THEN WriteString('DEFINITION '); Token := TranspilerLex(ALexer) END; IF Token.Kind = lexerKindImplementation THEN WriteString('IMPLEMENTATION '); Token := TranspilerLex(ALexer) END; WriteString('MODULE '); (* Write the module name and end the line with a semicolon and newline. *) Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteSemicolon(); WriteLn(); (* Write the module body. *) Token := TranspilerLex(ALexer); TranspileImportPart(AContext, ALexer); IF TranspileConstantPart(AContext, ALexer) THEN WriteLn() END; TranspileTypePart(AContext, ALexer); IF TranspileVariablePart(AContext, ALexer) THEN WriteLn() END; TranspileProcedurePart(AContext, ALexer); Token := LexerCurrent(ALexer); WHILE Token.Kind <> lexerKindEof DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); WriteLn(); Token := TranspilerLex(ALexer) END END TranspileModule; PROCEDURE TranspileTypeFields(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindEnd DO WriteString(' '); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(': '); TranspileTypeExpression(AContext, ALexer); Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindSemicolon THEN Token := TranspilerLex(ALexer); Write(';') END; WriteLn() END END TranspileTypeFields; PROCEDURE TranspileRecordType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN WriteString('RECORD'); WriteLn(); TranspileTypeFields(AContext, ALexer); WriteString(' END') END TranspileRecordType; PROCEDURE TranspilePointerType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := LexerCurrent(ALexer); WriteString('POINTER TO '); IF Token.Kind = lexerKindPointer THEN Token := TranspilerLex(ALexer) END; TranspileTypeExpression(AContext, ALexer) END TranspilePointerType; PROCEDURE TranspileArrayType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString('ARRAY'); Token := TranspilerLex(ALexer); IF Token.Kind <> lexerKindOf THEN Write('['); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString('..'); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); Write(']'); Token := TranspilerLex(ALexer) END; WriteString(' OF '); TranspileTypeExpression(AContext, ALexer) END TranspileArrayType; PROCEDURE TranspileEnumerationType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString('('); WriteLn(); WriteString(' '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WHILE Token.Kind = lexerKindComma DO Write(','); WriteLn(); WriteString(' '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END; WriteLn(); WriteString(' )') END TranspileEnumerationType; PROCEDURE TranspileUnionType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; END TranspileUnionType; PROCEDURE TranspileProcedureType(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := TranspilerLex(ALexer); WriteString('PROCEDURE('); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindRightParen DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindComma THEN Token := TranspilerLex(ALexer); WriteString(', ') END END; Write(')') END TranspileProcedureType; PROCEDURE TranspileTypeExpression(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindRecord THEN TranspileRecordType(AContext, ALexer) END; IF Token.Kind = lexerKindLeftParen THEN TranspileEnumerationType(AContext, ALexer) END; IF Token.Kind = lexerKindArray THEN TranspileArrayType(AContext, ALexer) END; IF (Token.Kind = lexerKindPointer) OR (Token.Kind = lexerKindHat) THEN TranspilePointerType(AContext, ALexer) END; IF Token.Kind = lexerKindProc THEN TranspileProcedureType(AContext, ALexer) END; IF Token.Kind = lexerKindIdentifier THEN WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start) END END TranspileTypeExpression; PROCEDURE TranspileTypeDeclaration(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' '); Token := LexerCurrent(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(' = '); TranspileTypeExpression(AContext, ALexer); Token := TranspilerLex(ALexer); WriteSemicolon(); END TranspileTypeDeclaration; PROCEDURE TranspileTypePart(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN Token := LexerCurrent(ALexer); IF Token.Kind = lexerKindType THEN WriteString('TYPE'); WriteLn(); Token := TranspilerLex(ALexer); WHILE Token.Kind = lexerKindIdentifier DO TranspileTypeDeclaration(AContext, ALexer); Token := TranspilerLex(ALexer) END; WriteLn() END END TranspileTypePart; PROCEDURE TranspileVariableDeclaration(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' '); Token := LexerCurrent(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(': '); TranspileTypeExpression(AContext, ALexer); Token := TranspilerLex(ALexer); WriteSemicolon() END TranspileVariableDeclaration; PROCEDURE TranspileVariablePart(AContext: PTranspilerContext; ALexer: PLexer): BOOLEAN; VAR Token: LexerToken; Result: BOOLEAN; BEGIN Token := LexerCurrent(ALexer); Result := Token.Kind = lexerKindVar; IF Result THEN WriteString('VAR'); WriteLn(); Token := TranspilerLex(ALexer); WHILE Token.Kind = lexerKindIdentifier DO TranspileVariableDeclaration(AContext, ALexer); Token := TranspilerLex(ALexer) END END; RETURN Result END TranspileVariablePart; PROCEDURE TranspileProcedureHeading(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString('PROCEDURE '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); Write('('); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindRightParen DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteString(': '); TranspileTypeExpression(AContext, ALexer); Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindSemicolon THEN WriteString('; '); Token := TranspilerLex(ALexer) END END; WriteString(')'); Token := TranspilerLex(ALexer); (* Check for the return type and write it. *) IF Token.Kind = lexerKindColon THEN WriteString(': '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END; Token := TranspilerLex(ALexer); WriteSemicolon() END TranspileProcedureHeading; PROCEDURE TranspileIfStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' IF '); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindThen DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Write(' '); Token := TranspilerLex(ALexer) END; WriteString('THEN'); WriteLn(); TranspileStatements(AContext, ALexer); WriteString(' END'); Token := TranspilerLex(ALexer) END TranspileIfStatement; PROCEDURE TranspileWhileStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' WHILE '); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindDo DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Write(' '); Token := TranspilerLex(ALexer) END; WriteString('DO'); WriteLn(); TranspileStatements(AContext, ALexer); WriteString(' END'); Token := TranspilerLex(ALexer) END TranspileWhileStatement; PROCEDURE TranspileAssignmentStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' := '); Token := TranspilerLex(ALexer); WHILE (Token.Kind <> lexerKindSemicolon) AND (Token.Kind <> lexerKindEnd) DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Write(' '); Token := TranspilerLex(ALexer) END END TranspileAssignmentStatement; PROCEDURE TranspileCallStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString('('); Token := TranspilerLex(ALexer); WHILE (Token.Kind <> lexerKindSemicolon) AND (Token.Kind <> lexerKindEnd) DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END END TranspileCallStatement; PROCEDURE TranspileReturnStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN WriteString(' RETURN '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END TranspileReturnStatement; PROCEDURE TranspileStatement(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; BEGIN Token := TranspilerLex(ALexer); IF Token.Kind = lexerKindIf THEN TranspileIfStatement(AContext, ALexer) END; IF Token.Kind = lexerKindWhile THEN TranspileWhileStatement(AContext, ALexer) END; IF Token.Kind = lexerKindReturn THEN TranspileReturnStatement(AContext, ALexer) END; IF Token.Kind = lexerKindIdentifier THEN WriteString(' '); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WHILE Token.Kind = lexerKindLeftSquare DO Write('['); Token := TranspilerLex(ALexer); WHILE Token.Kind <> lexerKindRightSquare DO WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer) END; Write(']'); Token := TranspilerLex(ALexer); END; IF Token.Kind = lexerKindHat THEN Write('^'); Token := TranspilerLex(ALexer) END; IF Token.Kind = lexerKindDot THEN Write('.'); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); END; IF Token.Kind = lexerKindHat THEN Write('^'); Token := TranspilerLex(ALexer) END; IF Token.Kind = lexerKindAssignment THEN TranspileAssignmentStatement(AContext, ALexer) END; IF Token.Kind = lexerKindLeftParen THEN TranspileCallStatement(AContext, ALexer) END END END TranspileStatement; PROCEDURE TranspileStatements(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN Token := LexerCurrent(ALexer); WHILE Token.Kind <> lexerKindEnd DO TranspileStatement(AContext, ALexer); Token := LexerCurrent(ALexer); IF Token.Kind = lexerKindSemicolon THEN Write(';') END; WriteLn() END END TranspileStatements; PROCEDURE TranspileStatementPart(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN Token := LexerCurrent(ALexer); IF Token.Kind = lexerKindBegin THEN WriteString('BEGIN'); WriteLn(); TranspileStatements(AContext, ALexer) END END TranspileStatementPart; PROCEDURE TranspileProcedureDeclaration(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; SeenPart: BOOLEAN; WrittenBytes: CARDINAL; BEGIN TranspileProcedureHeading(AContext, ALexer); SeenPart := TranspileConstantPart(AContext, ALexer); SeenPart := TranspileVariablePart(AContext, ALexer); TranspileStatementPart(AContext, ALexer); WriteString('END '); Token := TranspilerLex(ALexer); WrittenBytes := WriteNBytes(StdOut, ADDRESS(ALexer^.Current - ALexer^.Start), ALexer^.Start); Token := TranspilerLex(ALexer); WriteSemicolon(); Token := TranspilerLex(ALexer) END TranspileProcedureDeclaration; PROCEDURE TranspileProcedurePart(AContext: PTranspilerContext; ALexer: PLexer); VAR Token: LexerToken; BEGIN Token := LexerCurrent(ALexer); WHILE Token.Kind = lexerKindProc DO TranspileProcedureDeclaration(AContext, ALexer); Token := LexerCurrent(ALexer); WriteLn() END END TranspileProcedurePart; PROCEDURE Transpile(ALexer: PLexer); VAR Token: LexerToken; WrittenBytes: CARDINAL; Context: TranspilerContext; BEGIN TranspileModule(ADR(Context), ALexer) END Transpile; END Transpiler.