Remove semicolons after field declarations
This commit is contained in:
parent
8206b48dbd
commit
fccea0f938
4
Rakefile
4
Rakefile
@ -1,6 +1,6 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
# v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
# obtain one at https://mozilla.org/MPL/2.0/. -}
|
||||
# obtain one at https://mozilla.org/MPL/2.0/.
|
||||
|
||||
require 'pathname'
|
||||
require 'open3'
|
||||
@ -21,7 +21,7 @@ directory HOST_GCC
|
||||
directory HOST_INSTALL
|
||||
|
||||
task default: [TMP + 'elna'] do
|
||||
sh (TMP + 'elna').to_path, '--lex', 'source.elna'
|
||||
sh (TMP + 'elna').to_path, '--parse', 'source.elna'
|
||||
end
|
||||
|
||||
namespace :boot do
|
||||
|
@ -454,9 +454,9 @@ optional_statements:
|
||||
field_declaration:
|
||||
IDENTIFIER ":" type_expression { $$ = std::make_pair($1, $3); }
|
||||
required_fields:
|
||||
field_declaration ";" required_fields
|
||||
field_declaration required_fields
|
||||
{
|
||||
std::swap($$, $3);
|
||||
std::swap($$, $2);
|
||||
$$.emplace($$.cbegin(), $1);
|
||||
}
|
||||
| field_declaration { $$.emplace_back($1); }
|
||||
|
@ -620,7 +620,7 @@ namespace elna::gcc
|
||||
else
|
||||
{
|
||||
error_at(expression_location,
|
||||
"invalid operands of type '%s' and '%s' for operator %s",
|
||||
"Invalid operands of type '%s' and '%s' for operator %s",
|
||||
print_type(left_type).c_str(), print_type(right_type).c_str(),
|
||||
elna::boot::print_binary_operator(expression->operation()));
|
||||
return error_mark_node;
|
||||
|
761
source.elna
761
source.elna
@ -1,3 +1,6 @@
|
||||
(* This Source Code Form is subject to the terms of the Mozilla Public License,
|
||||
v. 2.0. If a copy of the MPL was not distributed with this file, You can
|
||||
obtain one at https://mozilla.org/MPL/2.0/. *)
|
||||
program
|
||||
|
||||
import dummy
|
||||
@ -78,48 +81,52 @@ type
|
||||
_import
|
||||
)
|
||||
Position* = record
|
||||
line: Word;
|
||||
line: Word
|
||||
column: Word
|
||||
end
|
||||
Location* = record
|
||||
first: Position;
|
||||
first: Position
|
||||
last: Position
|
||||
end
|
||||
SourceFile* = record
|
||||
buffer: [1024]Char;
|
||||
handle: ^FILE;
|
||||
size: Word;
|
||||
buffer: [1024]Char
|
||||
handle: ^FILE
|
||||
size: Word
|
||||
index: Word
|
||||
end
|
||||
FILE* = record end
|
||||
StringBuffer* = record
|
||||
data: ^Byte;
|
||||
size: Word;
|
||||
data: ^Byte
|
||||
size: Word
|
||||
capacity: Word
|
||||
end
|
||||
SourceCode = record
|
||||
position: Position;
|
||||
position: Position
|
||||
|
||||
input: ^Byte;
|
||||
empty: proc(^Byte) -> Bool;
|
||||
advance: proc(^Byte);
|
||||
input: ^Byte
|
||||
empty: proc(^Byte) -> Bool
|
||||
advance: proc(^Byte)
|
||||
head: proc(^Byte) -> Char
|
||||
end
|
||||
Token* = record
|
||||
kind: TokenKind;
|
||||
kind: TokenKind
|
||||
value: union
|
||||
int_value: Int;
|
||||
string: String;
|
||||
boolean_value: Bool;
|
||||
int_value: Int
|
||||
string: String
|
||||
boolean_value: Bool
|
||||
char_value: Char
|
||||
end;
|
||||
end
|
||||
location: Location
|
||||
end
|
||||
CommandLine* = record
|
||||
input: ^Char;
|
||||
lex: Bool;
|
||||
input: ^Char
|
||||
lex: Bool
|
||||
parse: Bool
|
||||
end
|
||||
Lexer* = record
|
||||
length: Word
|
||||
data: ^Token
|
||||
end
|
||||
|
||||
(*
|
||||
External procedures.
|
||||
@ -359,7 +366,7 @@ end
|
||||
Token procedures.
|
||||
*)
|
||||
|
||||
proc escape_char(escape: Char, result: ^Char) -> Bool;
|
||||
proc lexer_escape(escape: Char, result: ^Char) -> Bool;
|
||||
var
|
||||
successful: Bool
|
||||
begin
|
||||
@ -405,7 +412,8 @@ begin
|
||||
return successful
|
||||
end
|
||||
|
||||
proc skip_spaces(source_code: ^SourceCode);
|
||||
(* Skip spaces. *)
|
||||
proc lexer_spaces(source_code: ^SourceCode);
|
||||
var
|
||||
current: Char
|
||||
begin
|
||||
@ -419,21 +427,22 @@ begin
|
||||
end
|
||||
end
|
||||
|
||||
proc is_ident(char: Char) -> Bool;
|
||||
(* Checker whether the character is allowed in an identificator. *)
|
||||
proc lexer_is_ident(char: Char) -> Bool;
|
||||
return is_alnum(char) or char = '_'
|
||||
end
|
||||
|
||||
proc lex_identifier(source_code: ^SourceCode, token_content: ^StringBuffer);
|
||||
proc lexer_identifier(source_code: ^SourceCode, token_content: ^StringBuffer);
|
||||
var
|
||||
content_length: Word
|
||||
begin
|
||||
while ~source_code_empty(source_code) & is_ident(source_code_head(source_code^)) do
|
||||
while ~source_code_empty(source_code) & lexer_is_ident(source_code_head(source_code^)) do
|
||||
string_buffer_push(token_content, source_code_head(source_code^));
|
||||
source_code_advance(source_code)
|
||||
end
|
||||
end
|
||||
|
||||
proc lex_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
|
||||
proc lexer_comment(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
|
||||
var
|
||||
trailing: Word
|
||||
begin
|
||||
@ -456,7 +465,7 @@ begin
|
||||
return trailing = 2u
|
||||
end
|
||||
|
||||
proc lex_character(source_code: ^SourceCode, token_content: ^Char) -> Bool;
|
||||
proc lexer_character(source_code: ^SourceCode, token_content: ^Char) -> Bool;
|
||||
var
|
||||
successful: Bool
|
||||
begin
|
||||
@ -466,7 +475,7 @@ begin
|
||||
if source_code_head(source_code^) = '\\' then
|
||||
source_code_advance(source_code);
|
||||
|
||||
successful := ~source_code_empty(source_code) & escape_char(source_code_head(source_code^), token_content)
|
||||
successful := ~source_code_empty(source_code) & lexer_escape(source_code_head(source_code^), token_content)
|
||||
else
|
||||
token_content^ := source_code_head(source_code^);
|
||||
successful := true
|
||||
@ -478,7 +487,7 @@ begin
|
||||
return successful
|
||||
end
|
||||
|
||||
proc lex_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
|
||||
proc lexer_string(source_code: ^SourceCode, token_content: ^StringBuffer) -> Bool;
|
||||
var
|
||||
token_end, constructed_string: ^Char
|
||||
token_length: Word
|
||||
@ -488,7 +497,7 @@ begin
|
||||
is_valid := true;
|
||||
|
||||
while is_valid & ~source_code_empty(source_code) & source_code_head(source_code^) <> '"' do
|
||||
is_valid := lex_character(source_code, @next_char);
|
||||
is_valid := lexer_character(source_code, @next_char);
|
||||
|
||||
if is_valid then
|
||||
string_buffer_push(token_content, next_char)
|
||||
@ -503,7 +512,7 @@ begin
|
||||
return is_valid
|
||||
end
|
||||
|
||||
proc lex_number(source_code: ^SourceCode, token_content: ^Int);
|
||||
proc lexer_number(source_code: ^SourceCode, token_content: ^Int);
|
||||
begin
|
||||
token_content^ := 0;
|
||||
|
||||
@ -514,7 +523,342 @@ begin
|
||||
end
|
||||
end
|
||||
|
||||
proc print_tokens(tokens: ^Token, tokens_size: Word);
|
||||
(* Categorize an identifier. *)
|
||||
proc lexer_categorize(token_content: String) -> Token;
|
||||
var
|
||||
current_token: Token
|
||||
begin
|
||||
if token_content = "if" then
|
||||
current_token.kind := TokenKind._if
|
||||
elsif token_content = "then" then
|
||||
current_token.kind := TokenKind._then
|
||||
elsif token_content = "else" then
|
||||
current_token.kind := TokenKind._else
|
||||
elsif token_content = "elsif" then
|
||||
current_token.kind := TokenKind._elsif
|
||||
elsif token_content = "while" then
|
||||
current_token.kind := TokenKind._while
|
||||
elsif token_content = "do" then
|
||||
current_token.kind := TokenKind._do
|
||||
elsif token_content = "proc" then
|
||||
current_token.kind := TokenKind._proc
|
||||
elsif token_content = "begin" then
|
||||
current_token.kind := TokenKind._begin
|
||||
elsif token_content = "end" then
|
||||
current_token.kind := TokenKind._end
|
||||
elsif token_content = "extern" then
|
||||
current_token.kind := TokenKind._extern
|
||||
elsif token_content = "const" then
|
||||
current_token.kind := TokenKind._const
|
||||
elsif token_content = "var" then
|
||||
current_token.kind := TokenKind._var
|
||||
elsif token_content = "case" then
|
||||
current_token.kind := TokenKind._case
|
||||
elsif token_content = "of" then
|
||||
current_token.kind := TokenKind._of
|
||||
elsif token_content = "type" then
|
||||
current_token.kind := TokenKind._type
|
||||
elsif token_content = "record" then
|
||||
current_token.kind := TokenKind._record
|
||||
elsif token_content = "union" then
|
||||
current_token.kind := TokenKind._union
|
||||
elsif token_content = "true" then
|
||||
current_token.kind := TokenKind.boolean;
|
||||
current_token.value.boolean_value := true
|
||||
elsif token_content = "false" then
|
||||
current_token.kind := TokenKind.boolean;
|
||||
current_token.value.boolean_value := false
|
||||
elsif token_content = "nil" then
|
||||
current_token.kind := TokenKind.null
|
||||
elsif token_content = "or" then
|
||||
current_token.kind := TokenKind._or
|
||||
elsif token_content = "return" then
|
||||
current_token.kind := TokenKind._return
|
||||
elsif token_content = "cast" then
|
||||
current_token.kind := TokenKind._cast
|
||||
elsif token_content = "defer" then
|
||||
current_token.kind := TokenKind._defer
|
||||
elsif token_content = "program" then
|
||||
current_token.kind := TokenKind._program
|
||||
elsif token_content = "module" then
|
||||
current_token.kind := TokenKind._module
|
||||
elsif token_content = "import" then
|
||||
current_token.kind := TokenKind._import
|
||||
else
|
||||
current_token.kind := TokenKind.identifier;
|
||||
current_token.value.string := string_dup(token_content)
|
||||
end;
|
||||
|
||||
return current_token
|
||||
end
|
||||
|
||||
proc lexer_add_token(lexer: ^Lexer, token: Token);
|
||||
var
|
||||
new_length: Word
|
||||
begin
|
||||
new_length := lexer^.length + 1u;
|
||||
lexer^.data := cast(reallocarray(cast(lexer^.data: ^Byte), new_length, #size(Token)): ^Token);
|
||||
(lexer^.data + lexer^.length)^ := token;
|
||||
lexer^.length := new_length
|
||||
end
|
||||
|
||||
(* Read the next token from the input. *)
|
||||
proc lexer_next(source_code: SourceCode, token_buffer: ^StringBuffer) -> Token;
|
||||
var
|
||||
current_token: Token
|
||||
first_char: Char
|
||||
begin
|
||||
current_token.kind := TokenKind.unknown;
|
||||
|
||||
first_char := source_code_head(source_code);
|
||||
|
||||
if is_alpha(first_char) or first_char = '_' then
|
||||
lexer_identifier(@source_code, token_buffer);
|
||||
current_token := lexer_categorize(string_buffer_clear(token_buffer))
|
||||
elsif first_char = '#' then
|
||||
source_code_advance(@source_code);
|
||||
lexer_identifier(@source_code, token_buffer);
|
||||
|
||||
current_token.kind := TokenKind.trait;
|
||||
current_token.value.string := string_dup(string_buffer_clear(token_buffer))
|
||||
elsif is_digit(first_char) then
|
||||
lexer_number(@source_code, @current_token.value.int_value);
|
||||
|
||||
if source_code_expect(@source_code, 'u') then
|
||||
current_token.kind := TokenKind.word;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.integer
|
||||
end
|
||||
elsif first_char = '(' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token.kind := TokenKind.left_paren
|
||||
elsif source_code_head(source_code) = '*' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lexer_comment(@source_code, token_buffer) then
|
||||
current_token.value.string := string_dup(string_buffer_clear(token_buffer));
|
||||
current_token.kind := TokenKind.comment
|
||||
else
|
||||
current_token.kind := TokenKind.unknown
|
||||
end
|
||||
else
|
||||
current_token.kind := TokenKind.left_paren
|
||||
end
|
||||
elsif first_char = ')' then
|
||||
current_token.kind := TokenKind.right_paren;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '\'' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lexer_character(@source_code, @current_token.value.char_value) & source_code_expect(@source_code, '\'') then
|
||||
current_token.kind := TokenKind.character;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.unknown
|
||||
end
|
||||
elsif first_char = '"' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lexer_string(@source_code, token_buffer) then
|
||||
current_token.kind := TokenKind.string;
|
||||
current_token.value.string := string_dup(string_buffer_clear(token_buffer))
|
||||
else
|
||||
current_token.kind := TokenKind.unknown
|
||||
end
|
||||
elsif first_char = '[' then
|
||||
current_token.kind := TokenKind.left_square;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ']' then
|
||||
current_token.kind := TokenKind.right_square;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '>' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token.kind := TokenKind.greater_than
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token.kind := TokenKind.greater_equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token.kind := TokenKind.shift_right;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.greater_than
|
||||
end
|
||||
elsif first_char = '<' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token.kind := TokenKind.less_than
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token.kind := TokenKind.less_equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '<' then
|
||||
current_token.kind := TokenKind.shift_left;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token.kind := TokenKind.not_equal;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.less_than
|
||||
end
|
||||
elsif first_char = '=' then
|
||||
current_token.kind := TokenKind.equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ';' then
|
||||
current_token.kind := TokenKind.semicolon;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '.' then
|
||||
current_token.kind := TokenKind.dot;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ',' then
|
||||
current_token.kind := TokenKind.comma;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '+' then
|
||||
current_token.kind := TokenKind.plus;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '-' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token.kind := TokenKind.minus
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token.kind := TokenKind.arrow;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.minus
|
||||
end
|
||||
elsif first_char = '*' then
|
||||
current_token.kind := TokenKind.multiplication;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '/' then
|
||||
current_token.kind := TokenKind.division;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '%' then
|
||||
current_token.kind := TokenKind.remainder;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ':' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token.kind := TokenKind.colon
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token.kind := TokenKind.assignment;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.colon
|
||||
end
|
||||
elsif first_char = '^' then
|
||||
current_token.kind := TokenKind.hat;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '@' then
|
||||
current_token.kind := TokenKind.at;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '!' then
|
||||
current_token.kind := TokenKind.exclamation;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '&' then
|
||||
current_token.kind := TokenKind.and;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '~' then
|
||||
current_token.kind := TokenKind.not;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '|' then
|
||||
current_token.kind := TokenKind.pipe;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token.kind := TokenKind.unknown;
|
||||
source_code_advance(@source_code)
|
||||
end;
|
||||
|
||||
return current_token
|
||||
end
|
||||
|
||||
(* Split the source text into tokens. *)
|
||||
proc lexer_text(source_code: SourceCode) -> Lexer;
|
||||
var
|
||||
current_token: Token
|
||||
token_buffer: StringBuffer
|
||||
lexer: Lexer
|
||||
begin
|
||||
lexer := Lexer(0u, nil);
|
||||
token_buffer := string_buffer_new();
|
||||
|
||||
lexer_spaces(@source_code);
|
||||
|
||||
while ~source_code_empty(@source_code) do
|
||||
current_token := lexer_next(source_code, @token_buffer);
|
||||
|
||||
if current_token.kind <> TokenKind.unknown then
|
||||
lexer_add_token(@lexer, current_token);
|
||||
lexer_spaces(@source_code)
|
||||
else
|
||||
write_s("Lexical analysis error on \"");
|
||||
write_c(source_code_head(source_code));
|
||||
write_s("\".\n")
|
||||
end
|
||||
end;
|
||||
|
||||
return lexer
|
||||
end
|
||||
|
||||
(*
|
||||
Command line handling.
|
||||
*)
|
||||
|
||||
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine;
|
||||
var
|
||||
parameter: ^^Char
|
||||
i: Int
|
||||
result: ^CommandLine
|
||||
begin
|
||||
i := 1;
|
||||
result := cast(malloc(#size(CommandLine)): ^CommandLine);
|
||||
result^.lex := false;
|
||||
result^.parse := false;
|
||||
result^.input := nil;
|
||||
|
||||
while i < argc & result <> nil do
|
||||
parameter := argv + i;
|
||||
|
||||
if strcmp(parameter^, "--lex\0".ptr) = 0 then
|
||||
result^.lex := true
|
||||
elsif strcmp(parameter^, "--parse\0".ptr) = 0 then
|
||||
result^.parse := true
|
||||
elsif parameter^^ <> '-' then
|
||||
if result^.input <> nil then
|
||||
write_s("Fatal error: Only one source file can be given.\n");
|
||||
result := nil
|
||||
else
|
||||
result^.input := parameter^
|
||||
end
|
||||
else
|
||||
write_s("Fatal error: Unknown command line options: ");
|
||||
|
||||
write_z(parameter^);
|
||||
write_s(".\n");
|
||||
|
||||
result := nil
|
||||
end;
|
||||
|
||||
i := i + 1
|
||||
end;
|
||||
if result <> nil & result^.input = nil then
|
||||
write_s("Fatal error: no input files.\n");
|
||||
result := nil
|
||||
end;
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
(*
|
||||
Parser.
|
||||
*)
|
||||
|
||||
proc parse(tokens: ^Token, tokens_size: Word);
|
||||
var
|
||||
current_token: ^Token
|
||||
i: Word
|
||||
@ -673,319 +1017,27 @@ begin
|
||||
write_c('\n')
|
||||
end
|
||||
|
||||
proc categorize_identifier(token_content: String) -> Token;
|
||||
var
|
||||
current_token: Token
|
||||
begin
|
||||
if token_content = "if" then
|
||||
current_token.kind := TokenKind._if
|
||||
elsif token_content = "then" then
|
||||
current_token.kind := TokenKind._then
|
||||
elsif token_content = "else" then
|
||||
current_token.kind := TokenKind._else
|
||||
elsif token_content = "elsif" then
|
||||
current_token.kind := TokenKind._elsif
|
||||
elsif token_content = "while" then
|
||||
current_token.kind := TokenKind._while
|
||||
elsif token_content = "do" then
|
||||
current_token.kind := TokenKind._do
|
||||
elsif token_content = "proc" then
|
||||
current_token.kind := TokenKind._proc
|
||||
elsif token_content = "begin" then
|
||||
current_token.kind := TokenKind._begin
|
||||
elsif token_content = "end" then
|
||||
current_token.kind := TokenKind._end
|
||||
elsif token_content = "extern" then
|
||||
current_token.kind := TokenKind._extern
|
||||
elsif token_content = "const" then
|
||||
current_token.kind := TokenKind._const
|
||||
elsif token_content = "var" then
|
||||
current_token.kind := TokenKind._var
|
||||
elsif token_content = "case" then
|
||||
current_token.kind := TokenKind._case
|
||||
elsif token_content = "of" then
|
||||
current_token.kind := TokenKind._of
|
||||
elsif token_content = "type" then
|
||||
current_token.kind := TokenKind._type
|
||||
elsif token_content = "record" then
|
||||
current_token.kind := TokenKind._record
|
||||
elsif token_content = "union" then
|
||||
current_token.kind := TokenKind._union
|
||||
elsif token_content = "true" then
|
||||
current_token.kind := TokenKind.boolean;
|
||||
current_token.value.boolean_value := true
|
||||
elsif token_content = "false" then
|
||||
current_token.kind := TokenKind.boolean;
|
||||
current_token.value.boolean_value := false
|
||||
elsif token_content = "nil" then
|
||||
current_token.kind := TokenKind.null
|
||||
elsif token_content = "or" then
|
||||
current_token.kind := TokenKind._or
|
||||
elsif token_content = "return" then
|
||||
current_token.kind := TokenKind._return
|
||||
elsif token_content = "cast" then
|
||||
current_token.kind := TokenKind._cast
|
||||
elsif token_content = "defer" then
|
||||
current_token.kind := TokenKind._defer
|
||||
elsif token_content = "program" then
|
||||
current_token.kind := TokenKind._program
|
||||
elsif token_content = "module" then
|
||||
current_token.kind := TokenKind._module
|
||||
elsif token_content = "import" then
|
||||
current_token.kind := TokenKind._import
|
||||
else
|
||||
current_token.kind := TokenKind.identifier;
|
||||
current_token.value.string := string_dup(token_content)
|
||||
end;
|
||||
|
||||
return current_token
|
||||
end
|
||||
|
||||
proc tokenize(source_code: SourceCode, tokens_size: ^Word) -> ^Token;
|
||||
var
|
||||
tokens, current_token: ^Token
|
||||
first_char: Char
|
||||
token_buffer: StringBuffer
|
||||
begin
|
||||
tokens_size^ := 0u;
|
||||
tokens := nil;
|
||||
token_buffer := string_buffer_new();
|
||||
|
||||
skip_spaces(@source_code);
|
||||
|
||||
while ~source_code_empty(@source_code) do
|
||||
tokens := cast(reallocarray(cast(tokens: ^Byte), tokens_size^ + 1u, #size(Token)): ^Token);
|
||||
current_token := tokens + tokens_size^;
|
||||
first_char := source_code_head(source_code);
|
||||
|
||||
if is_alpha(first_char) or first_char = '_' then
|
||||
lex_identifier(@source_code, @token_buffer);
|
||||
current_token^ := categorize_identifier(string_buffer_clear(@token_buffer))
|
||||
elsif first_char = '#' then
|
||||
source_code_advance(@source_code);
|
||||
lex_identifier(@source_code, @token_buffer);
|
||||
|
||||
current_token^.kind := TokenKind.trait;
|
||||
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer))
|
||||
elsif is_digit(first_char) then
|
||||
lex_number(@source_code, @current_token^.value.int_value);
|
||||
|
||||
if source_code_expect(@source_code, 'u') then
|
||||
current_token^.kind := TokenKind.word;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.integer
|
||||
end
|
||||
elsif first_char = '(' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token^.kind := TokenKind.left_paren
|
||||
elsif source_code_head(source_code) = '*' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lex_comment(@source_code, @token_buffer) then
|
||||
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer));
|
||||
current_token^.kind := TokenKind.comment
|
||||
else
|
||||
current_token^.kind := TokenKind.unknown
|
||||
end
|
||||
else
|
||||
current_token^.kind := TokenKind.left_paren
|
||||
end
|
||||
elsif first_char = ')' then
|
||||
current_token^.kind := TokenKind.right_paren;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '\'' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lex_character(@source_code, @current_token^.value.char_value) & source_code_expect(@source_code, '\'') then
|
||||
current_token^.kind := TokenKind.character;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.unknown
|
||||
end
|
||||
elsif first_char = '"' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if lex_string(@source_code, @token_buffer) then
|
||||
current_token^.kind := TokenKind.string;
|
||||
current_token^.value.string := string_dup(string_buffer_clear(@token_buffer))
|
||||
else
|
||||
current_token^.kind := TokenKind.unknown
|
||||
end
|
||||
elsif first_char = '[' then
|
||||
current_token^.kind := TokenKind.left_square;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ']' then
|
||||
current_token^.kind := TokenKind.right_square;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '>' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token^.kind := TokenKind.greater_than
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token^.kind := TokenKind.greater_equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token^.kind := TokenKind.shift_right;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.greater_than
|
||||
end
|
||||
elsif first_char = '<' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token^.kind := TokenKind.less_than
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token^.kind := TokenKind.less_equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '<' then
|
||||
current_token^.kind := TokenKind.shift_left;
|
||||
source_code_advance(@source_code)
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token^.kind := TokenKind.not_equal;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.less_than
|
||||
end
|
||||
elsif first_char = '=' then
|
||||
current_token^.kind := TokenKind.equal;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ';' then
|
||||
current_token^.kind := TokenKind.semicolon;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '.' then
|
||||
current_token^.kind := TokenKind.dot;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ',' then
|
||||
current_token^.kind := TokenKind.comma;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '+' then
|
||||
current_token^.kind := TokenKind.plus;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '-' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token^.kind := TokenKind.minus
|
||||
elsif source_code_head(source_code) = '>' then
|
||||
current_token^.kind := TokenKind.arrow;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.minus
|
||||
end
|
||||
elsif first_char = '*' then
|
||||
current_token^.kind := TokenKind.multiplication;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '/' then
|
||||
current_token^.kind := TokenKind.division;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '%' then
|
||||
current_token^.kind := TokenKind.remainder;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = ':' then
|
||||
source_code_advance(@source_code);
|
||||
|
||||
if source_code_empty(@source_code) then
|
||||
current_token^.kind := TokenKind.colon
|
||||
elsif source_code_head(source_code) = '=' then
|
||||
current_token^.kind := TokenKind.assignment;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.colon
|
||||
end
|
||||
elsif first_char = '^' then
|
||||
current_token^.kind := TokenKind.hat;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '@' then
|
||||
current_token^.kind := TokenKind.at;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '!' then
|
||||
current_token^.kind := TokenKind.exclamation;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '&' then
|
||||
current_token^.kind := TokenKind.and;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '~' then
|
||||
current_token^.kind := TokenKind.not;
|
||||
source_code_advance(@source_code)
|
||||
elsif first_char = '|' then
|
||||
current_token^.kind := TokenKind.pipe;
|
||||
source_code_advance(@source_code)
|
||||
else
|
||||
current_token^.kind := TokenKind.unknown;
|
||||
source_code_advance(@source_code)
|
||||
end;
|
||||
|
||||
if current_token^.kind <> TokenKind.unknown then
|
||||
tokens_size^ := tokens_size^ + 1u;
|
||||
skip_spaces(@source_code)
|
||||
else
|
||||
write_s("Lexical analysis error on \"");
|
||||
write_c(first_char);
|
||||
write_s("\".\n")
|
||||
end
|
||||
end;
|
||||
|
||||
return tokens
|
||||
end
|
||||
|
||||
(*
|
||||
Command line handling.
|
||||
*)
|
||||
|
||||
proc parse_command_line*(argc: Int, argv: ^^Char) -> ^CommandLine;
|
||||
var
|
||||
parameter: ^^Char
|
||||
i: Int
|
||||
result: ^CommandLine
|
||||
begin
|
||||
i := 1;
|
||||
result := cast(malloc(#size(CommandLine)): ^CommandLine);
|
||||
result^.lex := false;
|
||||
result^.parse := false;
|
||||
result^.input := nil;
|
||||
|
||||
while i < argc & result <> nil do
|
||||
parameter := argv + i;
|
||||
|
||||
if strcmp(parameter^, "--lex\0".ptr) = 0 then
|
||||
result^.lex := true
|
||||
elsif strcmp(parameter^, "--parse\0".ptr) = 0 then
|
||||
result^.parse := true
|
||||
elsif parameter^^ <> '-' then
|
||||
if result^.input <> nil then
|
||||
write_s("Fatal error: Only one source file can be given.\n");
|
||||
result := nil
|
||||
else
|
||||
result^.input := parameter^
|
||||
end
|
||||
else
|
||||
write_s("Fatal error: Unknown command line options: ");
|
||||
|
||||
write_z(parameter^);
|
||||
write_s(".\n");
|
||||
|
||||
result := nil
|
||||
end;
|
||||
|
||||
i := i + 1
|
||||
end;
|
||||
if result <> nil & result^.input = nil then
|
||||
write_s("Fatal error: no input files.\n");
|
||||
result := nil
|
||||
end;
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
(*
|
||||
Compilation entry.
|
||||
*)
|
||||
|
||||
proc compile_in_stages(command_line: ^CommandLine, source_code: SourceCode) -> Int;
|
||||
var
|
||||
return_code: Int
|
||||
lexer: Lexer
|
||||
begin
|
||||
return_code := 0;
|
||||
|
||||
if command_line^.lex or command_line^.parse then
|
||||
lexer := lexer_text(source_code)
|
||||
end;
|
||||
if command_line^.parse then
|
||||
parse(lexer.data, lexer.length)
|
||||
end;
|
||||
|
||||
return return_code
|
||||
end
|
||||
|
||||
proc process(argc: Int, argv: ^^Char) -> Int;
|
||||
var
|
||||
tokens: ^Token
|
||||
@ -993,6 +1045,7 @@ var
|
||||
source_code: SourceCode
|
||||
command_line: ^CommandLine
|
||||
return_code: Int
|
||||
source_file: ^SourceFile
|
||||
begin
|
||||
return_code := 0;
|
||||
|
||||
@ -1002,26 +1055,26 @@ begin
|
||||
end;
|
||||
|
||||
if return_code = 0 then
|
||||
source_code.position := Position(1u, 1u);
|
||||
source_file := read_source(command_line^.input);
|
||||
|
||||
source_code.input := cast(read_source(command_line^.input): ^Byte);
|
||||
if source_file = nil then
|
||||
perror(command_line^.input);
|
||||
return_code := 3
|
||||
end
|
||||
end;
|
||||
|
||||
if return_code = 0 then
|
||||
defer
|
||||
fclose(source_file^.handle)
|
||||
end;
|
||||
|
||||
source_code.position := Position(1u, 1u);
|
||||
source_code.input := cast(source_file: ^Byte);
|
||||
source_code.empty := source_file_empty;
|
||||
source_code.head := source_file_head;
|
||||
source_code.advance := source_file_advance;
|
||||
|
||||
if source_code.input = nil then
|
||||
perror(command_line^.input);
|
||||
return_code := 3
|
||||
end
|
||||
end;
|
||||
if return_code = 0 then
|
||||
tokens := tokenize(source_code, @tokens_size);
|
||||
|
||||
fclose(cast(source_code.input: ^SourceFile)^.handle);
|
||||
|
||||
if command_line^.lex then
|
||||
print_tokens(tokens, tokens_size)
|
||||
end
|
||||
return_code := compile_in_stages(command_line, source_code)
|
||||
end;
|
||||
return return_code
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user