aboutsummaryrefslogtreecommitdiff
path: root/source/main.elna
blob: 3b78651ac3d02e398c968ca3ad2f6dc22c95168f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
(* 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(parameters)
import
  sys.posix.cstdio, sys.posix.cstdlib, sys.posix.cstring, common, command_line_interface,
  lexer, ast, diagnostic, symbol, parser, name_analysis, type_check, tac, qbe

proc read_file_to_buffer(filename: ^Word8) -> []const Word8
var
	file_handle: ^FILE
	file_size: Int
	buffer: ^Word8
	bytes_read: Word
	result: []const Word8
begin
	file_handle := fopen(filename, "rb\0".ptr);
	if file_handle = nil then
		perror(filename);
		exit(3)
	end;
	fseek(file_handle, 0, 2);
	file_size := ftell(file_handle);
	rewind(file_handle);

	buffer := malloc#[Word8](cast(file_size: Word) + 1u);
	bytes_read := fread#[Word8](buffer, 1u, cast(file_size: Word), file_handle);
	(buffer + bytes_read)^ := cast(0: Word8);
	fclose(file_handle);

	result := buffer[1u to bytes_read]
return result

proc print_errors(error_list: ^ElnaList)
var
	node: ^ElnaListNode
begin
	node := error_list^.first;
	while node <> nil do
		elna_error_print(cast(node: ^ElnaError));
		node := node^.next
	end
return

proc compile_file(command_line: ^CommandLine) -> Int
var
	source_text: []const Word8
	lexer_cursor: ElnaLexerCursor
	error_list: ElnaList
	parser_node: ^ElnaTreeModuleDeclaration
	tac_module: ^ElnaInstructionModule
	out_file: ^FILE := stdout
	token: ^ElnaLexerToken
	return_code: Int := 0
begin
	source_text := read_file_to_buffer(command_line^.input);

	if command_line^.lex then
		elna_lexer_initialize(@lexer_cursor, cast(source_text.ptr: ^Word8));
		token := elna_lexer_read(@lexer_cursor);
		while token^.kind <> ElnaLexerKind.eof do
			token := elna_lexer_read(@lexer_cursor)
		end
	else
		elna_list_initialize(@error_list);
		elna_symbol_table_build();

		elna_lexer_initialize(@lexer_cursor, cast(source_text.ptr: ^Word8));
		parser_node := elna_parser_module_declaration(@lexer_cursor, @error_list);

		if ~elna_list_empty(@error_list) then
			print_errors(@error_list);
			return_code := 1
		elsif ~command_line^.parse then
			elna_declaration_module_declaration(parser_node, @error_list);
			if ~elna_list_empty(@error_list) then
				print_errors(@error_list);
				return_code := 1
			else
				elna_name_module_declaration(parser_node, @error_list);
				if ~elna_list_empty(@error_list) then
					print_errors(@error_list);
					return_code := 1
				else
					elna_type_module_declaration(parser_node, @error_list);
					if ~elna_list_empty(@error_list) then
						print_errors(@error_list);
						return_code := 1
					else
						tac_module := elna_tac_module_declaration(parser_node);

						if command_line^.output <> nil then
							out_file := fopen(command_line^.output, "wb\0".ptr);
							if out_file = nil then
								perror(command_line^.output);
								return_code := 3
							end
						end;

						if return_code = 0 then
							elna_qbe_emit(tac_module, out_file);

							if command_line^.output <> nil & out_file <> stdout then
								fclose(out_file)
							end
						end
					end
				end
			end
		end
	end
return return_code

proc initialize_global_state()
begin
	stdin := fdopen(0, "r\0".ptr);
	stdout := fdopen(1, "w\0".ptr);
	stderr := fdopen(2, "w\0".ptr)
return

proc process(argc: Int; argv: ^^Word8) -> Int
var
	command_line: ^CommandLine
	return_code: Int := 0
begin
	command_line := parse_command_line(argc, argv);
	if command_line = nil then
		return_code := 2
	elsif command_line^.input = nil then
		write_s("Fatal error: no input file provided.\n");
		return_code := 2
	else
		return_code := compile_file(command_line)
	end
return return_code

begin
	initialize_global_state();

	exit(process(cast(parameters.length: Int), parameters.ptr))
end.