mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 08: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
|
|
@ -343,7 +343,7 @@ import ui/button as btn // another name: btn.draw()
|
||||||
| `union U` | `U`: `U u`, `u.field`, `U(field: v)`, by pointer to C (not by value) |
|
| `union U` | `U`: `U u`, `u.field`, `U(field: v)`, by pointer to C (not by value) |
|
||||||
| `#define N 4`, `"text"`, `(1u << 3)`, `((Uint64)2)` | constants (not function-like macros) |
|
| `#define N 4`, `"text"`, `(1u << 3)`, `((Uint64)2)` | constants (not function-like macros) |
|
||||||
|
|
||||||
A C function's `char *` result is a `str` already; structs by value work on every target (System V, Windows x64, ARM64).
|
A C function's `char *` result is a `str` already; structs by value work with the System V, Windows x64 and ARM64 Linux calling conventions (tested in emulators; macOS by CI).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -98,6 +98,18 @@ its `endLine` (used by the language server). The old `fn name(...) type` order i
|
||||||
`name.fch` there, the folders of `FINCH_PATH`, then the standard library `std/`. Each module is parsed once; its names
|
`name.fch` there, the folders of `FINCH_PATH`, then the standard library `std/`. Each module is parsed once; its names
|
||||||
are used as `module.name`. `import "x.h"` goes to the C importer instead.
|
are used as `module.name`. `import "x.h"` goes to the C importer instead.
|
||||||
|
|
||||||
|
A module is identified by its **file**: the key is its path from the main file's folder (or from a `FINCH_PATH` folder or
|
||||||
|
`std/`) without `.fch` and without an `_<os>` suffix, e.g. `ui/button`. `Program.imports` holds the name each file uses
|
||||||
|
(`button`, or `btn` for `import ui/button as btn`) and `Program.modules` the key; `modFor()` maps one to the other.
|
||||||
|
Symbols use the key with `/` turned into `$` (`@finch.ui$button.make`).
|
||||||
|
|
||||||
|
**Constants.** `const NAME = expr` is a top-level node; `constValue()` evaluates the expression at each use, in the
|
||||||
|
defining module with no local scopes, so it folds to a literal (integer literal operators are folded in `arith`).
|
||||||
|
`checkConst()` allows only literals, names, operators, members and numeric conversions; `constBusy` catches cycles.
|
||||||
|
|
||||||
|
**Lexer extras.** `1e9` / `2.5e-3` are float tokens (the text is normalised to have a `.` for LLVM); `"""` strings are
|
||||||
|
dedented by the closing line's indentation in `longString()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Code generation
|
## 4. Code generation
|
||||||
|
|
@ -138,6 +150,14 @@ LLVM IR does not do the C calling convention for you, so `gen.fch` reproduces it
|
||||||
|
|
||||||
The same code is what lets the compiler call libclang, whose API passes `CXCursor`, `CXType` and `CXString` by value.
|
The same code is what lets the compiler call libclang, whose API passes `CXCursor`, `CXType` and `CXString` by value.
|
||||||
|
|
||||||
|
**Macros.** A `#define` that is one number or a string literal is read from its tokens. One whose tokens could be a
|
||||||
|
constant expression (no braces, semicolons or statement keywords) is evaluated by clang: a second parse of the header
|
||||||
|
adds `static const __auto_type __finch_m_NAME = (NAME);` for each, and `clang_Cursor_Evaluate` gives an int, a double
|
||||||
|
or a string. Those that are not constants only produce errors in that second parse and are dropped.
|
||||||
|
|
||||||
|
**Unions** are a `CStruct` with `isUnion`: the LLVM type is `{ [n x iA] }` (size and alignment from clang), every field has
|
||||||
|
offset 0, and a field is read through memory. They can't be passed by value to C (`unsupported` says why).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. Driver, linking and targets
|
## 6. Driver, linking and targets
|
||||||
|
|
@ -201,8 +221,9 @@ from the slot they flow into (`exprWant()`), so `[]f32 xs = [1, 2.5]` works.
|
||||||
|
|
||||||
### Binary operands: `unify(l, r)`
|
### Binary operands: `unify(l, r)`
|
||||||
|
|
||||||
A literal side adapts to the other; otherwise the side that widens grows; `i32` + `u32` is an error;
|
A literal side adapts to the other (also a float literal next to an `f32`: `x * 2.0` stays `f32`); otherwise the side
|
||||||
int + float → float; f32 + f64 → f64. There is no C integer promotion: `u8 + u8` stays `u8`.
|
that widens grows; `i32` + `u32` is an error; int + float → float; f32 + f64 → f64. There is no C integer promotion:
|
||||||
|
`u8 + u8` stays `u8`. Two integer literals fold to a literal, so `FLAG_A | FLAG_B` still fits a `u64` or `u32`.
|
||||||
|
|
||||||
### Explicit conversions: `convert()`
|
### Explicit conversions: `convert()`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,18 @@ kolejność `fn nazwa(...) typ` to błąd składni z podpowiedzią.
|
||||||
katalogów z `FINCH_PATH`, potem biblioteki standardowej `std/`. Każdy moduł jest parsowany raz; jego nazwy są używane jako
|
katalogów z `FINCH_PATH`, potem biblioteki standardowej `std/`. Każdy moduł jest parsowany raz; jego nazwy są używane jako
|
||||||
`modul.nazwa`. `import "x.h"` trafia do importera C.
|
`modul.nazwa`. `import "x.h"` trafia do importera C.
|
||||||
|
|
||||||
|
Moduł rozpoznaje się po **pliku**: kluczem jest jego ścieżka od folderu głównego pliku (albo od folderu z `FINCH_PATH`
|
||||||
|
lub `std/`) bez `.fch` i bez końcówki `_<system>`, np. `ui/przycisk`. `Program.imports` trzyma nazwę używaną w danym
|
||||||
|
pliku (`przycisk`, albo `p` przy `import ui/przycisk as p`), a `Program.modules` klucz; `modFor()` zamienia jedno na drugie.
|
||||||
|
Symbole używają klucza z `/` zamienionym na `$` (`@finch.ui$przycisk.rysuj`).
|
||||||
|
|
||||||
|
**Stałe.** `const NAZWA = wyrażenie` to węzeł najwyższego poziomu; `constValue()` liczy wyrażenie przy każdym użyciu, w
|
||||||
|
module, w którym je zdefiniowano, bez zmiennych lokalnych, więc wychodzi literał (operatory na literałach całkowitych
|
||||||
|
składa `arith`). `checkConst()` dopuszcza tylko literały, nazwy, operatory, pola i konwersje liczb; `constBusy` łapie pętle.
|
||||||
|
|
||||||
|
**Dodatki leksera.** `1e9` / `2.5e-3` to tokeny float (tekst dostaje `.` dla LLVM); teksty `"""` tracą wcięcie
|
||||||
|
linii zamykającej w `longString()`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. Generowanie kodu
|
## 4. Generowanie kodu
|
||||||
|
|
@ -130,6 +142,14 @@ LLVM IR nie realizuje za ciebie konwencji wywołań C, więc `gen.fch` ją odtwa
|
||||||
- reguły Microsoft x64 (struktury 1, 2, 4, 8 bajtów w rejestrze, reszta przez wskaźnik);
|
- reguły Microsoft x64 (struktury 1, 2, 4, 8 bajtów w rejestrze, reszta przez wskaźnik);
|
||||||
- `makePlan` zamienia klasyfikację w plan wywołania; `cThunk` robi z `addr(fn)` coś, co C może wywołać (callbacki).
|
- `makePlan` zamienia klasyfikację w plan wywołania; `cThunk` robi z `addr(fn)` coś, co C może wywołać (callbacki).
|
||||||
|
|
||||||
|
**Makra.** `#define` będący jedną liczbą albo tekstem czyta się z tokenów. Taki, którego tokeny mogą być wyrażeniem
|
||||||
|
stałym (bez klamer, średników i słów instrukcji), liczy clang: drugie parsowanie nagłówka dodaje
|
||||||
|
`static const __auto_type __finch_m_NAZWA = (NAZWA);` dla każdego, a `clang_Cursor_Evaluate` daje liczbę całkowitą,
|
||||||
|
double albo tekst. Te, które nie są stałymi, dają tylko błędy w tym drugim parsowaniu i są pomijane.
|
||||||
|
|
||||||
|
**Unie** to `CStruct` z `isUnion`: typ LLVM to `{ [n x iA] }` (rozmiar i wyrównanie od clanga), każde pole ma
|
||||||
|
przesunięcie 0, a pole czyta się przez pamięć. Nie da się ich przekazać do C przez wartość (`unsupported` mówi dlaczego).
|
||||||
|
|
||||||
Ten sam kod pozwala kompilatorowi wołać libclang, którego API przekazuje `CXCursor`, `CXType` i `CXString` przez wartość.
|
Ten sam kod pozwala kompilatorowi wołać libclang, którego API przekazuje `CXCursor`, `CXType` i `CXString` przez wartość.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
@ -194,9 +214,10 @@ Literały tablic biorą typ elementu z miejsca, do którego trafiają (`exprWant
|
||||||
|
|
||||||
### Argumenty operatorów: `unify(l, r)`
|
### Argumenty operatorów: `unify(l, r)`
|
||||||
|
|
||||||
Strona-literał dopasowuje się do drugiej; w przeciwnym razie rośnie strona, która się rozszerza;
|
Strona-literał dopasowuje się do drugiej (także literał float obok `f32`: `x * 2.0` zostaje `f32`); w przeciwnym razie
|
||||||
`i32` + `u32` to błąd; całkowita + zmiennoprzecinkowa → zmiennoprzecinkowa; f32 + f64 → f64.
|
rośnie strona, która się rozszerza; `i32` + `u32` to błąd; całkowita + zmiennoprzecinkowa → zmiennoprzecinkowa;
|
||||||
Nie ma promocji całkowitych z C: `u8 + u8` zostaje `u8`.
|
f32 + f64 → f64. Nie ma promocji całkowitych z C: `u8 + u8` zostaje `u8`. Dwa literały całkowite składają się w literał,
|
||||||
|
więc `FLAG_A | FLAG_B` dalej mieści się w `u64` albo `u32`.
|
||||||
|
|
||||||
### Konwersje jawne: `convert()`
|
### Konwersje jawne: `convert()`
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -342,7 +342,7 @@ import ui/przycisk as p // inna nazwa: p.rysuj()
|
||||||
| `union U` | `U`: `U u`, `u.pole`, `U(pole: v)`, przez wskaźnik do C (nie przez wartość) |
|
| `union U` | `U`: `U u`, `u.pole`, `U(pole: v)`, przez wskaźnik do C (nie przez wartość) |
|
||||||
| `#define N 4`, `"tekst"`, `(1u << 3)`, `((Uint64)2)` | stałe (bez makr-funkcji) |
|
| `#define N 4`, `"tekst"`, `(1u << 3)`, `((Uint64)2)` | stałe (bez makr-funkcji) |
|
||||||
|
|
||||||
Wynik `char *` z funkcji C to od razu `str`; struktury przez wartość działają na każdym systemie (System V, Windows x64, ARM64).
|
Wynik `char *` z funkcji C to od razu `str`; struktury przez wartość działają w konwencjach System V, Windows x64 i ARM64 Linux (sprawdzone w emulatorach; macOS przez CI).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|
|
||||||
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("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("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("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("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("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("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("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("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."),
|
doc("char", "char(int n) -> char", "The character with this code."),
|
||||||
|
|
@ -86,7 +90,7 @@ fn mapMethods() -> []Doc {
|
||||||
|
|
||||||
fn keywords() -> []str {
|
fn keywords() -> []str {
|
||||||
return ["fn", "return", "if", "else", "while", "for", "in", "break", "continue", "true", "false", "null", "import",
|
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 {
|
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
|
// what is known about every function, struct and enum, for completion and the outline
|
||||||
fn indexAll(ptr[Gen] g, []str mainImports) {
|
fn indexAll(ptr[Gen] g, []str mainImports) {
|
||||||
for im in mainImports {
|
for i in 0..mainImports.len {
|
||||||
g.idx.push("@I\t" + im)
|
g.idx.push("@I\t" + mainImports[i] + "\t" + g.progs[0].modules[i]) // the name used here, the module
|
||||||
}
|
}
|
||||||
for sg in g.sigs {
|
for sg in g.sigs {
|
||||||
f := g.progs[sg.prog].fns[sg.index]
|
f := g.progs[sg.prog].fns[sg.index]
|
||||||
|
|
@ -5319,8 +5319,8 @@ fn call(ptr[Gen] g, ast.Node c) -> Val {
|
||||||
if ei >= 0 {
|
if ei >= 0 {
|
||||||
return enumFrom(g, c, ei)
|
return enumFrom(g, c, ei)
|
||||||
}
|
}
|
||||||
if g.ci.fns.has(n) {
|
if g.ci.fns.has(n) && !["min", "max", "abs", "clamp"].contains(n) {
|
||||||
return callC(g, c, g.ci.fns[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)
|
si = findStruct(g, "C", n)
|
||||||
if si >= 0 {
|
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
|
// min(a, b, ...), max(a, b, ...), abs(x), clamp(x, lo, hi) for any numbers
|
||||||
fn mathCall(ptr[Gen] g, ast.Node c) -> Val {
|
fn mathCall(ptr[Gen] g, ast.Node c) -> Val {
|
||||||
n := c.name
|
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 {
|
for nm in c.names {
|
||||||
if nm.len > 0 {
|
if nm.len > 0 {
|
||||||
fail(g, c, "named arguments (" + nm + ": ...) are only for creating structs")
|
fail(g, c, "named arguments (" + nm + ": ...) are only for creating structs")
|
||||||
|
|
|
||||||
|
|
@ -80,6 +80,7 @@ struct Analysis {
|
||||||
[]StructRec structs
|
[]StructRec structs
|
||||||
[]EnumRec enums
|
[]EnumRec enums
|
||||||
[]str imports
|
[]str imports
|
||||||
|
[]str importKeys // the module each import is: btn -> ui/button
|
||||||
Diag diag
|
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(",")))
|
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 {
|
} else if tag == "@I" && f.len >= 2 {
|
||||||
a.imports.push(f[1])
|
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 {
|
} else if !a.diag.has {
|
||||||
a.diag = parseDiag(line)
|
a.diag = parseDiag(line)
|
||||||
|
|
@ -348,6 +354,7 @@ fn Server.analyse(str path) {
|
||||||
a.vars = g.vars
|
a.vars = g.vars
|
||||||
if a.imports.len == 0 {
|
if a.imports.len == 0 {
|
||||||
a.imports = g.imports
|
a.imports = g.imports
|
||||||
|
a.importKeys = g.importKeys
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.results[path] = a
|
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
|
// a module: its functions, structs and enums
|
||||||
if a.imports.contains(owner) {
|
if a.imports.contains(owner) {
|
||||||
|
owner = a.importKeys[a.imports.find(owner)]
|
||||||
for f in a.fns {
|
for f in a.fns {
|
||||||
if f.module == owner && f.recv.len == 0 {
|
if f.module == owner && f.recv.len == 0 {
|
||||||
items.push(item(f.name, 3, f.signature, ""))
|
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 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 std // the standard library's folder, or ""
|
||||||
[]str paths // FINCH_PATH: more folders to look for modules in
|
[]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 overlayPaths // --overlay path=file: read `path` from `file` (the text an editor has, not saved yet)
|
||||||
[]str overlayFiles
|
[]str overlayFiles
|
||||||
}
|
}
|
||||||
|
|
@ -89,6 +90,27 @@ fn findModule(ptr[Loader] l, str from, str name) -> str {
|
||||||
return local
|
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) {
|
fn load(ptr[Loader] l, str path, str module) {
|
||||||
if !file_exists(path) && !l.overlayPaths.contains(path) {
|
if !file_exists(path) && !l.overlayPaths.contains(path) {
|
||||||
if module.len > 0 {
|
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 := parser.parse(lexer.lex(src, path), file, path)
|
||||||
prog.module = module
|
prog.module = module
|
||||||
prog.path = path
|
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
|
modules := prog.modules
|
||||||
l.progs.push(prog)
|
l.progs.push(prog)
|
||||||
for name in modules {
|
for i in 0..modules.len {
|
||||||
if !l.loaded.contains(name) {
|
if !l.loaded.contains(modules[i]) {
|
||||||
l.loaded.push(name)
|
l.loaded.push(modules[i])
|
||||||
load(l, findModule(l, path, name), name)
|
load(l, files[i], modules[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -186,6 +186,19 @@ c.notify("textDocument/didChange", {"textDocument": {"uri": uri2, "version": 4},
|
||||||
diags = c.diagnostics(uri2)
|
diags = c.diagnostics(uri2)
|
||||||
check("error: unhandled failure", diags and "can fail" in diags[0]["message"], diags)
|
check("error: unhandled failure", diags and "can fail" in diags[0]["message"], diags)
|
||||||
|
|
||||||
|
# a module imported under another name: completion after its name
|
||||||
|
src3 = "import path as pt\n\nfn main() {\n print(pt.base(\"a/b\"))\n}\n"
|
||||||
|
path3 = os.path.join(d, "alias.fch")
|
||||||
|
open(path3, "w").write(src3)
|
||||||
|
uri3 = pathlib.Path(path3).resolve().as_uri()
|
||||||
|
c.notify("textDocument/didOpen", {"textDocument": {"uri": uri3, "languageId": "finch", "version": 1, "text": src3}})
|
||||||
|
check("import as: compiles", c.diagnostics(uri3) == [])
|
||||||
|
e3 = src3.replace(" print(", " pt.\n print(")
|
||||||
|
c.notify("textDocument/didChange", {"textDocument": {"uri": uri3, "version": 2}, "contentChanges": [{"text": e3}]})
|
||||||
|
comp = c.request("textDocument/completion", {"textDocument": {"uri": uri3}, "position": {"line": 3, "character": 7}})
|
||||||
|
labels = [i["label"] for i in comp["items"]]
|
||||||
|
check("completion: a module under another name", "base" in labels and "join" in labels, labels)
|
||||||
|
|
||||||
c.request("shutdown", None)
|
c.request("shutdown", None)
|
||||||
c.notify("exit", None)
|
c.notify("exit", None)
|
||||||
c.p.wait(timeout=5)
|
c.p.wait(timeout=5)
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
// C unions (SDL_Event-style) and #defines that are expressions, strings or casts
|
// C unions (SDL_Event-style) and #defines that are expressions, strings or casts
|
||||||
import "cunion.h"
|
import "cunion.h"
|
||||||
|
import "stdlib.h"
|
||||||
link "cunion.c"
|
link "cunion.c"
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
@ -22,4 +23,5 @@ fn main() {
|
||||||
Tagged t
|
Tagged t
|
||||||
t.as.f = 0.5
|
t.as.f = 0.5
|
||||||
print(t.as.f)
|
print(t.as.f)
|
||||||
|
print(abs(-2.5), abs(-3), min(EV_KEY, 7)) // Finch's abs, not C's int-only one
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,3 +4,4 @@ motion 1.5 -2
|
||||||
true 32
|
true 32
|
||||||
32 2
|
32 2
|
||||||
0.5
|
0.5
|
||||||
|
2.5 3 7
|
||||||
|
|
|
||||||
10
tests/new/modules_by_file.fch
Normal file
10
tests/new/modules_by_file.fch
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
// a module is its file: ui/menu's "import button" is this file's "import ui/button",
|
||||||
|
// and ui/util.fch and util.fch are two different modules
|
||||||
|
import ui/button
|
||||||
|
import ui/menu
|
||||||
|
import util
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
button.Button b = menu.first()
|
||||||
|
print(b.label, b.w, menu.whoami(), util.who())
|
||||||
|
}
|
||||||
1
tests/new/modules_by_file.out
Normal file
1
tests/new/modules_by_file.out
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
menu 30 ui/util util
|
||||||
11
tests/new/ui/menu.fch
Normal file
11
tests/new/ui/menu.fch
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
// for tests/new/modules_by_file.fch: its sibling, imported without the folder
|
||||||
|
import button
|
||||||
|
import util
|
||||||
|
|
||||||
|
fn first() -> button.Button {
|
||||||
|
return button.make("menu")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn whoami() -> str {
|
||||||
|
return util.who()
|
||||||
|
}
|
||||||
4
tests/new/ui/util.fch
Normal file
4
tests/new/ui/util.fch
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// for tests/new/modules_by_file.fch: a util.fch in a folder
|
||||||
|
fn who() -> str {
|
||||||
|
return "ui/util"
|
||||||
|
}
|
||||||
4
tests/new/util.fch
Normal file
4
tests/new/util.fch
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
// for tests/new/modules_by_file.fch: the top-level util.fch
|
||||||
|
fn who() -> str {
|
||||||
|
return "util"
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue