IMPLEMENTATION MODULE Transpiler; FROM FIO IMPORT StdErr, WriteNBytes, WriteLine, WriteChar, WriteString; FROM SYSTEM IMPORT ADR, ADDRESS, TSIZE; FROM NumberIO IMPORT IntToStr; FROM Storage IMPORT ALLOCATE, REALLOCATE; FROM MemUtils IMPORT MemCopy, MemZero; FROM Common IMPORT Identifier, PIdentifier, ShortString; FROM Lexer IMPORT Lexer, LexerToken, lexer_current, lexer_lex, LexerKind; FROM Parser IMPORT AstTypeExpressionKind, AstExpressionKind, AstLiteralKind, AstUnaryOperator, AstBinaryOperator, AstModule, PAstModule, AstExpression, PPAstExpression, PAstExpression, PAstLiteral, PAstConstantDeclaration, PPAstConstantDeclaration, PAstStatement, AstStatementKind, AstTypeDeclaration, PAstTypeDeclaration, PPAstTypeDeclaration, PAstVariableDeclaration, PPAstVariableDeclaration, PAstImportStatement, PPAstImportStatement, PAstTypeExpression, PPAstTypeExpression, AstFieldDeclaration, PAstFieldDeclaration, parse_type_expression, parse_variable_part, parse_type_part, parse_constant_part, parse_import_part, parse_designator, parse_expression, parse_return_statement, parse_assignment_statement, parse_call_statement; (* 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; output: File); VAR written_bytes: CARDINAL; count: CARDINAL; BEGIN count := lexer^.current; DEC(count, lexer^.start); written_bytes := WriteNBytes(output, count, lexer^.start) END write_current; PROCEDURE transpile_import_statement(context: PTranspilerContext; import_statement: PAstImportStatement); VAR token: LexerToken; written_bytes: CARDINAL; current_symbol: PIdentifier; BEGIN WriteString(context^.output, 'FROM '); written_bytes := WriteNBytes(context^.output, ORD(import_statement^.package[1]), ADR(import_statement^.package[2])); WriteString(context^.output, ' IMPORT '); current_symbol := import_statement^.symbols; written_bytes := WriteNBytes(context^.output, ORD(current_symbol^[1]), ADR(current_symbol^[2])); INC(current_symbol, TSIZE(Identifier)); WHILE ORD(current_symbol^[1]) <> 0 DO WriteString(context^.output, ', '); written_bytes := WriteNBytes(context^.output, ORD(current_symbol^[1]), ADR(current_symbol^[2])); INC(current_symbol, TSIZE(Identifier)) END; write_semicolon(context^.output) END transpile_import_statement; PROCEDURE transpile_import_part(context: PTranspilerContext; imports: PPAstImportStatement); VAR import_statement: PAstImportStatement; BEGIN WHILE imports^ <> NIL DO transpile_import_statement(context, imports^); INC(imports, TSIZE(PAstImportStatement)) END; WriteLine(context^.output) END transpile_import_part; PROCEDURE transpile_constant_declaration(context: PTranspilerContext; declaration: PAstConstantDeclaration); VAR buffer: ARRAY[1..20] OF CHAR; written_bytes: CARDINAL; BEGIN WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(declaration^.constant_name[1]), ADR(declaration^.constant_name[2])); WriteString(context^.output, ' = '); IntToStr(declaration^.constant_value, 0, buffer); WriteString(context^.output, buffer); write_semicolon(context^.output) END transpile_constant_declaration; PROCEDURE transpile_constant_part(context: PTranspilerContext; declarations: PPAstConstantDeclaration); VAR current_declaration: PPAstConstantDeclaration; BEGIN IF declarations^ <> NIL THEN WriteString(context^.output, 'CONST'); WriteLine(context^.output); current_declaration := declarations; WHILE current_declaration^ <> NIL DO transpile_constant_declaration(context, current_declaration^); INC(current_declaration, TSIZE(PAstConstantDeclaration)) END; WriteLine(context^.output) END END transpile_constant_part; PROCEDURE transpile_module(context: PTranspilerContext): PAstModule; VAR token: LexerToken; result: PAstModule; BEGIN NEW(result); token := transpiler_lex(context^.lexer); IF token.kind = lexerKindModule THEN WriteString(context^.output, 'IMPLEMENTATION ') END; WriteString(context^.output, 'MODULE '); (* Write the module name and end the line with a semicolon and newline. *) transpile_module_name(context); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); WriteLine(context^.output); (* Write the module body. *) token := transpiler_lex(context^.lexer); result^.imports := parse_import_part(context^.lexer); transpile_import_part(context, result^.imports); result^.constants := parse_constant_part(context^.lexer); transpile_constant_part(context, result^.constants); result^.types := parse_type_part(context^.lexer); transpile_type_part(context, result^.types); result^.variables := parse_variable_part(context^.lexer); transpile_variable_part(context, result^.variables); transpile_procedure_part(context); transpile_statement_part(context); WriteString(context^.output, 'END '); transpile_module_name(context); token := transpiler_lex(context^.lexer); WriteChar(context^.output, '.'); token := transpiler_lex(context^.lexer); WriteLine(context^.output); RETURN result END transpile_module; PROCEDURE transpile_type_fields(context: PTranspilerContext; fields: PAstFieldDeclaration); VAR written_bytes: CARDINAL; current_field: PAstFieldDeclaration; BEGIN current_field := fields; WHILE ORD(current_field^.field_name[1]) <> 0 DO WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(current_field^.field_name[1]), ADR(current_field^.field_name[2])); WriteString(context^.output, ': '); transpile_type_expression(context, current_field^.field_type); INC(current_field , TSIZE(AstFieldDeclaration)); IF ORD(current_field^.field_name[1]) <> 0 THEN WriteChar(context^.output, ';') END; WriteLine(context^.output) END END transpile_type_fields; PROCEDURE transpile_record_type(context: PTranspilerContext; type_expression: PAstTypeExpression); BEGIN WriteString(context^.output, 'RECORD'); WriteLine(context^.output); transpile_type_fields(context, type_expression^.fields); WriteString(context^.output, ' END') END transpile_record_type; PROCEDURE transpile_pointer_type(context: PTranspilerContext; type_expression: PAstTypeExpression); VAR token: LexerToken; BEGIN WriteString(context^.output, 'POINTER TO '); transpile_type_expression(context, type_expression^.target) END transpile_pointer_type; PROCEDURE transpile_array_type(context: PTranspilerContext; type_expression: PAstTypeExpression); VAR buffer: ARRAY[1..20] OF CHAR; BEGIN WriteString(context^.output, 'ARRAY'); IF type_expression^.length <> 0 THEN WriteString(context^.output, '[1..'); IntToStr(type_expression^.length, 0, buffer); WriteString(context^.output, buffer); WriteChar(context^.output, ']') END; WriteString(context^.output, ' OF '); transpile_type_expression(context, type_expression^.base) END transpile_array_type; PROCEDURE transpile_enumeration_type(context: PTranspilerContext; type_expression: PAstTypeExpression); VAR current_case: PIdentifier; written_bytes: CARDINAL; BEGIN current_case := type_expression^.cases; WriteString(context^.output, '('); WriteLine(context^.output); WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(current_case^[1]), ADR(current_case^[2])); INC(current_case, TSIZE(Identifier)); WHILE ORD(current_case^[1]) <> 0 DO WriteChar(context^.output, ','); WriteLine(context^.output); WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(current_case^[1]), ADR(current_case^[2])); INC(current_case, TSIZE(Identifier)) END; WriteLine(context^.output); WriteString(context^.output, ' )') END transpile_enumeration_type; PROCEDURE transpile_named_type(context: PTranspilerContext; type_expression: PAstTypeExpression); VAR written_bytes: CARDINAL; BEGIN written_bytes := WriteNBytes(context^.output, ORD(type_expression^.name[1]), ADR(type_expression^.name[2])) END transpile_named_type; PROCEDURE transpile_procedure_type(context: PTranspilerContext; type_expression: PAstTypeExpression); VAR result: PAstTypeExpression; current_parameter: PPAstTypeExpression; parameter_count: CARDINAL; BEGIN WriteString(context^.output, 'PROCEDURE('); current_parameter := type_expression^.parameters; WHILE current_parameter^ <> NIL DO transpile_type_expression(context, current_parameter^); INC(current_parameter, TSIZE(PAstTypeExpression)); IF current_parameter^ <> NIL THEN WriteString(context^.output, ', ') END END; WriteChar(context^.output, ')') END transpile_procedure_type; PROCEDURE transpile_type_expression(context: PTranspilerContext; type_expression: PAstTypeExpression); BEGIN IF type_expression^.kind = astTypeExpressionKindRecord THEN transpile_record_type(context, type_expression) END; IF type_expression^.kind = astTypeExpressionKindEnumeration THEN transpile_enumeration_type(context, type_expression) END; IF type_expression^.kind = astTypeExpressionKindArray THEN transpile_array_type(context, type_expression) END; IF type_expression^.kind = astTypeExpressionKindPointer THEN transpile_pointer_type(context, type_expression) END; IF type_expression^.kind = astTypeExpressionKindProcedure THEN transpile_procedure_type(context, type_expression) END; IF type_expression^.kind = astTypeExpressionKindNamed THEN transpile_named_type(context, type_expression) END END transpile_type_expression; PROCEDURE transpile_type_declaration(context: PTranspilerContext; declaration: PAstTypeDeclaration); VAR written_bytes: CARDINAL; BEGIN WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(declaration^.identifier[1]), ADR(declaration^.identifier[2])); WriteString(context^.output, ' = '); transpile_type_expression(context, declaration^.type_expression); write_semicolon(context^.output) END transpile_type_declaration; PROCEDURE transpile_type_part(context: PTranspilerContext; declarations: PPAstTypeDeclaration); VAR current_declaration: PPAstTypeDeclaration; BEGIN IF declarations^ <> NIL THEN WriteString(context^.output, 'TYPE'); WriteLine(context^.output); current_declaration := declarations; WHILE current_declaration^ <> NIL DO transpile_type_declaration(context, current_declaration^); INC(current_declaration, TSIZE(PAstTypeDeclaration)) END; WriteLine(context^.output) END END transpile_type_part; PROCEDURE transpile_variable_declaration(context: PTranspilerContext; declaration: PAstVariableDeclaration); VAR written_bytes: CARDINAL; BEGIN WriteString(context^.output, ' '); written_bytes := WriteNBytes(context^.output, ORD(declaration^.variable_name[1]), ADR(declaration^.variable_name[2])); WriteString(context^.output, ': '); transpile_type_expression(context, declaration^.variable_type); write_semicolon(context^.output) END transpile_variable_declaration; PROCEDURE transpile_variable_part(context: PTranspilerContext; declarations: PPAstVariableDeclaration); VAR current_declaration: PPAstVariableDeclaration; BEGIN IF declarations^ <> NIL THEN WriteString(context^.output, 'VAR'); WriteLine(context^.output); current_declaration := declarations; WHILE current_declaration^ <> NIL DO transpile_variable_declaration(context, current_declaration^); INC(current_declaration, TSIZE(PAstVariableDeclaration)) END; WriteLine(context^.output) END END transpile_variable_part; PROCEDURE transpile_procedure_heading(context: PTranspilerContext): LexerToken; VAR token: LexerToken; result: LexerToken; type_expression: PAstTypeExpression; BEGIN WriteString(context^.output, 'PROCEDURE '); result := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteChar(context^.output, '('); token := transpiler_lex(context^.lexer); WHILE token.kind <> lexerKindRightParen DO write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteString(context^.output, ': '); token := transpiler_lex(context^.lexer); type_expression := parse_type_expression(context^.lexer); transpile_type_expression(context, type_expression); token := transpiler_lex(context^.lexer); IF (token.kind = lexerKindSemicolon) OR (token.kind = lexerKindComma) THEN WriteString(context^.output, '; '); token := transpiler_lex(context^.lexer) END END; WriteString(context^.output, ')'); token := transpiler_lex(context^.lexer); (* Check for the return type and write it. *) IF token.kind = lexerKindArrow THEN WriteString(context^.output, ': '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) END; token := transpiler_lex(context^.lexer); write_semicolon(context^.output); RETURN result END transpile_procedure_heading; PROCEDURE transpile_unary_operator(context: PTranspilerContext; operator: AstUnaryOperator); BEGIN IF operator = astUnaryOperatorMinus THEN WriteChar(context^.output, '-') END; IF operator = astUnaryOperatorNot THEN WriteChar(context^.output, '~') END END transpile_unary_operator; PROCEDURE transpile_binary_operator(context: PTranspilerContext; operator: AstBinaryOperator); BEGIN IF operator = astBinaryOperatorSum THEN WriteChar(context^.output, '+') END; IF operator = astBinaryOperatorSubtraction THEN WriteChar(context^.output, '-') END; IF operator = astBinaryOperatorMultiplication THEN WriteChar(context^.output, '*') END; IF operator = astBinaryOperatorEquals THEN WriteChar(context^.output, '=') END; IF operator = astBinaryOperatorNotEquals THEN WriteChar(context^.output, '#') END; IF operator = astBinaryOperatorLess THEN WriteChar(context^.output, '<') END; IF operator = astBinaryOperatorGreater THEN WriteChar(context^.output, '>') END; IF operator = astBinaryOperatorLessEqual THEN WriteString(context^.output, '<=') END; IF operator = astBinaryOperatorGreaterEqual THEN WriteString(context^.output, '>=') END; IF operator = astBinaryOperatorDisjunction THEN WriteString(context^.output, 'OR') END; IF operator = astBinaryOperatorConjunction THEN WriteString(context^.output, 'AND') END END transpile_binary_operator; PROCEDURE transpile_expression(context: PTranspilerContext; expression: PAstExpression); VAR literal: PAstLiteral; buffer: ARRAY[1..20] OF CHAR; written_bytes: CARDINAL; argument_index: CARDINAL; current_argument: PPAstExpression; BEGIN IF expression^.kind = astExpressionKindLiteral THEN literal := expression^.literal; IF literal^.kind = astLiteralKindInteger THEN IntToStr(literal^.integer, 0, buffer); WriteString(context^.output, buffer); END; IF literal^.kind = astLiteralKindString THEN WriteString(context^.output, literal^.string) END; IF literal^.kind = astLiteralKindNull THEN WriteString(context^.output, 'NIL') END; IF (literal^.kind = astLiteralKindBoolean) AND literal^.boolean THEN WriteString(context^.output, 'TRUE') END; IF (literal^.kind = astLiteralKindBoolean) AND (literal^.boolean = FALSE) THEN WriteString(context^.output, 'FALSE') END END; IF expression^.kind = astExpressionKindIdentifier THEN written_bytes := WriteNBytes(context^.output, ORD(expression^.identifier[1]), ADR(expression^.identifier[2])) END; IF expression^.kind = astExpressionKindDereference THEN transpile_expression(context, expression^.reference); WriteChar(context^.output, '^') END; IF expression^.kind = astExpressionKindArrayAccess THEN transpile_expression(context, expression^.array); WriteChar(context^.output, '['); transpile_expression(context, expression^.index); WriteChar(context^.output, ']') END; IF expression^.kind = astExpressionKindFieldAccess THEN transpile_expression(context, expression^.aggregate); WriteChar(context^.output, '.'); written_bytes := WriteNBytes(context^.output, ORD(expression^.field[1]), ADR(expression^.field[2])); END; IF expression^.kind = astExpressionKindUnary THEN transpile_unary_operator(context, expression^.unary_operator); transpile_expression(context, expression^.unary_operand) END; IF expression^.kind = astExpressionKindBinary THEN WriteChar(context^.output, '('); transpile_expression(context, expression^.lhs); WriteChar(context^.output, ' '); transpile_binary_operator(context, expression^.binary_operator); WriteChar(context^.output, ' '); transpile_expression(context, expression^.rhs); WriteChar(context^.output, ')') END; IF expression^.kind = astExpressionKindCall THEN transpile_expression(context, expression^.callable); WriteChar(context^.output, '('); current_argument := expression^.arguments; IF expression^.argument_count > 0 THEN transpile_expression(context, current_argument^); argument_index := 1; INC(current_argument, TSIZE(PAstExpression)); WHILE argument_index < expression^.argument_count DO WriteString(context^.output, ', '); transpile_expression(context, current_argument^); INC(current_argument, TSIZE(PAstExpression)); INC(argument_index) END END; WriteChar(context^.output, ')') END END transpile_expression; PROCEDURE transpile_if_statement(context: PTranspilerContext): PAstStatement; VAR token: LexerToken; result: PAstStatement; BEGIN NEW(result); result^.kind := astStatementKindIf; WriteString(context^.output, ' IF '); token := transpiler_lex(context^.lexer); result^.if_condition := parse_expression(context^.lexer); transpile_expression(context, result^.if_condition); token := lexer_current(context^.lexer); WriteString(context^.output, ' THEN'); WriteLine(context^.output); transpile_statements(context); WriteString(context^.output, ' END'); token := transpiler_lex(context^.lexer); RETURN result END transpile_if_statement; PROCEDURE transpile_while_statement(context: PTranspilerContext): PAstStatement; VAR token: LexerToken; result: PAstStatement; BEGIN NEW(result); result^.kind := astStatementKindWhile; WriteString(context^.output, ' WHILE '); token := transpiler_lex(context^.lexer); result^.while_condition := parse_expression(context^.lexer); transpile_expression(context, result^.while_condition); token := lexer_current(context^.lexer); WriteString(context^.output, ' DO'); WriteLine(context^.output); transpile_statements(context); WriteString(context^.output, ' END'); token := transpiler_lex(context^.lexer); RETURN result END transpile_while_statement; PROCEDURE transpile_assignment_statement(context: PTranspilerContext; statement: PAstStatement); BEGIN transpile_expression(context, statement^.assignee); WriteString(context^.output, ' := '); transpile_expression(context, statement^.assignment) END transpile_assignment_statement; PROCEDURE transpile_return_statement(context: PTranspilerContext; statement: PAstStatement); BEGIN WriteString(context^.output, ' RETURN '); transpile_expression(context, statement^.returned); END transpile_return_statement; PROCEDURE transpile_statement(context: PTranspilerContext); VAR token: LexerToken; written_bytes: CARDINAL; statement: PAstStatement; designator: PAstExpression; BEGIN token := transpiler_lex(context^.lexer); IF token.kind = lexerKindIf THEN statement := transpile_if_statement(context) END; IF token.kind = lexerKindWhile THEN statement := transpile_while_statement(context) END; IF token.kind = lexerKindReturn THEN statement := parse_return_statement(context^.lexer); transpile_return_statement(context, statement) END; IF token.kind = lexerKindIdentifier THEN designator := parse_designator(context^.lexer); token := lexer_current(context^.lexer); IF token.kind = lexerKindAssignment THEN statement := parse_assignment_statement(context^.lexer, designator); transpile_assignment_statement(context, statement) END; IF token.kind <> lexerKindAssignment THEN statement := parse_call_statement(context^.lexer, designator); transpile_expression(context, designator); written_bytes := WriteNBytes(StdErr, 5, context^.lexer^.start); WriteLine(StdErr); 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 WriteChar(context^.output, ';') END; WriteLine(context^.output) 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(context^.output, 'BEGIN'); WriteLine(context^.output); transpile_statements(context) END END transpile_statement_part; PROCEDURE transpile_procedure_declaration(context: PTranspilerContext); VAR token: LexerToken; seen_variables: PPAstVariableDeclaration; written_bytes: CARDINAL; seen_constants: PPAstConstantDeclaration; BEGIN token := transpile_procedure_heading(context); seen_constants := parse_constant_part(context^.lexer); transpile_constant_part(context, seen_constants); seen_variables := parse_variable_part(context^.lexer); transpile_variable_part(context, seen_variables); transpile_statement_part(context); WriteString(context^.output, 'END '); written_bytes := WriteNBytes(context^.output, 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); WriteLine(context^.output) END END transpile_procedure_part; PROCEDURE transpile_module_name(context: PTranspilerContext); VAR counter: CARDINAL; last_slash: CARDINAL; BEGIN counter := 1; last_slash := 0; WHILE (context^.input_name[counter] <> '.') AND (ORD(context^.input_name[counter]) <> 0) DO IF context^.input_name[counter] = '/' THEN last_slash := counter END; INC(counter) END; IF last_slash = 0 THEN counter := 1 END; IF last_slash <> 0 THEN counter := last_slash + 1 END; WHILE (context^.input_name[counter] <> '.') AND (ORD(context^.input_name[counter]) <> 0) DO WriteChar(context^.output, context^.input_name[counter]); INC(counter) END; END transpile_module_name; PROCEDURE transpile(lexer: PLexer; output: File; input_name: ShortString); VAR token: LexerToken; context: TranspilerContext; ast_module: PAstModule; BEGIN context.input_name := input_name; context.output := output; context.lexer := lexer; ast_module := transpile_module(ADR(context)) END transpile; END Transpiler.