Add a command line parsing procedure
This commit is contained in:
15
source/CommandLineInterface.def
Normal file
15
source/CommandLineInterface.def
Normal file
@ -0,0 +1,15 @@
|
||||
DEFINITION MODULE CommandLineInterface;
|
||||
|
||||
FROM Common IMPORT ShortString;
|
||||
|
||||
TYPE
|
||||
CommandLine = RECORD
|
||||
input: ShortString;
|
||||
lex: BOOLEAN;
|
||||
parse: BOOLEAN
|
||||
END;
|
||||
PCommandLine = POINTER TO CommandLine;
|
||||
|
||||
PROCEDURE parse_command_line(): PCommandLine;
|
||||
|
||||
END CommandLineInterface.
|
75
source/CommandLineInterface.elna
Normal file
75
source/CommandLineInterface.elna
Normal file
@ -0,0 +1,75 @@
|
||||
implementation module CommandLineInterface;
|
||||
|
||||
from SYSTEM import ADR, TSIZE;
|
||||
|
||||
from Args import GetArg, Narg;
|
||||
from FIO import WriteString, WriteChar, WriteLine, StdErr;
|
||||
from Storage import ALLOCATE;
|
||||
from Strings import CompareStr, Length;
|
||||
from MemUtils import MemZero;
|
||||
|
||||
from Common import ShortString;
|
||||
|
||||
proc parse_command_line() -> PCommandLine;
|
||||
var
|
||||
parameter: ShortString;
|
||||
i: CARDINAL;
|
||||
result: PCommandLine;
|
||||
parsed: BOOLEAN;
|
||||
begin
|
||||
i := 1;
|
||||
ALLOCATE(result, TSIZE(CommandLine));
|
||||
result^.lex := false;
|
||||
result^.parse := false;
|
||||
MemZero(ADR(result^.input), 256);
|
||||
|
||||
while (i < Narg()) & (result <> nil) do
|
||||
parsed := GetArg(parameter, i);
|
||||
parsed := false;
|
||||
|
||||
if CompareStr(parameter, '--lex') = 0 then
|
||||
parsed := true;
|
||||
result^.lex := true
|
||||
end;
|
||||
if CompareStr(parameter, '--parse') = 0 then
|
||||
parsed := true;
|
||||
result^.parse := true
|
||||
end;
|
||||
if parameter[0] <> '-' then
|
||||
parsed := true;
|
||||
|
||||
if Length(result^.input) > 0 then
|
||||
WriteString(StdErr, 'Fatal error: only one source file can be compiled at once. First given "');
|
||||
WriteString(StdErr, result^.input);
|
||||
WriteString(StdErr, '", then "');
|
||||
WriteString(StdErr, parameter);
|
||||
WriteString(StdErr, '".');
|
||||
WriteLine(StdErr);
|
||||
result := nil
|
||||
end;
|
||||
if result <> nil then
|
||||
result^.input := parameter
|
||||
end
|
||||
end;
|
||||
if parsed = false then
|
||||
WriteString(StdErr, 'Fatal error: unknown command line options: ');
|
||||
|
||||
WriteString(StdErr, parameter);
|
||||
WriteChar(StdErr, '.');
|
||||
WriteLine(StdErr);
|
||||
|
||||
result := nil
|
||||
end;
|
||||
|
||||
i := i + 1
|
||||
end;
|
||||
if (result <> nil) & (Length(result^.input) = 0) then
|
||||
WriteString(StdErr, 'Fatal error: no input files.');
|
||||
WriteLine(StdErr);
|
||||
result := nil
|
||||
end;
|
||||
|
||||
return result
|
||||
end;
|
||||
|
||||
end CommandLineInterface.
|
8
source/Common.def
Normal file
8
source/Common.def
Normal file
@ -0,0 +1,8 @@
|
||||
DEFINITION MODULE Common;
|
||||
|
||||
TYPE
|
||||
ShortString = ARRAY[0..255] OF CHAR;
|
||||
Identifier = ARRAY[1..256] OF CHAR;
|
||||
PIdentifier = POINTER TO Identifier;
|
||||
|
||||
END Common.
|
3
source/Common.elna
Normal file
3
source/Common.elna
Normal file
@ -0,0 +1,3 @@
|
||||
implementation module Common;
|
||||
|
||||
end Common.
|
@ -1,18 +1,51 @@
|
||||
module Compiler;
|
||||
|
||||
from FIO import StdIn;
|
||||
from FIO import Close, IsNoError, File, OpenToRead, StdErr, StdOut, WriteLine, WriteString;
|
||||
from SYSTEM import ADR;
|
||||
from M2RTS import HALT, ExitOnHalt;
|
||||
|
||||
from Lexer import Lexer, lexer_destroy, lexer_initialize;
|
||||
from Transpiler import transpile;
|
||||
from CommandLineInterface import PCommandLine, parse_command_line;
|
||||
|
||||
var
|
||||
command_line: PCommandLine;
|
||||
|
||||
proc compile_from_stream();
|
||||
var
|
||||
lexer: Lexer;
|
||||
source_input: File;
|
||||
begin
|
||||
source_input := OpenToRead(command_line^.input);
|
||||
|
||||
if IsNoError(source_input) = false then
|
||||
WriteString(StdErr, 'Fatal error: failed to read the input file "');
|
||||
WriteString(StdErr, command_line^.input);
|
||||
WriteString(StdErr, '".');
|
||||
WriteLine(StdErr);
|
||||
|
||||
ExitOnHalt(2)
|
||||
end;
|
||||
if IsNoError(source_input) then
|
||||
lexer_initialize(ADR(lexer), source_input);
|
||||
|
||||
transpile(ADR(lexer), StdOut);
|
||||
|
||||
lexer_destroy(ADR(lexer));
|
||||
|
||||
Close(source_input)
|
||||
end
|
||||
end;
|
||||
|
||||
begin
|
||||
lexer_initialize(ADR(lexer), StdIn);
|
||||
ExitOnHalt(0);
|
||||
command_line := parse_command_line();
|
||||
|
||||
transpile(ADR(lexer));
|
||||
|
||||
lexer_destroy(ADR(lexer))
|
||||
if command_line <> nil then
|
||||
compile_from_stream()
|
||||
end;
|
||||
if command_line = nil then
|
||||
ExitOnHalt(1)
|
||||
end;
|
||||
HALT()
|
||||
end Compiler.
|
||||
|
@ -2,6 +2,8 @@ DEFINITION MODULE Lexer;
|
||||
|
||||
FROM FIO IMPORT File;
|
||||
|
||||
FROM Common IMPORT Identifier;
|
||||
|
||||
TYPE
|
||||
PLexerBuffer = POINTER TO CHAR;
|
||||
Lexer = RECORD
|
||||
@ -81,7 +83,8 @@ TYPE
|
||||
LexerToken = RECORD
|
||||
CASE kind: LexerKind OF
|
||||
lexerKindBoolean: booleanKind: BOOLEAN |
|
||||
lexerKindIdentifier: identifierKind: ARRAY[1..256] OF CHAR
|
||||
lexerKindIdentifier: identifierKind: Identifier |
|
||||
lexerKindInteger: integerKind: INTEGER
|
||||
END
|
||||
END;
|
||||
PLexerToken = POINTER TO LexerToken;
|
||||
|
@ -1,8 +1,10 @@
|
||||
implementation module Lexer;
|
||||
|
||||
from FIO import ReadNBytes;
|
||||
from SYSTEM import ADR;
|
||||
from FIO import ReadNBytes, StdErr;
|
||||
from SYSTEM import ADR, TSIZE;
|
||||
|
||||
from DynamicStrings import String, InitStringCharStar, KillString;
|
||||
from StringConvert import StringToInteger;
|
||||
from Storage import DEALLOCATE, ALLOCATE;
|
||||
from Strings import Length;
|
||||
from MemUtils import MemCopy, MemZero;
|
||||
@ -211,13 +213,13 @@ begin
|
||||
end
|
||||
end;
|
||||
|
||||
proc compare_keyword(Keyword: ARRAY OF CHAR, TokenStart: PLexerBuffer, TokenEnd: PLexerBuffer) -> BOOLEAN;
|
||||
proc compare_keyword(Keyword: ARRAY OF CHAR, TokenStart: PLexerBuffer, TokenEnd: PLexerBuffer) -> BOOLEAN;
|
||||
var
|
||||
result: BOOLEAN;
|
||||
index: CARDINAL;
|
||||
begin
|
||||
index := 0;
|
||||
result := TRUE;
|
||||
result := true;
|
||||
|
||||
while (index < Length(Keyword)) & (TokenStart <> TokenEnd) & result DO
|
||||
result := (Keyword[index] = TokenStart^) or (Lower(Keyword[index]) = TokenStart^);
|
||||
@ -229,20 +231,20 @@ begin
|
||||
end;
|
||||
|
||||
(* Reached the end of file. *)
|
||||
proc transition_action_eof(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_eof(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
token^.kind := lexerKindEof
|
||||
end;
|
||||
|
||||
(* Add the character to the token currently read and advance to the next character. *)
|
||||
proc transition_action_accumulate(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_accumulate(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
INC(lexer^.Current)
|
||||
end;
|
||||
|
||||
(* The current character is not a part of the token. Finish the token already
|
||||
* read. Don't advance to the next character. *)
|
||||
proc transition_action_finalize(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_finalize(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
if lexer^.Start^ = ':' then
|
||||
token^.kind := lexerKindColon
|
||||
@ -265,7 +267,7 @@ begin
|
||||
end;
|
||||
|
||||
(* An action for tokens containing multiple characters. *)
|
||||
proc transition_action_composite(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_composite(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
if lexer^.Start^ = '<' then
|
||||
if lexer^.Current^ = '>' then
|
||||
@ -291,14 +293,14 @@ begin
|
||||
end;
|
||||
|
||||
(* Skip a space. *)
|
||||
proc transition_action_skip(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_skip(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
INC(lexer^.Current);
|
||||
INC(lexer^.Start)
|
||||
end;
|
||||
|
||||
(* Delimited string action. *)
|
||||
proc transition_action_delimited(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_delimited(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
if lexer^.Start^ = '(' then
|
||||
token^.kind := lexerKindComment
|
||||
@ -313,7 +315,7 @@ begin
|
||||
end;
|
||||
|
||||
(* Finalize keyword or identifier. *)
|
||||
proc transition_action_key_id(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_key_id(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
token^.kind := lexerKindIdentifier;
|
||||
|
||||
@ -409,17 +411,17 @@ begin
|
||||
end;
|
||||
if compare_keyword('TRUE', lexer^.Start, lexer^.Current) then
|
||||
token^.kind := lexerKindBoolean;
|
||||
token^.booleanKind := TRUE
|
||||
token^.booleanKind := true
|
||||
end;
|
||||
if compare_keyword('FALSE', lexer^.Start, lexer^.Current) then
|
||||
token^.kind := lexerKindBoolean;
|
||||
token^.booleanKind := FALSE
|
||||
token^.booleanKind := false
|
||||
end
|
||||
end;
|
||||
|
||||
(* Action for tokens containing only one character. The character cannot be
|
||||
* followed by other characters forming a composite token. *)
|
||||
proc transition_action_single(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_single(lexer: PLexer, token: PLexerToken);
|
||||
begin
|
||||
if lexer^.Current^ = '&' then
|
||||
token^.kind := lexerKindAnd
|
||||
@ -467,12 +469,24 @@ begin
|
||||
end;
|
||||
|
||||
(* Handle an integer literal. *)
|
||||
proc transition_action_integer(lexer: PLexer, token: PLexerToken);
|
||||
proc transition_action_integer(lexer: PLexer, token: PLexerToken);
|
||||
var
|
||||
buffer: String;
|
||||
integer_length: CARDINAL;
|
||||
found: BOOLEAN;
|
||||
begin
|
||||
token^.kind := lexerKindInteger
|
||||
token^.kind := lexerKindInteger;
|
||||
|
||||
integer_length := lexer^.Current - lexer^.Start;
|
||||
MemZero(ADR(token^.identifierKind), TSIZE(Identifier));
|
||||
MemCopy(lexer^.Start, integer_length, ADR(token^.identifierKind[1]));
|
||||
|
||||
buffer := InitStringCharStar(ADR(token^.identifierKind[1]));
|
||||
token^.integerKind := StringToInteger(buffer, 10, found);
|
||||
buffer := KillString(buffer)
|
||||
end;
|
||||
|
||||
proc set_default_transition(CurrentState: TransitionState, DefaultAction: TransitionAction, NextState: TransitionState);
|
||||
proc set_default_transition(CurrentState: TransitionState, DefaultAction: TransitionAction, NextState: TransitionState);
|
||||
var
|
||||
DefaultTransition: Transition;
|
||||
begin
|
||||
@ -757,7 +771,7 @@ begin
|
||||
transitions[ORD(transitionStateDecimalSuffix) + 1][ORD(transitionClassX) + 1].NextState := transitionStateEnd
|
||||
end;
|
||||
|
||||
proc lexer_initialize(lexer: PLexer, Input: File);
|
||||
proc lexer_initialize(lexer: PLexer, Input: File);
|
||||
begin
|
||||
lexer^.Input := Input;
|
||||
lexer^.Length := 0;
|
||||
|
59
source/Parser.def
Normal file
59
source/Parser.def
Normal file
@ -0,0 +1,59 @@
|
||||
DEFINITION MODULE Parser;
|
||||
|
||||
FROM Common IMPORT Identifier, PIdentifier;
|
||||
|
||||
TYPE
|
||||
AstConstantDeclaration = RECORD
|
||||
END;
|
||||
PAstConstantDeclaration = POINTER TO AstConstantDeclaration;
|
||||
PPAstConstantDeclaration = POINTER TO PAstConstantDeclaration;
|
||||
|
||||
AstFieldDeclaration = RECORD
|
||||
field_name: Identifier;
|
||||
field_type: PAstTypeExpression
|
||||
END;
|
||||
PAstFieldDeclaration = POINTER TO AstFieldDeclaration;
|
||||
|
||||
AstTypeExpressionKind = (
|
||||
astTypeExpressionKindNamed,
|
||||
astTypeExpressionKindRecord,
|
||||
astTypeExpressionKindEnumeration,
|
||||
astTypeExpressionKindArray,
|
||||
astTypeExpressionKindPointer,
|
||||
astTypeExpressionKindProcedure
|
||||
);
|
||||
AstTypeExpression = RECORD
|
||||
CASE kind: AstTypeExpressionKind OF
|
||||
astTypeExpressionKindNamed: name: Identifier |
|
||||
astTypeExpressionKindEnumeration: cases: PIdentifier |
|
||||
astTypeExpressionKindPointer: target: PAstTypeExpression |
|
||||
astTypeExpressionKindRecord: fields: PAstFieldDeclaration |
|
||||
astTypeExpressionKindArray:
|
||||
base: PAstTypeExpression;
|
||||
length: CARDINAL |
|
||||
astTypeExpressionKindProcedure: parameters: PPAstTypeExpression
|
||||
END
|
||||
END;
|
||||
PAstTypeExpression = POINTER TO AstTypeExpression;
|
||||
PPAstTypeExpression = POINTER TO PAstTypeExpression;
|
||||
|
||||
AstTypeDeclaration = RECORD
|
||||
identifier: Identifier;
|
||||
type_expression: PAstTypeExpression
|
||||
END;
|
||||
PAstTypeDeclaration = POINTER TO AstTypeDeclaration;
|
||||
PPAstTypeDeclaration = POINTER TO PAstTypeDeclaration;
|
||||
|
||||
AstVariableDeclaration = RECORD
|
||||
END;
|
||||
PAstVariableDeclaration = POINTER TO AstVariableDeclaration;
|
||||
PPAstVariableDeclaration = POINTER TO PAstVariableDeclaration;
|
||||
|
||||
AstModule = RECORD
|
||||
constants: PPAstConstantDeclaration;
|
||||
types: PPAstTypeDeclaration;
|
||||
variables: PPAstVariableDeclaration
|
||||
END;
|
||||
PAstModule = POINTER TO AstModule;
|
||||
|
||||
END Parser.
|
3
source/Parser.elna
Normal file
3
source/Parser.elna
Normal file
@ -0,0 +1,3 @@
|
||||
implementation module Parser;
|
||||
|
||||
end Parser.
|
@ -1,7 +1,17 @@
|
||||
DEFINITION MODULE Transpiler;
|
||||
|
||||
FROM Lexer IMPORT PLexer;
|
||||
FROM FIO IMPORT File;
|
||||
|
||||
PROCEDURE transpile(ALexer: PLexer);
|
||||
FROM Lexer IMPORT PLexer, Lexer;
|
||||
|
||||
TYPE
|
||||
TranspilerContext = RECORD
|
||||
indentation: CARDINAL;
|
||||
output: File;
|
||||
lexer: PLexer
|
||||
END;
|
||||
PTranspilerContext = POINTER TO TranspilerContext;
|
||||
|
||||
PROCEDURE transpile(lexer: PLexer; output: File);
|
||||
|
||||
END Transpiler.
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user