mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 16:54:25 +00:00
Modules are known by their file: import button from ui/menu.fch and import ui/button are one module, two util.fch are two; min/max/abs/clamp win over C's int-only abs; completion after an import's other name; hover docs; engineers' guides
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016GzNP6r8dg6WDxUVZy8wjL
This commit is contained in:
parent
17863ecf7b
commit
3ba2dcd5d6
16 changed files with 152 additions and 18 deletions
10
src/docs.fch
10
src/docs.fch
|
|
@ -21,11 +21,15 @@ fn builtins() -> []Doc {
|
|||
doc("delete_file", "delete_file(str path) -> bool", "Deletes the file. true if it worked; with or / try, the reason it didn't."),
|
||||
doc("error", "error(str message)", "The failure a fallible function returns: return error(\"...\"). The caller handles it with or / try."),
|
||||
doc("shell", "shell(str command) -> int", "Runs a command in the system's shell and gives back its exit code."),
|
||||
doc("exit", "exit(int code)", "Ends the program right away with this exit code."),
|
||||
doc("exit", "exit(int code)", "Ends the program right away with this exit code (it also ends an or { } block)."),
|
||||
doc("min", "min(a, b, ...) -> number", "The smallest of the numbers, in the type they all fit."),
|
||||
doc("max", "max(a, b, ...) -> number", "The biggest of the numbers, in the type they all fit."),
|
||||
doc("abs", "abs(x) -> number", "x without its minus sign (any number type)."),
|
||||
doc("clamp", "clamp(x, lo, hi) -> number", "x, but at least lo and at most hi."),
|
||||
doc("addr", "addr(x) -> ptr[T]", "A pointer to a variable, field or element. addr(function) gives C a callback."),
|
||||
doc("new", "new(value) -> ptr[T]", "Puts a copy of the value on the heap. Give it back with free(...)."),
|
||||
doc("free", "free(ptr p)", "Gives back memory from new(...) (or from C), freeing what the value owns."),
|
||||
doc("str", "str(x) -> str", "Turns a number, bool, char, []u8 or C char pointer into text."),
|
||||
doc("str", "str(x) -> str", "Turns a number, bool, char, []u8 or C char pointer into text. str(x, 2): a number with 2 digits after the point."),
|
||||
doc("int", "int(x) -> int", "Converts to a whole number: cuts off fractions, parses text (stops the program if it isn't a number, unless handled: int(s) or 0)."),
|
||||
doc("float", "float(x) -> float", "Converts to a decimal number; parses text (int(s) or 0.0 handles bad text)."),
|
||||
doc("char", "char(int n) -> char", "The character with this code."),
|
||||
|
|
@ -86,7 +90,7 @@ fn mapMethods() -> []Doc {
|
|||
|
||||
fn keywords() -> []str {
|
||||
return ["fn", "return", "if", "else", "while", "for", "in", "break", "continue", "true", "false", "null", "import",
|
||||
"link", "struct", "enum", "match", "export", "defer", "or", "try", "self"]
|
||||
"link", "struct", "enum", "match", "export", "defer", "or", "try", "self", "const"]
|
||||
}
|
||||
|
||||
fn typeNames() -> []str {
|
||||
|
|
|
|||
11
src/gen.fch
11
src/gen.fch
|
|
@ -279,8 +279,8 @@ fn noteCall(ptr[Gen] g, ast.Node c, FnSig sig) {
|
|||
|
||||
// what is known about every function, struct and enum, for completion and the outline
|
||||
fn indexAll(ptr[Gen] g, []str mainImports) {
|
||||
for im in mainImports {
|
||||
g.idx.push("@I\t" + im)
|
||||
for i in 0..mainImports.len {
|
||||
g.idx.push("@I\t" + mainImports[i] + "\t" + g.progs[0].modules[i]) // the name used here, the module
|
||||
}
|
||||
for sg in g.sigs {
|
||||
f := g.progs[sg.prog].fns[sg.index]
|
||||
|
|
@ -5319,8 +5319,8 @@ fn call(ptr[Gen] g, ast.Node c) -> Val {
|
|||
if ei >= 0 {
|
||||
return enumFrom(g, c, ei)
|
||||
}
|
||||
if g.ci.fns.has(n) {
|
||||
return callC(g, c, g.ci.fns[n])
|
||||
if g.ci.fns.has(n) && !["min", "max", "abs", "clamp"].contains(n) {
|
||||
return callC(g, c, g.ci.fns[n]) // (C's abs only takes an int; Finch's takes any number)
|
||||
}
|
||||
si = findStruct(g, "C", n)
|
||||
if si >= 0 {
|
||||
|
|
@ -5339,6 +5339,9 @@ fn call(ptr[Gen] g, ast.Node c) -> Val {
|
|||
// min(a, b, ...), max(a, b, ...), abs(x), clamp(x, lo, hi) for any numbers
|
||||
fn mathCall(ptr[Gen] g, ast.Node c) -> Val {
|
||||
n := c.name
|
||||
if g.index {
|
||||
noteRef(g, c, c.col, n.len, docs.hover(docs.find(docs.builtins(), n)), "", 0, 0)
|
||||
}
|
||||
for nm in c.names {
|
||||
if nm.len > 0 {
|
||||
fail(g, c, "named arguments (" + nm + ": ...) are only for creating structs")
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ struct Analysis {
|
|||
[]StructRec structs
|
||||
[]EnumRec enums
|
||||
[]str imports
|
||||
[]str importKeys // the module each import is: btn -> ui/button
|
||||
Diag diag
|
||||
}
|
||||
|
||||
|
|
@ -315,6 +316,11 @@ fn parseOutput(str output) -> Analysis {
|
|||
a.enums.push(EnumRec(module: f[1], name: f[2], file: f[3], line: intOf(f[4]), col: intOf(f[5]), variants: f[6].split(",")))
|
||||
} else if tag == "@I" && f.len >= 2 {
|
||||
a.imports.push(f[1])
|
||||
if f.len >= 3 {
|
||||
a.importKeys.push(f[2])
|
||||
} else {
|
||||
a.importKeys.push(f[1])
|
||||
}
|
||||
}
|
||||
} else if !a.diag.has {
|
||||
a.diag = parseDiag(line)
|
||||
|
|
@ -348,6 +354,7 @@ fn Server.analyse(str path) {
|
|||
a.vars = g.vars
|
||||
if a.imports.len == 0 {
|
||||
a.imports = g.imports
|
||||
a.importKeys = g.importKeys
|
||||
}
|
||||
}
|
||||
self.results[path] = a
|
||||
|
|
@ -537,6 +544,7 @@ fn completionAt(Analysis a, str text, str path, int line, int col) -> json.Json
|
|||
}
|
||||
// a module: its functions, structs and enums
|
||||
if a.imports.contains(owner) {
|
||||
owner = a.importKeys[a.imports.find(owner)]
|
||||
for f in a.fns {
|
||||
if f.module == owner && f.recv.len == 0 {
|
||||
items.push(item(f.name, 3, f.signature, ""))
|
||||
|
|
|
|||
39
src/main.fch
39
src/main.fch
|
|
@ -47,6 +47,7 @@ struct Loader {
|
|||
str os // "windows", "macos" or "linux": a module may have a version for each (time_windows.fch)
|
||||
str std // the standard library's folder, or ""
|
||||
[]str paths // FINCH_PATH: more folders to look for modules in
|
||||
str root // the main file's folder: modules are named by their path from here (ui/button)
|
||||
[]str overlayPaths // --overlay path=file: read `path` from `file` (the text an editor has, not saved yet)
|
||||
[]str overlayFiles
|
||||
}
|
||||
|
|
@ -89,6 +90,27 @@ fn findModule(ptr[Loader] l, str from, str name) -> str {
|
|||
return local
|
||||
}
|
||||
|
||||
// A module is named by its file, so `import button` in ui/menu.fch and `import ui/button` in main.fch
|
||||
// are the same module, and two different util.fch files are two modules.
|
||||
fn moduleKey(ptr[Loader] l, str file, str name) -> str {
|
||||
[]str roots
|
||||
roots.push(l.root)
|
||||
for d in l.paths {
|
||||
roots.push(d)
|
||||
}
|
||||
roots.push(l.std)
|
||||
for r in roots {
|
||||
if r.len > 0 && file.starts_with(r + "/") && file.ends_with(".fch") {
|
||||
key := file.sub(r.len + 1, file.len - 4)
|
||||
if key.ends_with("_" + l.os) {
|
||||
key = key.sub(0, key.len - l.os.len - 1) // time_windows.fch is the module time
|
||||
}
|
||||
return key
|
||||
}
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
||||
fn load(ptr[Loader] l, str path, str module) {
|
||||
if !file_exists(path) && !l.overlayPaths.contains(path) {
|
||||
if module.len > 0 {
|
||||
|
|
@ -104,12 +126,21 @@ fn load(ptr[Loader] l, str path, str module) {
|
|||
prog := parser.parse(lexer.lex(src, path), file, path)
|
||||
prog.module = module
|
||||
prog.path = path
|
||||
if module.len == 0 {
|
||||
l.root = dirOf(path)
|
||||
}
|
||||
[]str files
|
||||
for i in 0..prog.modules.len {
|
||||
f := findModule(l, path, prog.modules[i])
|
||||
files.push(f)
|
||||
prog.modules[i] = moduleKey(l, f, prog.modules[i])
|
||||
}
|
||||
modules := prog.modules
|
||||
l.progs.push(prog)
|
||||
for name in modules {
|
||||
if !l.loaded.contains(name) {
|
||||
l.loaded.push(name)
|
||||
load(l, findModule(l, path, name), name)
|
||||
for i in 0..modules.len {
|
||||
if !l.loaded.contains(modules[i]) {
|
||||
l.loaded.push(modules[i])
|
||||
load(l, files[i], modules[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue