elna/source/Transpiler.elna

631 lines
17 KiB
Plaintext

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 = ^TranspilerContext;
TranspilerContext = record
Indentation: CARDINAL
end;
(* Calls LexerLex() but skips the comments. *)
proc 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. *)
proc WriteSemicolon();
begin
WriteString(';');
WriteLn()
END WriteSemicolon;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc TranspileRecordType(AContext: PTranspilerContext; ALexer: PLexer);
var
Token: LexerToken;
begin
WriteString('RECORD');
WriteLn();
TranspileTypeFields(AContext, ALexer);
WriteString(' END')
END TranspileRecordType;
proc 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;
proc 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;
proc 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;
proc TranspileUnionType(AContext: PTranspilerContext; ALexer: PLexer);
var
Token: LexerToken;
END TranspileUnionType;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc 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;
proc Transpile(ALexer: PLexer);
var
Token: LexerToken;
WrittenBytes: CARDINAL;
Context: TranspilerContext;
begin
TranspileModule(ADR(Context), ALexer)
END Transpile;
END Transpiler.