Skip to content

Commit

Permalink
little better error handling
Browse files Browse the repository at this point in the history
  • Loading branch information
mustafaquraish committed Nov 25, 2024
1 parent ac243ee commit 4ff1ed9
Show file tree
Hide file tree
Showing 11 changed files with 1,849 additions and 22,718 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ out
*.png
*.qoi

*.trace

tmp/**

docs/build/
Expand Down
24,258 changes: 1,545 additions & 22,713 deletions bootstrap/stage0.c

Large diffs are not rendered by default.

47 changes: 47 additions & 0 deletions compiler/ast/nodes.oc
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,53 @@ struct AST {
returns: bool
}

// enum AST2 {
// span: Span
// etype: &Type = null
// hint: &Type = null
// resolved_symbol: &Symbol = null
// returns: bool = false

// Assert(assertion: Assertion)
// Block(block: Block)
// BoolLiteral(value: bool)
// Break
// Call(call: FuncCall)
// Continue
// Error
// Identifier(ident: Identifier)
// If(if_stmt: IfStatement)
// Import(import_path: Import)
// IntLiteral(value: NumLiteral)
// Member(member: Member)
// NSLookup(lookup: NSLookup)
// OverloadedOperator(symbol: &Symbol)
// Return(expr: &AST2)
// Yield(expr: &AST2)
// StringLiteral(value: str)
// SizeOf(type: &Type)
// VarDeclaration(var: &Variable)
// While(loop: Loop)
// For(loop: Loop)
// CharLiteral(value: str)
// FloatLiteral(value: NumLiteral)
// FormatStringLiteral(fmt_str: FormatString)
// Cast(cast: Cast)
// Null
// Match(match_stmt: Match)
// Defer(stmt: &AST2)
// Specialization(spec: Specialization)
// ArrayLiteral(arr: ArrayLiteral)
// CreateClosure(func: &Function)

// // Used for heap-allocating the result of an expression
// // @new XYZ => { let a = mem::alloc<typeof(XYZ)>(); *a = XYZ; yield a }
// CreateNew(expr: &AST2)

// UnaryOp(unop: Unary)
// BinaryOp(binop: Binary)
// }

def AST::new(type: ASTType, span: Span): &AST {
let ast = mem::alloc<AST>()
ast.type = type
Expand Down
53 changes: 53 additions & 0 deletions compiler/lsp/mod.oc
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import std::value::Value
import std::{ panic, exit }
import std::fs
import std::json
import std::signal::{ set_signal_handler, Signal }
import std::setjmp::{ ErrorContext }

import @lexer::Lexer
import @parser::Parser
Expand All @@ -18,6 +20,7 @@ import @ast::nodes::*
import @ast::program::*
import @ast::scopes::*
import @types::*
import @passes::visitor::Visitor

import .finder::{ Finder }
import .utils::{ this, verbose }
Expand Down Expand Up @@ -58,6 +61,39 @@ def handle_validate(program: &Program, path: str) {
let err_value = utils::gen_error_json(err)
println(`{err_value.dbg()}`)
}


return // Look below

/// TODO: The following code "works", but current way of handling this in server
// clears all hints when document is changed and validate is called, which
// causes the hints to appear glitchy when user is typing. Need some way to
// keep the old hints around till we have a next successful validate call?
// For now, just disable this code and only show errors
let visitor = Visitor(
node_fn: |node| => match node.type {
VarDeclaration => {
let msg = utils::gen_inlay_hint(node.u.var_decl, path)
if msg? then println(`{msg.dbg()}`)
}
CreateClosure => {
for param in node.u.closure.params.iter() {
let msg = utils::gen_inlay_hint(param, path)
if msg? then println(`{msg.dbg()}`)
}
}
else => {}
}
)
for ns in program.iter_namespaces() {
if ns.path.eq(path) {
for func in ns.functions.iter() {
if func.body? {
visitor.visit_po(func.body)
}
}
}
}
}


Expand Down Expand Up @@ -163,6 +199,13 @@ def lsp_usage(code: i32, full: bool) {
exit(code)
}

let global_err_ctx: ErrorContext

def signal_handler(sig: i32) {
if verbose then println(`Received signal {sig as Signal}, exiting`)
global_err_ctx.jump_back(1)
}

def lsp_main(argc: i32, argv: &str) {
shift_args(&argc, &argv) // skip the first argument

Expand Down Expand Up @@ -220,8 +263,18 @@ def lsp_main(argc: i32, argv: &str) {
show_path = fs::realpath(show_path)
}

set_signal_handler(SIGSEGV, signal_handler)
set_signal_handler(SIGILL, signal_handler)
set_signal_handler(SIGFPE, signal_handler)

// Load the program
let program = Program::new()

if global_err_ctx.set_jump_point() > 0 {
// Do nothing here, since LSP is sensitive to unnecessary output
exit(1)
}

program.setup_library_paths()
// Always try to load stdlib for LSP
program.include_stdlib = true
Expand Down
21 changes: 20 additions & 1 deletion compiler/lsp/utils.oc
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ def gen_hover_string(sym: &Symbol): str => match sym.type {
let field = sym.u.enum_field
yield `enum {field.variant.sym.display}.{field.idx}`
}
Closure => `closure {sym.display}`
Closure => gen_type_string(sym.u.func.type)
ClosedVariable => {
let var = sym.u.closed_var.orig
let buf = Buffer::make()
Expand Down Expand Up @@ -716,4 +716,23 @@ def gen_completions_json(finder: &Finder): &Value {
let obj = Value::new(Dictionary)
obj["completions"] = completions
return obj
}

def gen_inlay_hint(var: &Variable, path: str): &Value {
if var.parsed_type? return null

let msg = Value::new(Dictionary)
// FIXME: both here and in ocen-vscode, change this to use a `kind` field and only
// use `severity` for diagnostics.
msg["severity"] = "InlayType"
if var.type? {
let hint_type = gen_type_string(var.type)
msg["type"] = hint_type
} else {
msg["type"] = "???"
}

let loc = Location(path,0,0,0)
msg["span"] = gen_span_json_with_filename(var.sym.span, loc)
return msg
}
20 changes: 20 additions & 0 deletions compiler/main.oc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import std::vector::{ Vector }
import std::buffer::{ Buffer }
import std::{ shift_args }
import std::logging::{ init_logging, log, LogLevel }
import std::signal::{ set_signal_handler, Signal }
import std::setjmp::{ ErrorContext }

import .ast::program::{ Program }
import .parser::{ Parser }
Expand Down Expand Up @@ -153,14 +155,32 @@ def parse_args(argc: &i32, argv: &&str, program: &Program) {
}
}

let global_err_ctx: &ErrorContext

def signal_handler(sig: i32) {
log(Error, f"Received signal {sig as Signal}, exiting compilation")
global_err_ctx.jump_back(1)
}

def main(argc: i32, argv: &str) {
shift_args(&argc, &argv)
if argc > 1 and argv[0] == "lsp" {
lsp_main(argc, argv)
std::exit(0)
}

set_signal_handler(SIGSEGV, signal_handler)
set_signal_handler(SIGILL, signal_handler)
set_signal_handler(SIGFPE, signal_handler)

let program = Program::new()
// Context for parsing, typechecking, codegen.
global_err_ctx = program.add_error_context()
if global_err_ctx.set_jump_point() > 0 {
program.exit_with_errors_if_any()
std::exit(1)
}

program.setup_library_paths()

parse_args(&argc, &argv, program)
Expand Down
8 changes: 8 additions & 0 deletions compiler/passes/code_generator.oc
Original file line number Diff line number Diff line change
Expand Up @@ -1511,6 +1511,9 @@ def CodeGenerator::gen_function_decl_toplevel(&this, func: &Function) {

.gen_function_decl(func)
if func.exits then .out += " __attribute__((noreturn))"
// .out += " asm(\""
// .out += func.sym.display
// .out += "\")"
.out += ";\n"
}
return
Expand All @@ -1519,6 +1522,11 @@ def CodeGenerator::gen_function_decl_toplevel(&this, func: &Function) {
if func.sym.is_dead then return
.gen_function_decl(func)
if func.exits then .out += " __attribute__((noreturn))"
// if not func.sym.out_name().eq("main") {
// .out += " asm(\""
// .out += func.sym.display
// .out += "\")"
// }
.out += ";\n"
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/passes/typechecker.oc
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ def TypeChecker::resolve_templated_symbol(&this, sym: &Symbol, node: &AST): &Sym

// Want to also build a new display name for the specialization
let new_display_name = Buffer::make()
new_display_name += sym.name
new_display_name += sym.display
new_display_name += "<"

// This is the scope that we'll use to resolve the template parameters
Expand Down
116 changes: 116 additions & 0 deletions compiler/passes/visitor.oc
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import @ast::nodes::{ AST, Variable }
import @types::{ Type }

struct Visitor {
node_fn: @fn(&AST) = |node| {}
type_fn: @fn(&Type) = |type| {}
}



def Visitor::visit_var(&this, var: &Variable) {
if not var? or not var.type? return

// FIXME: This is eh, should we traverse through type-tree as well?
.type_fn(var.type)

.visit_po(var.default_value)
}

// Visit the nodes in post-order
def Visitor::visit_po(&this, node: &AST) {
if not node? return
match node.type {
BoolLiteral | IntLiteral | Break | Continue | Error | Identifier | Null
| Import | OverloadedOperator | StringLiteral | SizeOf
| CharLiteral | FloatLiteral => {}
Assert => {
.visit_po(node.u.assertion.expr)
.visit_po(node.u.assertion.msg)
}
Block => {
for stmt in node.u.block.statements.iter() {
.visit_po(stmt)
}
}
Call => {
.visit_po(node.u.call.callee)
for arg in node.u.call.args.iter() {
.visit_po(arg.expr)
}
}
If => {
for branch in node.u.if_stmt.branches.iter() {
.visit_po(branch.cond)
.visit_po(branch.body)
}
.visit_po(node.u.if_stmt.els)
}
Member => {
.visit_po(node.u.member.lhs)
}
NSLookup => {
.visit_po(node.u.lookup.lhs)
}
Return => {
.visit_po(node.u.ret.expr)
}
Yield => {
.visit_po(node.u.child)
}
VarDeclaration => {
.visit_var(node.u.var_decl)
}
While | For => {
.visit_po(node.u.loop.cond)
.visit_po(node.u.loop.body)
.visit_po(node.u.loop.step)
}
FormatStringLiteral => {
for arg in node.u.fmt_str.exprs.iter() {
.visit_po(arg)
}
}
Cast => {
.visit_po(node.u.cast.lhs)
}
Match => {
let mtch = node.u.match_stmt
.visit_po(mtch.expr)
for _case in mtch.cases.iter() {
for cond in _case.conds.iter() {
.visit_po(cond.expr)
}
.visit_po(_case.body)
}
.visit_po(mtch.defolt)
}
Defer => {
.visit_po(node.u.child)
}
Specialization => {
.visit_po(node.u.spec.base)
}
ArrayLiteral => {
for elem in node.u.array_literal.elements.iter() {
.visit_po(elem)
}
}
CreateClosure => {
let func = node.u.closure
.visit_po(func.body)
}
CreateNew => {
.visit_po(node.u.child)
}
UnaryOp => {
.visit_po(node.u.unary.expr)
}
BinaryOp => {
.visit_po(node.u.binary.lhs)
.visit_po(node.u.binary.rhs)
}
}
.node_fn(node)
}

6 changes: 3 additions & 3 deletions meta/gen_bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ mkdir -p build
set -e

echo "[+] Testing 3-stage bootstrap for validity"
$initial -d compiler/main.oc -o build/stage1
./build/stage1 -d compiler/main.oc -o build/stage2
./build/stage2 -d compiler/main.oc -o build/stage3
$initial compiler/main.oc -o build/stage1
./build/stage1 compiler/main.oc -o build/stage2
./build/stage2 compiler/main.oc -o build/stage3
if diff build/stage2.c build/stage3.c; then
echo "[+] Verification successful!"
echo
Expand Down
Loading

0 comments on commit 4ff1ed9

Please sign in to comment.