195 lines
6.6 KiB
Plaintext
195 lines
6.6 KiB
Plaintext
import "@std/mem"
|
|
import "@std/arraylist"
|
|
import "@std/enummap"
|
|
import "@std/io"
|
|
import "@std/process"
|
|
|
|
import "@source/diagnostics"
|
|
import "@source/strpool"
|
|
import "@source/lexer"
|
|
import "@source/ast"
|
|
import "@source/parser"
|
|
import "@source/sema"
|
|
import "@source/ir"
|
|
import "@source/lower"
|
|
import "@source/interpreter"
|
|
typecheck :: import "@source/sema/check"
|
|
|
|
test import "@std/mem"
|
|
test import "@std/enummap"
|
|
test import "@std/arraylist"
|
|
test import "@std/hashmap"
|
|
test import "@std/strmap"
|
|
test import "@std/meta"
|
|
test import "@std/io"
|
|
test import "@std/process"
|
|
test import "@source/diagnostics"
|
|
test import "@source/strpool"
|
|
test import "@source/lexer"
|
|
test import "@source/ast"
|
|
test import "@source/parser"
|
|
test import "@source/sema"
|
|
test import "@source/sema/check"
|
|
test import "@source/lower"
|
|
test import "@source/interpreter"
|
|
|
|
@hide:file
|
|
DriverError :: alias (
|
|
mem.AllocError
|
|
| diagnostics.CapacityError
|
|
| lexer.SourceError
|
|
| strpool.InternError
|
|
| io.OpenError
|
|
| io.CloseError
|
|
| io.ReadError
|
|
| io.WriteError
|
|
)
|
|
|
|
main proc(init process.Init) i32 {
|
|
return command(mem.c_allocator, init.io, init.args) catch |err| {
|
|
if (init.args.values.len > 2) {
|
|
io.print("{s}: infrastructure error: {s}\n", io.stderr(init.io), {
|
|
init.args.values[2], infrastructure_message(err),
|
|
}) catch |_| {}
|
|
} else {
|
|
io.print("honey: infrastructure error: {s}\n", io.stderr(init.io), {
|
|
infrastructure_message(err),
|
|
}) catch |_| {}
|
|
}
|
|
return 2
|
|
}
|
|
}
|
|
|
|
@hide:file
|
|
infrastructure_message proc(error DriverError) []u8 {
|
|
return match error {
|
|
.out_of_memory: "out of memory"
|
|
.diagnostic_limit_reached: "diagnostic limit reached"
|
|
.source_too_large: "source file is too large"
|
|
.out_of_space: "identifier storage is full"
|
|
.open_failed: "cannot open source file"
|
|
.close_failed: "cannot close source file"
|
|
.not_open_for_reading: "source file is not readable"
|
|
.read_failed: "cannot read source file"
|
|
.not_open_for_writing: "output is not writable"
|
|
.write_failed: "cannot write output"
|
|
.no_progress: "output writer made no progress"
|
|
}
|
|
}
|
|
|
|
@hide:file
|
|
command proc(allocator mem.Allocator, runtime io.Io, args process.Args) i32 ! DriverError {
|
|
if (args.values.len != 3) {
|
|
try io.print("usage: honey run <file>\n", io.stderr(runtime), {})
|
|
return 2
|
|
}
|
|
if (!mem.eql(args.values[1], "run")) {
|
|
try io.print("usage: honey run <file>\n", io.stderr(runtime), {})
|
|
return 2
|
|
}
|
|
return try run_file(allocator, runtime, args.values[2])
|
|
}
|
|
|
|
@hide:file
|
|
read_source proc(allocator mem.Allocator, runtime io.Io, path [;0]u8) arraylist.ArrayList(u8) ! DriverError {
|
|
file :: try io.open(runtime, path, .read_only)
|
|
closing := false
|
|
defer {
|
|
if (!closing) io.close(file) catch |_| {}
|
|
}
|
|
bytes := io.read_all(allocator, io.reader(file), usize(maxval!(u32))) catch |err| {
|
|
match err {
|
|
.limit_exceeded: return .source_too_large
|
|
else: return err
|
|
}
|
|
}
|
|
errdefer arraylist.deinit(&bytes)
|
|
closing = true
|
|
try io.close(file)
|
|
return bytes
|
|
}
|
|
|
|
@hide:file
|
|
compile proc(allocator mem.Allocator, source []u8, diagnostic_store @mut diagnostics.Store) ir.Module ! DriverError {
|
|
strpool.strings = strpool.init(allocator)
|
|
defer strpool.deinit(&strpool.strings)
|
|
scan_state := lexer.init(allocator)
|
|
defer lexer.deinit(&scan_state)
|
|
try lexer.scan(&scan_state, source, diagnostic_store)
|
|
tree := try parser.parse(allocator, scan_state.tokens.items, source, diagnostic_store)
|
|
defer ast.deinit(&tree)
|
|
resolution := try sema.resolve(allocator, &tree, diagnostic_store)
|
|
defer sema.deinit(&resolution)
|
|
checked := try typecheck.check(allocator, &tree, &resolution, diagnostic_store)
|
|
defer typecheck.deinit(&checked)
|
|
return try lower.lower(allocator, &tree, &resolution, &checked, diagnostic_store)
|
|
}
|
|
|
|
@hide:file
|
|
run_file proc(allocator mem.Allocator, runtime io.Io, path [;0]u8) i32 ! DriverError {
|
|
source := try read_source(allocator, runtime, path)
|
|
defer arraylist.deinit(&source)
|
|
diagnostic_store := diagnostics.init(allocator)
|
|
defer diagnostics.deinit(&diagnostic_store)
|
|
module := try compile(allocator, source.items, &diagnostic_store)
|
|
defer ir.deinit(&module)
|
|
outcome :: try interpreter.run(allocator, &module, &diagnostic_store)
|
|
if (outcome.trap == null) try print_result(io.stdout(runtime), &module, outcome.value)
|
|
try report_diagnostics(allocator, io.stderr(runtime), path, source.items, &diagnostic_store, outcome.trap)
|
|
if (outcome.trap != null or diagnostics.count(&diagnostic_store) != 0) return 1
|
|
return 0
|
|
}
|
|
|
|
@hide:file
|
|
print_result proc(output io.Writer, module @ir.Module, value ir.Value) void ! io.WriteError {
|
|
match value.type {
|
|
.int: try io.print("{}\n", output, {value.data.integer})
|
|
.float: try io.print("{d}\n", output, {value.data.real})
|
|
.bool: try io.print("{}\n", output, {value.data.boolean})
|
|
.string: try io.print("{s}\n", output, {ir.string_bytes(module, value.data.string)})
|
|
.void: {}
|
|
else: unreachable
|
|
}
|
|
}
|
|
|
|
@hide:file
|
|
report_diagnostics proc(
|
|
allocator mem.Allocator,
|
|
output io.Writer,
|
|
path []u8,
|
|
source []u8,
|
|
diagnostic_store @diagnostics.Store,
|
|
trap ?diagnostics.DiagnosticId,
|
|
) void ! (mem.AllocError | io.WriteError) {
|
|
if (diagnostics.count(diagnostic_store) == 0) return
|
|
starts := arraylist.init(usize, allocator)
|
|
defer arraylist.deinit(&starts)
|
|
try arraylist.append(&starts, usize(0))
|
|
for (source) |byte, index| {
|
|
if (byte == '\n') try arraylist.append(&starts, index + 1)
|
|
}
|
|
for (0..diagnostics.count(diagnostic_store)) |index| {
|
|
if (diagnostics.get(diagnostic_store, diagnostics.DiagnosticId(index))) |diagnostic| {
|
|
offset :: usize(diagnostic.span.start)
|
|
line := usize(0)
|
|
end := starts.items.len
|
|
while (line + 1 < end) {
|
|
middle :: line + divtrunc!(end - line, 2)
|
|
if (starts.items[middle] <= offset) line = middle
|
|
else end = middle
|
|
}
|
|
label []u8 := "error"
|
|
if (trap) |id| {
|
|
if (id == diagnostics.DiagnosticId(index)) label = "runtime trap"
|
|
}
|
|
try io.print("{s}:{}:{}: {s}: {s}\n", output, {
|
|
path,
|
|
line + 1,
|
|
offset - starts.items[line] + 1,
|
|
label,
|
|
enummap.get(diagnostics.Code, []u8, &diagnostics.messages, diagnostic.code)?,
|
|
})
|
|
}
|
|
}
|
|
}
|