implementation module Transpiler; from FIO import WriteNBytes, WriteLine, WriteChar, WriteString; from SYSTEM import ADR, ADDRESS, TSIZE; from Storage import ALLOCATE, REALLOCATE; from MemUtils import MemCopy, MemZero; from Common import Identifier, PIdentifier; from Lexer import Lexer, LexerToken, lexer_current, lexer_lex, LexerKind; from Parser import AstModule, PAstModule, AstTypeExpressionKind, AstConstantDeclaration, PPAstConstantDeclaration, AstTypeDeclaration, PAstTypeDeclaration, PPAstTypeDeclaration, AstVariableDeclaration, PPAstVariableDeclaration, PAstTypeExpression, AstTypeExpression; (* Calls lexer_lex() but skips the comments. *) proc 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; (* Write a semicolon followed by a newline. *) proc write_semicolon(output: File); begin WriteChar(output, ';'); WriteLine(output) end; proc write_current(lexer: PLexer, output: File); var written_bytes: CARDINAL; begin written_bytes := WriteNBytes(output, ADDRESS(lexer^.Current - lexer^.Start), lexer^.Start) end; proc transpile_import(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, 'FROM '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteString(context^.output, ' IMPORT '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); while token.kind <> lexerKindSemicolon do WriteString(context^.output, ', '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) end; write_semicolon(context^.output); token := transpiler_lex(context^.lexer) end; proc 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; WriteLine(context^.output) end; proc transpile_constant(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, ' '); token := lexer_current(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteString(context^.output, ' = '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); write_semicolon(context^.output) end; proc transpile_constant_part(context: PTranspilerContext) -> PPAstConstantDeclaration; var token: LexerToken; begin token := lexer_current(context^.lexer); if token.kind = lexerKindConst then WriteString(context^.output, 'CONST'); WriteLine(context^.output); token := transpiler_lex(context^.lexer); while token.kind = lexerKindIdentifier do transpile_constant(context); token := transpiler_lex(context^.lexer) end end; return nil end; proc transpile_module(context: PTranspilerContext) -> PAstModule; var token: LexerToken; result: PAstModule; begin ALLOCATE(result, TSIZE(AstModule)); token := transpiler_lex(context^.lexer); if token.kind = lexerKindDefinition then WriteString(context^.output, 'DEFINITION '); token := transpiler_lex(context^.lexer) end; if token.kind = lexerKindImplementation then WriteString(context^.output, 'IMPLEMENTATION '); token := transpiler_lex(context^.lexer) end; WriteString(context^.output, 'MODULE '); (* Write the module name and end the line with a semicolon and newline. *) token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); WriteLine(context^.output); (* Write the module body. *) token := transpiler_lex(context^.lexer); transpile_import_part(context); result^.constants := transpile_constant_part(context); result^.types := transpile_type_part(context); result^.variables := transpile_variable_part(context); transpile_procedure_part(context); transpile_statement_part(context); WriteString(context^.output, 'END '); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteChar(context^.output, '.'); token := transpiler_lex(context^.lexer); WriteLine(context^.output); return result end; proc transpile_type_fields(context: PTranspilerContext); var token: LexerToken; type_expression: PAstTypeExpression; begin token := transpiler_lex(context^.lexer); while token.kind <> lexerKindEnd do WriteString(context^.output, ' '); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteString(context^.output, ': '); type_expression := transpile_type_expression(context); token := transpiler_lex(context^.lexer); if token.kind = lexerKindSemicolon then token := transpiler_lex(context^.lexer); WriteChar(context^.output, ';') end; WriteLine(context^.output) end end; proc transpile_record_type(context: PTranspilerContext) -> PAstTypeExpression; var result: PAstTypeExpression; begin ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindRecord; WriteString(context^.output, 'RECORD'); WriteLine(context^.output); transpile_type_fields(context); WriteString(context^.output, ' END'); return result end; proc transpile_pointer_type(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; type_expression: PAstTypeExpression; result: PAstTypeExpression; begin ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindPointer; token := lexer_current(context^.lexer); WriteString(context^.output, 'POINTER TO '); if token.kind = lexerKindPointer then token := transpiler_lex(context^.lexer) end; type_expression := transpile_type_expression(context); return result end; proc transpile_array_type(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; type_expression: PAstTypeExpression; result: PAstTypeExpression; begin ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindArray; WriteString(context^.output, 'ARRAY'); token := lexer_current(context^.lexer); if token.kind = lexerKindArray then token := transpiler_lex(context^.lexer) end; if token.kind <> lexerKindOf then WriteString(context^.output, '[1..'); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteChar(context^.output, ']') end; WriteString(context^.output, ' OF '); type_expression := transpile_type_expression(context); return result end; proc transpile_enumeration_type(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; result: PAstTypeExpression; current_case: PIdentifier; case_count: CARDINAL; written_bytes: CARDINAL; begin ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindEnumeration; case_count := 1; ALLOCATE(result^.cases, TSIZE(Identifier) * 2); token := transpiler_lex(context^.lexer); current_case := result^.cases; current_case^ := token.identifierKind; token := transpiler_lex(context^.lexer); while token.kind = lexerKindComma do token := transpiler_lex(context^.lexer); INC(case_count); REALLOCATE(result^.cases, TSIZE(Identifier) * (case_count + 1)); current_case := result^.cases; INC(current_case, TSIZE(Identifier) * (case_count - 1)); current_case^ := token.identifierKind; token := transpiler_lex(context^.lexer) end; INC(current_case, TSIZE(Identifier)); MemZero(current_case, TSIZE(Identifier)); (* Write the cases using the generated identifier list before. *) current_case := result^.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, ' )'); return result end; proc transpile_named_type(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; result: PAstTypeExpression; written_bytes: CARDINAL; begin token := lexer_current(context^.lexer); ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindNamed; result^.name := token.identifierKind; written_bytes := WriteNBytes(context^.output, ORD(result^.name[1]), ADR(result^.name[2])); return result end; proc transpile_procedure_type(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; result: PAstTypeExpression; begin ALLOCATE(result, TSIZE(AstTypeExpression)); result^.kind := astTypeExpressionKindProcedure; token := transpiler_lex(context^.lexer); WriteString(context^.output, 'PROCEDURE('); token := transpiler_lex(context^.lexer); while token.kind <> lexerKindRightParen do write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); if token.kind = lexerKindComma then token := transpiler_lex(context^.lexer); WriteString(context^.output, ', ') end end; WriteChar(context^.output, ')'); return nil end; proc transpile_type_expression(context: PTranspilerContext) -> PAstTypeExpression; var token: LexerToken; result: PAstTypeExpression; begin result := nil; token := transpiler_lex(context^.lexer); if token.kind = lexerKindRecord then result := transpile_record_type(context) end; if token.kind = lexerKindLeftParen then result := transpile_enumeration_type(context) end; if (token.kind = lexerKindArray) or (token.kind = lexerKindLeftSquare) then result := transpile_array_type(context) end; if token.kind = lexerKindHat then result := transpile_pointer_type(context) end; if token.kind = lexerKindProc then result := transpile_procedure_type(context) end; if token.kind = lexerKindIdentifier then result := transpile_named_type(context) end; return result end; proc transpile_type_declaration(context: PTranspilerContext) -> PAstTypeDeclaration; var token: LexerToken; result: PAstTypeDeclaration; written_bytes: CARDINAL; begin WriteString(context^.output, ' '); token := lexer_current(context^.lexer); ALLOCATE(result, TSIZE(AstTypeDeclaration)); result^.identifier := token.identifierKind; written_bytes := WriteNBytes(context^.output, ORD(token.identifierKind[1]), ADR(token.identifierKind[2])); token := transpiler_lex(context^.lexer); WriteString(context^.output, ' = '); result^.type_expression := transpile_type_expression(context); token := transpiler_lex(context^.lexer); write_semicolon(context^.output); return result end; proc transpile_type_part(context: PTranspilerContext) -> PPAstTypeDeclaration; var token: LexerToken; result: PPAstTypeDeclaration; current_declaration: PPAstTypeDeclaration; declaration_count: CARDINAL; begin token := lexer_current(context^.lexer); ALLOCATE(result, TSIZE(PAstTypeDeclaration)); current_declaration := result; declaration_count := 0; if token.kind = lexerKindType then WriteString(context^.output, 'TYPE'); WriteLine(context^.output); token := transpiler_lex(context^.lexer); while token.kind = lexerKindIdentifier do INC(declaration_count); REALLOCATE(result, TSIZE(PAstTypeDeclaration) * (declaration_count + 1)); current_declaration := result; INC(current_declaration, TSIZE(PAstTypeDeclaration) * (declaration_count - 1)); current_declaration^ := transpile_type_declaration(context); token := transpiler_lex(context^.lexer) end; WriteLine(context^.output) end; if declaration_count <> 0 then INC(current_declaration, TSIZE(PAstTypeDeclaration)) end; current_declaration^ := nil; return result end; proc transpile_variable_declaration(context: PTranspilerContext); var token: LexerToken; type_expression: PAstTypeExpression; begin WriteString(context^.output, ' '); token := lexer_current(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); WriteString(context^.output, ': '); type_expression := transpile_type_expression(context); token := transpiler_lex(context^.lexer); write_semicolon(context^.output) end; proc transpile_variable_part(context: PTranspilerContext) -> PPAstVariableDeclaration; var token: LexerToken; begin token := lexer_current(context^.lexer); if token.kind = lexerKindVar then WriteString(context^.output, 'VAR'); WriteLine(context^.output); token := transpiler_lex(context^.lexer); while token.kind = lexerKindIdentifier do transpile_variable_declaration(context); token := transpiler_lex(context^.lexer) end end; return nil end; proc 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, ': '); type_expression := transpile_type_expression(context); 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; proc transpile_expression(context: PTranspilerContext, trailing_token: LexerKind); var token: LexerToken; written_bytes: CARDINAL; begin token := transpiler_lex(context^.lexer); while (token.kind <> trailing_token) & (token.kind <> lexerKindEnd) do written_bytes := 0; if token.kind = lexerKindNull then WriteString(context^.output, 'NIL '); written_bytes := 1 end; if (token.kind = lexerKindBoolean) & token.booleanKind then WriteString(context^.output, 'TRUE '); written_bytes := 1 end; if (token.kind = lexerKindBoolean) & (~token.booleanKind) then WriteString(context^.output, 'FALSE '); written_bytes := 1 end; if token.kind = lexerKindOr then WriteString(context^.output, 'OR '); written_bytes := 1 end; if token.kind = lexerKindAnd then WriteString(context^.output, 'AND '); written_bytes := 1 end; if token.kind = lexerKindNot then WriteString(context^.output, 'NOT '); written_bytes := 1 end; if written_bytes = 0 then write_current(context^.lexer, context^.output); WriteChar(context^.output, ' ') end; token := transpiler_lex(context^.lexer) end end; proc transpile_if_statement(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, ' IF '); transpile_expression(context, lexerKindThen); WriteString(context^.output, 'THEN'); WriteLine(context^.output); transpile_statements(context); WriteString(context^.output, ' END'); token := transpiler_lex(context^.lexer) end; proc transpile_while_statement(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, ' WHILE '); transpile_expression(context, lexerKindDo); WriteString(context^.output, 'DO'); WriteLine(context^.output); transpile_statements(context); WriteString(context^.output, ' END'); token := transpiler_lex(context^.lexer) end; proc transpile_assignment_statement(context: PTranspilerContext); begin WriteString(context^.output, ' := '); transpile_expression(context, lexerKindSemicolon); end; proc transpile_call_statement(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, '('); token := transpiler_lex(context^.lexer); while (token.kind <> lexerKindSemicolon) & (token.kind <> lexerKindEnd) do write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) end end; proc transpile_designator_expression(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, ' '); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer); while token.kind = lexerKindLeftSquare do WriteChar(context^.output, '['); token := transpiler_lex(context^.lexer); while token.kind <> lexerKindRightSquare do write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) end; WriteChar(context^.output, ']'); token := transpiler_lex(context^.lexer) end; if token.kind = lexerKindHat then WriteChar(context^.output, '^'); token := transpiler_lex(context^.lexer) end; if token.kind = lexerKindDot then WriteChar(context^.output, '.'); token := transpiler_lex(context^.lexer); write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) end; if token.kind = lexerKindHat then WriteChar(context^.output, '^'); token := transpiler_lex(context^.lexer) end; while token.kind = lexerKindLeftSquare do WriteChar(context^.output, '['); token := transpiler_lex(context^.lexer); while token.kind <> lexerKindRightSquare do write_current(context^.lexer, context^.output); token := transpiler_lex(context^.lexer) end; WriteChar(context^.output, ']'); token := transpiler_lex(context^.lexer) end end; proc transpile_return_statement(context: PTranspilerContext); var token: LexerToken; begin WriteString(context^.output, ' RETURN '); transpile_expression(context, lexerKindSemicolon) end; proc 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; proc 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; proc 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; proc 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 := transpile_constant_part(context); seen_variables := transpile_variable_part(context); 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; proc 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; proc transpile(lexer: PLexer, output: File); var token: LexerToken; context: TranspilerContext; ast_module: PAstModule; begin context.indentation := 0; context.output := output; context.lexer := lexer; ast_module := transpile_module(ADR(context)) end; end Transpiler.