IMPLEMENTATION MODULE Transpiler; FROM FIO IMPORT WriteNBytes, StdOut, WriteLine, WriteChar; FROM SYSTEM IMPORT ADR, ADDRESS; FROM Terminal IMPORT Write, WriteLn, WriteString; FROM Lexer IMPORT Lexer, LexerToken, lexer_current, lexer_lex, LexerKind; (* Calls lexer_lex() but skips the comments. *) PROCEDURE transpiler_lex(lexer: PLexer): LexerToken; VAR result: LexerToken; BEGIN result := lexer_lex(lexer); WHILE result.kind = lexerKindComment DO result := lexer_lex(lexer) END; RETURN result END transpiler_lex; (* Write a semicolon followed by a newline. *) PROCEDURE write_semicolon(output: File); BEGIN WriteChar(output, ';'); WriteLine(output) END write_semicolon; PROCEDURE write_current(lexer: PLexer); VAR written_bytes: CARDINAL; BEGIN written_bytes := WriteNBytes(StdOut, ADDRESS(lexer^.Current - lexer^.Start), lexer^.Start) END write_current; PROCEDURE transpile_import(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString('FROM '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(' IMPORT '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindSemicolon DO WriteString(', '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; write_semicolon(context^.output); token := transpiler_lex(context^.lexer) END transpile_import; PROCEDURE transpile_import_part(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); WHILE token.kind = lexerKindFrom DO transpile_import(context); token := lexer_current(context^.lexer) END; WriteLn() END transpile_import_part; PROCEDURE transpile_constant(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' '); token := lexer_current(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(' = '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); write_semicolon(context^.output) END transpile_constant; PROCEDURE transpile_constant_part(context: PTranspilerContext): BOOLEAN; VAR token: LexerToken; result: BOOLEAN; BEGIN token := lexer_current(context^.lexer); result := token.kind = lexerKindConst; IF result THEN WriteString('CONST'); WriteLn(); token := transpiler_lex(context^.lexer); WHILE token.kind = lexerKindIdentifier DO transpile_constant(context); token := transpiler_lex(context^.lexer) END END; RETURN result END transpile_constant_part; PROCEDURE transpile_module(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := transpiler_lex(context^.lexer); IF token.kind = lexerKindDefinition THEN WriteString('DEFINITION '); token := transpiler_lex(context^.lexer) END; IF token.kind = lexerKindImplementation THEN WriteString('IMPLEMENTATION '); token := transpiler_lex(context^.lexer) END; WriteString('MODULE '); (* Write the module name and end the line with a semicolon and newline. *) token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); WriteLn(); (* Write the module body. *) token := transpiler_lex(context^.lexer); transpile_import_part(context); IF transpile_constant_part(context) THEN WriteLn() END; transpile_type_part(context); IF transpile_variable_part(context) THEN WriteLn() END; transpile_procedure_part(context); transpile_statement_part(context); WriteString('END '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); Write('.'); token := transpiler_lex(context^.lexer); WriteLn() END transpile_module; PROCEDURE transpile_type_fields(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindEnd DO WriteString(' '); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(': '); transpile_type_expression(context); token := transpiler_lex(context^.lexer); IF token.kind = lexerKindSemicolon THEN token := transpiler_lex(context^.lexer); Write(';') END; WriteLn() END END transpile_type_fields; PROCEDURE transpile_record_type(context: PTranspilerContext); BEGIN WriteString('RECORD'); WriteLn(); transpile_type_fields(context); WriteString(' END') END transpile_record_type; PROCEDURE transpile_pointer_type(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); WriteString('POINTER TO '); IF token.kind = lexerKindPointer THEN token := transpiler_lex(context^.lexer) END; transpile_type_expression(context) END transpile_pointer_type; PROCEDURE transpile_array_type(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString('ARRAY'); token := lexer_current(context^.lexer); IF token.kind = lexerKindArray THEN token := transpiler_lex(context^.lexer) END; IF token.kind <> lexerKindOf THEN WriteString('[1..'); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); Write(']') END; WriteString(' OF '); transpile_type_expression(context) END transpile_array_type; PROCEDURE transpile_enumeration_type(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString('('); WriteLn(); WriteString(' '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WHILE token.kind = lexerKindComma DO Write(','); WriteLn(); WriteString(' '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; WriteLn(); WriteString(' )') END transpile_enumeration_type; PROCEDURE transpile_union_type(context: PTranspilerContext); END transpile_union_type; PROCEDURE transpile_procedure_type(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := transpiler_lex(context^.lexer); WriteString('PROCEDURE('); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindRightParen DO write_current(context^.lexer); token := transpiler_lex(context^.lexer); IF token.kind = lexerKindComma THEN token := transpiler_lex(context^.lexer); WriteString(', ') END END; Write(')') END transpile_procedure_type; PROCEDURE transpile_type_expression(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := transpiler_lex(context^.lexer); IF token.kind = lexerKindRecord THEN transpile_record_type(context) END; IF token.kind = lexerKindLeftParen THEN transpile_enumeration_type(context) END; IF (token.kind = lexerKindArray) OR (token.kind = lexerKindLeftSquare) THEN transpile_array_type(context) END; IF token.kind = lexerKindHat THEN transpile_pointer_type(context) END; IF token.kind = lexerKindProc THEN transpile_procedure_type(context) END; IF token.kind = lexerKindIdentifier THEN write_current(context^.lexer) END END transpile_type_expression; PROCEDURE transpile_type_declaration(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' '); token := lexer_current(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(' = '); transpile_type_expression(context); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); END transpile_type_declaration; PROCEDURE transpile_type_part(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); IF token.kind = lexerKindType THEN WriteString('TYPE'); WriteLn(); token := transpiler_lex(context^.lexer); WHILE token.kind = lexerKindIdentifier DO transpile_type_declaration(context); token := transpiler_lex(context^.lexer) END; WriteLn() END END transpile_type_part; PROCEDURE transpile_variable_declaration(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' '); token := lexer_current(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(': '); transpile_type_expression(context); token := transpiler_lex(context^.lexer); write_semicolon(context^.output) END transpile_variable_declaration; PROCEDURE transpile_variable_part(context: PTranspilerContext): BOOLEAN; VAR token: LexerToken; result: BOOLEAN; BEGIN token := lexer_current(context^.lexer); result := token.kind = lexerKindVar; IF result THEN WriteString('VAR'); WriteLn(); token := transpiler_lex(context^.lexer); WHILE token.kind = lexerKindIdentifier DO transpile_variable_declaration(context); token := transpiler_lex(context^.lexer) END END; RETURN result END transpile_variable_part; PROCEDURE transpile_procedure_heading(context: PTranspilerContext): LexerToken; VAR token: LexerToken; result: LexerToken; BEGIN WriteString('PROCEDURE '); result := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer); Write('('); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindRightParen DO write_current(context^.lexer); token := transpiler_lex(context^.lexer); WriteString(': '); transpile_type_expression(context); token := transpiler_lex(context^.lexer); IF (token.kind = lexerKindSemicolon) OR (token.kind = lexerKindComma) THEN WriteString('; '); token := transpiler_lex(context^.lexer) END END; WriteString(')'); token := transpiler_lex(context^.lexer); (* Check for the return type and write it. *) IF token.kind = lexerKindArrow THEN WriteString(': '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; token := transpiler_lex(context^.lexer); write_semicolon(context^.output); RETURN result END transpile_procedure_heading; PROCEDURE transpile_expression(context: PTranspilerContext; trailing_token: LexerKind); VAR token: LexerToken; written_bytes: CARDINAL; BEGIN token := transpiler_lex(context^.lexer); WHILE (token.kind <> trailing_token) AND (token.kind <> lexerKindEnd) DO written_bytes := 0; IF token.kind = lexerKindNull THEN WriteString('NIL '); written_bytes := 1 END; IF (token.kind = lexerKindBoolean) AND token.booleanKind THEN WriteString('TRUE '); written_bytes := 1 END; IF (token.kind = lexerKindBoolean) AND (~token.booleanKind) THEN WriteString('FALSE '); written_bytes := 1 END; IF token.kind = lexerKindOr THEN WriteString('OR '); written_bytes := 1 END; IF token.kind = lexerKindAnd THEN WriteString('AND '); written_bytes := 1 END; IF token.kind = lexerKindNot THEN WriteString('NOT '); written_bytes := 1 END; IF written_bytes = 0 THEN write_current(context^.lexer); Write(' ') END; token := transpiler_lex(context^.lexer) END END transpile_expression; PROCEDURE transpile_if_statement(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' IF '); transpile_expression(context, lexerKindThen); WriteString('THEN'); WriteLn(); transpile_statements(context); WriteString(' END'); token := transpiler_lex(context^.lexer) END transpile_if_statement; PROCEDURE transpile_while_statement(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' WHILE '); transpile_expression(context, lexerKindDo); WriteString('DO'); WriteLn(); transpile_statements(context); WriteString(' END'); token := transpiler_lex(context^.lexer) END transpile_while_statement; PROCEDURE transpile_assignment_statement(context: PTranspilerContext); BEGIN WriteString(' := '); transpile_expression(context, lexerKindSemicolon); END transpile_assignment_statement; PROCEDURE transpile_call_statement(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString('('); token := transpiler_lex(context^.lexer); WHILE (token.kind <> lexerKindSemicolon) AND (token.kind <> lexerKindEnd) DO write_current(context^.lexer); token := transpiler_lex(context^.lexer) END END transpile_call_statement; PROCEDURE transpile_designator_expression(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' '); write_current(context^.lexer); token := transpiler_lex(context^.lexer); WHILE token.kind = lexerKindLeftSquare DO Write('['); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindRightSquare DO write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; Write(']'); token := transpiler_lex(context^.lexer) END; IF token.kind = lexerKindHat THEN Write('^'); token := transpiler_lex(context^.lexer) END; IF token.kind = lexerKindDot THEN Write('.'); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; IF token.kind = lexerKindHat THEN Write('^'); token := transpiler_lex(context^.lexer) END; WHILE token.kind = lexerKindLeftSquare DO Write('['); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindRightSquare DO write_current(context^.lexer); token := transpiler_lex(context^.lexer) END; Write(']'); token := transpiler_lex(context^.lexer) END END transpile_designator_expression; PROCEDURE transpile_return_statement(context: PTranspilerContext); VAR token: LexerToken; BEGIN WriteString(' RETURN '); token := transpiler_lex(context^.lexer); write_current(context^.lexer); token := transpiler_lex(context^.lexer) END transpile_return_statement; PROCEDURE transpile_statement(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := transpiler_lex(context^.lexer); IF token.kind = lexerKindIf THEN transpile_if_statement(context) END; IF token.kind = lexerKindWhile THEN transpile_while_statement(context) END; IF token.kind = lexerKindReturn THEN transpile_return_statement(context) END; IF token.kind = lexerKindIdentifier THEN transpile_designator_expression(context); token := lexer_current(context^.lexer); IF token.kind = lexerKindAssignment THEN transpile_assignment_statement(context) END; IF token.kind = lexerKindLeftParen THEN transpile_call_statement(context) END END END transpile_statement; PROCEDURE transpile_statements(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); WHILE token.kind <> lexerKindEnd DO transpile_statement(context); token := lexer_current(context^.lexer); IF token.kind = lexerKindSemicolon THEN Write(';') END; WriteLn() END END transpile_statements; PROCEDURE transpile_statement_part(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); IF token.kind = lexerKindBegin THEN WriteString('BEGIN'); WriteLn(); transpile_statements(context) END END transpile_statement_part; PROCEDURE transpile_procedure_declaration(context: PTranspilerContext); VAR token: LexerToken; seen_part: BOOLEAN; written_bytes: CARDINAL; BEGIN token := transpile_procedure_heading(context); seen_part := transpile_constant_part(context); seen_part := transpile_variable_part(context); transpile_statement_part(context); WriteString('END '); written_bytes := WriteNBytes(StdOut, ORD(token.identifierKind[1]), ADR(token.identifierKind[2])); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); token := transpiler_lex(context^.lexer) END transpile_procedure_declaration; PROCEDURE transpile_procedure_part(context: PTranspilerContext); VAR token: LexerToken; BEGIN token := lexer_current(context^.lexer); WHILE token.kind = lexerKindProc DO transpile_procedure_declaration(context); token := lexer_current(context^.lexer); WriteLn() END END transpile_procedure_part; PROCEDURE transpile(lexer: PLexer); VAR token: LexerToken; written_bytes: CARDINAL; context: TranspilerContext; BEGIN context.indentation := 0; context.output := StdOut; context.lexer := lexer; transpile_module(ADR(context)) END transpile; END Transpiler.