-
Notifications
You must be signed in to change notification settings - Fork 8
/
main.oc
194 lines (167 loc) · 5.96 KB
/
main.oc
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
//* Entry point for the compiler
import std::fs
import std::libc::{ exit, system }
import std::vector::{ Vector }
import std::buffer::{ Buffer }
import std::{ shift_args }
import std::logging::{ init_logging, log, LogLevel }
import .ast::program::{ Program }
import .parser::{ Parser }
import .passes::{ run_typecheck_passes, run_codegen_passes }
import .docgen::{ generate_doc_json }
import .lsp::{ lsp_main }
def usage(code: i32, full: bool) {
println("Usage:")
println(" ./ocen [--help] [compile-options] <file>")
println(" ./ocen lsp [--help] [lsp-options] <file>")
if not full then exit(code)
println("--------------------------------------------------------")
println("Compile Options:")
println(" -o path Output executable (default: ./out)")
println(" -c path Output C code (default: {out}.c)")
println(" --no-stdlid Don't include the standard library")
println(" -e0 Minimal one-line errors")
println(" -e1 Error messages with source code (default)")
println(" -e2 Error messages with source / hints")
println(" -s Silent mode (no debug output)")
println(" -n Don't compile C code (default: false)")
println(" --no-dce Don't perform dead code elimination")
println(" -d Emit debug information (default: false)")
println(" -l path Directory to search for libraries (can be used multiple times)")
println(" --docs path Output documentation JSON (default: none)")
println(" --cflags flags Additional C flags (can be used multiple times)")
println(" -h Display this information")
println(" -r <args> Run executable with arguments (can only be at the end)")
exit(code)
}
let exec_path: str = "./out"
let c_path: str = null
let filename: str = null
let compile_c: bool = true
let silent: bool = false
let debug: bool = false
let error_level: u32 = 2
let docs_path: str = null
let include_stdlib: bool = true
let extra_c_flags: &Vector<str>
let run_after_compile: bool = false
def save_and_compile_code(program: &Program, code: str) {
if not c_path? {
c_path = `{exec_path}.c`
}
fs::write_file_str(c_path, code)
if not compile_c then return
let cmd = Buffer::make()
let c_compiler = std::libc::getenv("CC")
if not c_compiler? then c_compiler = "gcc"
cmd <<= `{c_compiler} -o {exec_path} {c_path}`
for flag : program.c_flags.iter() {
cmd += " "
cmd += flag
}
for flag : extra_c_flags.iter() {
cmd += " "
cmd += flag
}
if debug then cmd += " -ggdb3"
log(Info, f"{cmd}")
let exit_code = system(cmd.str())
if exit_code != 0 {
log(Error, "Failed to compile C code")
std::exit(1)
}
}
def run_executable(argc: i32, argv: &str) {
let cmd = Buffer::make()
cmd += exec_path
for let i = 0i32; i < argc; i++ {
cmd += " "
cmd += argv[i]
}
log(Info, f"{cmd}")
let ret = system(cmd.str())
let exit_code = (ret >> 8) & 0xFF // Effectively WEXITSTATUS(ret)
log(Info, f"Exited with code: {exit_code}")
std::exit(exit_code)
}
def parse_args(argc: &i32, argv: &&str, program: &Program) {
extra_c_flags = Vector<str>::new()
while *argc > 0 {
let arg = shift_args(argc, argv)
match arg {
"--help" => usage(code: 0, true)
"-s" => silent = true
"-d" => debug = true
"-n" => {
compile_c = false
}
"--no-dce" => program.keep_all_code = true
"-o" => exec_path = shift_args(argc, argv)
"-c" => c_path = shift_args(argc, argv)
"-l" => program.library_paths.push(shift_args(argc, argv))
"-e0" => error_level = 0
"-e1" => error_level = 1
"-e2" => error_level = 2
"--docs" => {
docs_path = shift_args(argc, argv)
program.check_doc_links = true
}
"--no-stdlib" => include_stdlib = false
"--cflags" | "-cf" => extra_c_flags.push(shift_args(argc, argv))
"-r" | "--run" => {
run_after_compile = true
// All remaining arguments are for the executable
break
}
else => {
if arg[0] == '-' {
println("Unknown option: %s", arg)
usage(1, true)
} else if not filename? {
filename = arg
} else {
println("Unknown option/argument: '%s'", arg)
usage(code: 1, true)
}
}
}
}
if not filename? {
println("No file specified")
usage(code: 1, false)
}
if run_after_compile and not compile_c {
println("Cannot run without compiling")
usage(code: 1, false)
}
}
def main(argc: i32, argv: &str) {
shift_args(&argc, &argv)
if argc > 1 and argv[0] == "lsp" {
lsp_main(argc, argv)
std::exit(0)
}
let program = Program::new()
program.setup_library_paths()
parse_args(&argc, &argv, program)
let level: LogLevel = match silent {
true => Error
false => Info
}
// Initialize logging without a timestamp, since we're not a long-running process
init_logging(level, time_format: null)
program.error_level = error_level
program.gen_debug_info = debug
program.include_stdlib = include_stdlib
Parser::parse_toplevel(program, filename, file_contents: null, include_workspace_main: true)
run_typecheck_passes(program)
program.exit_with_errors_if_any()
if docs_path? {
generate_doc_json(program, docs_path)
} else {
let code = run_codegen_passes(program)
program.exit_with_errors_if_any()
save_and_compile_code(program, code)
if run_after_compile then run_executable(argc, argv)
}
}