resolve.ha (4181B)
1 // SPDX-License-Identifier: GPL-3.0-only 2 // (c) Hare authors <https://harelang.org> 3 4 use bufio; 5 use fmt; 6 use fs; 7 use hare::ast; 8 use hare::lex; 9 use hare::module; 10 use hare::parse; 11 use io; 12 use os; 13 14 type symkind = enum { 15 LOCAL, 16 MODULE, 17 SYMBOL, 18 ENUM_LOCAL, 19 ENUM_REMOTE, 20 }; 21 22 // Resolves a reference. Given an identifier, determines if it refers to a local 23 // symbol, a module, or a symbol in a remote module, then returns this 24 // information combined with a corrected ident if necessary. 25 fn resolve(ctx: *context, what: ast::ident) ((ast::ident, symkind) | void | error) = { 26 if (is_local(ctx, what)) { 27 return (what, symkind::LOCAL); 28 }; 29 30 if (len(what) > 1) { 31 // Look for symbol in remote module 32 let partial = what[..len(what) - 1]; 33 34 match (module::find(ctx.mctx, partial)) { 35 case let r: (str, module::srcset) => 36 module::finish_srcset(&r.1); 37 return (what, symkind::SYMBOL); 38 case module::error => void; 39 }; 40 }; 41 if (len(what) == 2) { 42 match (lookup_local_enum(ctx, what)) { 43 case let id: ast::ident => 44 return (id, symkind::ENUM_LOCAL); 45 case => void; 46 }; 47 }; 48 if (len(what) > 2) { 49 match (lookup_remote_enum(ctx, what)?) { 50 case let id: ast::ident => 51 return (id, symkind::ENUM_REMOTE); 52 case => void; 53 }; 54 }; 55 56 match (module::find(ctx.mctx, what)) { 57 case let r: (str, module::srcset) => 58 module::finish_srcset(&r.1); 59 return (what, symkind::MODULE); 60 case module::error => void; 61 }; 62 63 return; 64 }; 65 66 fn is_local(ctx: *context, what: ast::ident) bool = { 67 if (len(what) != 1) { 68 return false; 69 }; 70 71 const summary = ctx.summary; 72 for (let c &.. summary.constants) { 73 const name = decl_ident(c)[0]; 74 if (name == what[0]) { 75 return true; 76 }; 77 }; 78 for (let e &.. summary.errors) { 79 const name = decl_ident(e)[0]; 80 if (name == what[0]) { 81 return true; 82 }; 83 }; 84 for (let t &.. summary.types) { 85 const name = decl_ident(t)[0]; 86 if (name == what[0]) { 87 return true; 88 }; 89 }; 90 for (let g &.. summary.globals) { 91 const name = decl_ident(g)[0]; 92 if (name == what[0]) { 93 return true; 94 }; 95 }; 96 for (let f &.. summary.funcs) { 97 const name = decl_ident(f)[0]; 98 if (name == what[0]) { 99 return true; 100 }; 101 }; 102 103 return false; 104 }; 105 106 fn lookup_local_enum(ctx: *context, what: ast::ident) (ast::ident | void) = { 107 for (let decl &.. ctx.summary.types) { 108 const name = decl_ident(decl)[0]; 109 if (name == what[0]) { 110 const t = (decl.decl as []ast::decl_type)[0]; 111 const e = match (t._type.repr) { 112 case let e: ast::enum_type => 113 yield e; 114 case => 115 return; 116 }; 117 for (let value .. e.values) { 118 if (value.name == what[1]) { 119 return what; 120 }; 121 }; 122 }; 123 }; 124 }; 125 126 fn lookup_remote_enum(ctx: *context, what: ast::ident) (ast::ident | void | error) = { 127 // mod::decl_name::member 128 const mod = what[..len(what) - 2]; 129 const decl_name = what[len(what) - 2]; 130 const member = what[len(what) - 1]; 131 132 const srcs = match (module::find(ctx.mctx, mod)) { 133 case let s: (str, module::srcset) => 134 yield s.1; 135 case let e: module::error => 136 module::finish_error(e); 137 return void; 138 }; 139 140 // This would take a lot of memory to load 141 let decls: []ast::decl = []; 142 defer { 143 for (let decl .. decls) { 144 ast::decl_finish(decl); 145 }; 146 free(decls); 147 }; 148 for (let in .. srcs.ha) { 149 let d = scan(in)?; 150 defer free(d); 151 append(decls, d...)!; 152 }; 153 154 for (let decl .. decls) { 155 const decls = match (decl.decl) { 156 case let t: []ast::decl_type => 157 yield t; 158 case => 159 continue; 160 }; 161 162 for (let d .. decls) { 163 if (d.ident[0] == decl_name) { 164 const e = match (d._type.repr) { 165 case let e: ast::enum_type => 166 yield e; 167 case => 168 abort(); 169 }; 170 for (let value .. e.values) { 171 if (value.name == member) { 172 return what; 173 }; 174 }; 175 }; 176 }; 177 }; 178 }; 179 180 export fn scan(path: str) ([]ast::decl | parse::error) = { 181 const input = match (os::open(path)) { 182 case let f: io::file => 183 yield f; 184 case let err: fs::error => 185 fmt::fatalf("Error reading {}: {}", path, fs::strerror(err)); 186 }; 187 defer io::close(input)!; 188 let sc = bufio::newscanner(input); 189 defer bufio::finish(&sc); 190 let lexer = lex::init(&sc, path, lex::flag::COMMENTS); 191 let su = parse::subunit(&lexer)?; 192 ast::imports_finish(su.imports); 193 return su.decls; 194 };