mirror of
https://github.com/Nexoniarz/Finch.git
synced 2026-11-03 00:44:27 +00:00
finch.toml, threads and atomics, finch fmt, C++ files, struct #defines, a struct variable given for its ptr
- finch.toml: name, entry, paths, include, links, libdirs, cflags, ldflags, and [target.windows] etc.;
finch run / build / test with no file use its entry
- import thread: spawn, join, mutex, cpus; atomic_add/sub/load/store/swap/cas built in
- finch fmt [--check]: indentation by { }, trailing spaces, empty lines; also as LSP formatting
- link "x.cpp" (or in finch.toml): built with the C++ compiler, which then links (FINCH_CXX)
- #define RAYWHITE (Color){ 245, 245, 245, 255 } imports as a Color
- push(body) where push takes ptr[Body]: the same as push(addr(body))
- --target android (NDK clang from ANDROID_NDK_HOME, -llog)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016GzNP6r8dg6WDxUVZy8wjL
This commit is contained in:
parent
c830c780fe
commit
d428498efd
40 changed files with 1398 additions and 18 deletions
|
|
@ -124,6 +124,12 @@ syms = c.request("textDocument/documentSymbol", {"textDocument": td})
|
|||
names = sorted(s["name"] for s in syms)
|
||||
check("document symbols", names == ["Point", "add", "main"], names)
|
||||
|
||||
messy = "fn main() {\n x := 1 \n\n\n\tprint(x)\n}"
|
||||
c.notify("textDocument/didChange", {"textDocument": {"uri": uri, "version": 5}, "contentChanges": [{"text": messy}]})
|
||||
fm = c.request("textDocument/formatting", {"textDocument": td, "options": {"tabSize": 4, "insertSpaces": True}})
|
||||
check("formatting", fm and fm[0]["newText"] == "fn main() {\n x := 1\n\n print(x)\n}\n", fm)
|
||||
c.notify("textDocument/didChange", {"textDocument": {"uri": uri, "version": 5}, "contentChanges": [{"text": src}]})
|
||||
|
||||
bad = src.replace("total := add(p.x, p.y)", 'total := add(p.x, "y")')
|
||||
c.notify("textDocument/didChange", {"textDocument": {"uri": uri, "version": 6}, "contentChanges": [{"text": bad}]})
|
||||
diags = c.diagnostics(uri)
|
||||
|
|
|
|||
23
tests/new/auto_addr.fch
Normal file
23
tests/new/auto_addr.fch
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
struct Body {
|
||||
float x
|
||||
float y
|
||||
}
|
||||
|
||||
fn push(ptr[Body] b, float dx) {
|
||||
b.x += dx
|
||||
}
|
||||
|
||||
fn Body.moved(float dx) -> Body {
|
||||
return Body(x: self.x + dx, y: self.y)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
b := Body(x: 1.0, y: 2.0)
|
||||
push(b, 0.5) // the same as push(addr(b), 0.5)
|
||||
push(addr(b), 0.5)
|
||||
print(b.x)
|
||||
p := addr(b)
|
||||
push(p, 1.0) // already a ptr: as it is
|
||||
print(b.x)
|
||||
print(b.moved(1.0).x)
|
||||
}
|
||||
3
tests/new/auto_addr.out
Normal file
3
tests/new/auto_addr.out
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
2
|
||||
3
|
||||
4
|
||||
|
|
@ -24,4 +24,6 @@ fn main() {
|
|||
t.as.f = 0.5
|
||||
print(t.as.f)
|
||||
print(abs(-2.5), abs(-3), min(EV_KEY, 7)) // Finch's abs, not C's int-only one
|
||||
bg := RAYWHITE // #define RAYWHITE (Color){ 245, 245, 245, 255 }
|
||||
print(bg.r, bg.a, DARK.g, DARK.a, HALF_UP.x, HALF_UP.y)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,3 +5,4 @@ true 32
|
|||
32 2
|
||||
0.5
|
||||
2.5 3 7
|
||||
245 255 20 0 0.5 -1
|
||||
|
|
|
|||
|
|
@ -22,6 +22,14 @@ typedef struct { int kind; union { int i; float f; } as; } Tagged;
|
|||
#define LONG_NAME "a" "b"
|
||||
#define NOT_A_CONSTANT do { } while (0)
|
||||
|
||||
typedef struct { unsigned char r, g, b, a; } Color;
|
||||
typedef struct { float x, y; } Vec2;
|
||||
#define CLITERAL(type) (type)
|
||||
#define RAYWHITE CLITERAL(Color){ 245, 245, 245, 255 }
|
||||
#define DARK (Color){ 20, 20, 20 }
|
||||
#define HALF_UP (Vec2){ 0.5f, -1 }
|
||||
#define NOT_A_STRUCT_CONST (Color){ 1, 2, (unsigned char)(long)&poll_event, 4 }
|
||||
|
||||
static inline int fill_key(Event *e, int key) {
|
||||
e->key.type = EV_KEY;
|
||||
e->key.key = key;
|
||||
|
|
|
|||
13
tests/new/fail/auto_addr_value.fch
Normal file
13
tests/new/fail/auto_addr_value.fch
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
// expect: argument 'b' must be ptr[Body], but this is Body
|
||||
struct Body {
|
||||
float x
|
||||
}
|
||||
fn push(ptr[Body] b) {
|
||||
b.x += 1.0
|
||||
}
|
||||
fn make() -> Body {
|
||||
return Body(x: 1.0)
|
||||
}
|
||||
fn main() {
|
||||
push(make())
|
||||
}
|
||||
21
tests/new/interpolation.fch
Normal file
21
tests/new/interpolation.fch
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
// "\(x)" in text
|
||||
struct P {
|
||||
int x
|
||||
float y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
name := "Ada"
|
||||
hp := 7
|
||||
p := P(x: 3, y: 0.25)
|
||||
print("hi \(name), hp \(hp), next \(hp + 1)")
|
||||
print("p = (\(p.x), \(p.y, 2)) \("nested \(name)")")
|
||||
map[str]int m
|
||||
m["k"] = 5
|
||||
print("\(m["k"]) \(str(1.0 / 3.0, 3))")
|
||||
print("no code here: \\(x) and \t tab")
|
||||
long := """
|
||||
speed \(p.y * 4.0)
|
||||
"""
|
||||
print(long)
|
||||
}
|
||||
5
tests/new/interpolation.out
Normal file
5
tests/new/interpolation.out
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
hi Ada, hp 7, next 8
|
||||
p = (3, 0.25) nested Ada
|
||||
5 0.333
|
||||
no code here: \(x) and tab
|
||||
speed 1
|
||||
55
tests/new/threads.fch
Normal file
55
tests/new/threads.fch
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
import thread
|
||||
|
||||
struct Shared {
|
||||
[]int seen
|
||||
}
|
||||
|
||||
struct Job {
|
||||
ptr[int] counter
|
||||
int adds
|
||||
ptr[thread.Mutex] lock
|
||||
ptr[Shared] common
|
||||
int id
|
||||
}
|
||||
|
||||
fn work(ptr arg) {
|
||||
ptr[Job] job = arg
|
||||
for i in 0..job.adds {
|
||||
atomic_add(job.counter, 1)
|
||||
}
|
||||
job.lock.lock()
|
||||
job.common.seen.push(job.id)
|
||||
job.lock.unlock()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
count := 0
|
||||
m := thread.mutex()
|
||||
defer m.close()
|
||||
both := Shared()
|
||||
[]Job jobs
|
||||
for i in 0..4 {
|
||||
jobs.push(Job(counter: addr(count), adds: 100000, lock: addr(m), common: addr(both), id: i))
|
||||
}
|
||||
[]thread.Thread ts
|
||||
for i in 0..4 {
|
||||
ts.push(thread.spawn(addr(work), addr(jobs[i])) or {
|
||||
print("can't start")
|
||||
exit(1)
|
||||
})
|
||||
}
|
||||
for i in 0..4 {
|
||||
ts[i].join()
|
||||
}
|
||||
print(atomic_load(addr(count)))
|
||||
print(both.seen.len)
|
||||
print(thread.cpus() > 0)
|
||||
|
||||
u32 flags = 5
|
||||
print(atomic_swap(addr(flags), 9))
|
||||
print(atomic_cas(addr(flags), 9, 1))
|
||||
print(atomic_cas(addr(flags), 9, 2))
|
||||
atomic_store(addr(flags), 7)
|
||||
print(atomic_sub(addr(flags), 2))
|
||||
print(flags)
|
||||
}
|
||||
8
tests/new/threads.out
Normal file
8
tests/new/threads.out
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
400000
|
||||
4
|
||||
true
|
||||
5
|
||||
true
|
||||
false
|
||||
7
|
||||
5
|
||||
18
tests/run.sh
18
tests/run.sh
|
|
@ -56,6 +56,24 @@ for f in fail/*.fch new/fail/*.fch; do
|
|||
fi
|
||||
done
|
||||
|
||||
# the commands around the compiler: finch test, the chain of calls, finch fmt, a finch.toml project
|
||||
check_tool() { # name, what it printed, the file with what it should print
|
||||
if [ "$2" == "$(tr -d '\r' < "$3")" ]; then
|
||||
pass=$((pass + 1))
|
||||
else
|
||||
failed=$((failed + 1))
|
||||
echo "FAIL $1"
|
||||
diff <(echo "$2") "$3" | head -20
|
||||
fi
|
||||
}
|
||||
check_tool "finch test" "$(cd tool && "$FINCH" test test_blocks.fch 2>&1 | tr -d '\r')" tool/test_blocks.out
|
||||
check_tool "the chain of calls" "$(cd tool && "$FINCH" run trace.fch 2>&1 | tr -d '\r')" tool/trace.out
|
||||
cp tool/fmt_in.fch "$tmp/fmt.fch"
|
||||
"$FINCH" fmt "$tmp/fmt.fch"
|
||||
check_tool "finch fmt" "$(cat "$tmp/fmt.fch")" tool/fmt_out.fch
|
||||
check_tool "finch fmt (again)" "$("$FINCH" fmt --check tool/fmt_out.fch 2>&1)" /dev/null
|
||||
check_tool "finch.toml" "$(cd tool/project && "$FINCH" run 2>&1 | tr -d '\r')" tool/project/expected.out
|
||||
|
||||
# the same programs with debug info (-g -O0): the metadata has to be valid for every kind of thing a program has
|
||||
for f in new/enums.fch new/generics.fch new/std_json.fch run/basics.fch run/methods_basic.fch run/maps.fch run/errors.fch \
|
||||
run/defer.fch run/c_structs.fch run/memory.fch; do
|
||||
|
|
|
|||
24
tests/tool/fmt_in.fch
Normal file
24
tests/tool/fmt_in.fch
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
struct P {
|
||||
int x
|
||||
int y
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
p := P(x: 1,
|
||||
y: 2)
|
||||
msg := """
|
||||
a {
|
||||
b
|
||||
"""
|
||||
if p.x > 0 {
|
||||
print("{ \(p.x) }") // } not counted
|
||||
} else {
|
||||
print(msg)
|
||||
}
|
||||
s := "a" +
|
||||
"b"
|
||||
print(s)
|
||||
}
|
||||
|
||||
21
tests/tool/fmt_out.fch
Normal file
21
tests/tool/fmt_out.fch
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
struct P {
|
||||
int x
|
||||
int y
|
||||
}
|
||||
|
||||
fn main() {
|
||||
p := P(x: 1,
|
||||
y: 2)
|
||||
msg := """
|
||||
a {
|
||||
b
|
||||
"""
|
||||
if p.x > 0 {
|
||||
print("{ \(p.x) }") // } not counted
|
||||
} else {
|
||||
print(msg)
|
||||
}
|
||||
s := "a" +
|
||||
"b"
|
||||
print(s)
|
||||
}
|
||||
1
tests/tool/project/expected.out
Normal file
1
tests/tool/project/expected.out
Normal file
|
|
@ -0,0 +1 @@
|
|||
hello world 42
|
||||
14
tests/tool/project/finch.toml
Normal file
14
tests/tool/project/finch.toml
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# a project: finch run / build / test here need no file and no flags
|
||||
name = "mygame"
|
||||
entry = "src/main.fch"
|
||||
paths = ["src"]
|
||||
include = ["native"]
|
||||
links = [
|
||||
"native/glue.cpp", # C++, built with the C++ compiler
|
||||
]
|
||||
cflags = ["-DGLUE_BASE=40"]
|
||||
|
||||
[target.linux]
|
||||
links = ["m"]
|
||||
[target.windows]
|
||||
links = ["ws2_32"]
|
||||
9
tests/tool/project/native/glue.cpp
Normal file
9
tests/tool/project/native/glue.cpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
// C++ behind a C header: extern "C" gives the function a name C (and Finch) can call
|
||||
#include "glue.h"
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
extern "C" int glue_answer(void) {
|
||||
std::vector<int> v = {1, 1};
|
||||
return GLUE_BASE + std::accumulate(v.begin(), v.end(), 0);
|
||||
}
|
||||
7
tests/tool/project/native/glue.h
Normal file
7
tests/tool/project/native/glue.h
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
int glue_answer(void);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
3
tests/tool/project/src/game/world.fch
Normal file
3
tests/tool/project/src/game/world.fch
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
fn hello() -> str {
|
||||
return "world"
|
||||
}
|
||||
6
tests/tool/project/src/main.fch
Normal file
6
tests/tool/project/src/main.fch
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
import "glue.h"
|
||||
import game/world
|
||||
|
||||
fn main() {
|
||||
print("hello \(world.hello()) \(glue_answer())")
|
||||
}
|
||||
30
tests/tool/test_blocks.fch
Normal file
30
tests/tool/test_blocks.fch
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
// finch test: each test "..." block runs on its own; a failed assert or a runtime error fails only that one
|
||||
fn add(int a, int b) -> int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
fn second([]int xs) -> int {
|
||||
return xs[1]
|
||||
}
|
||||
|
||||
test "add works" {
|
||||
assert(add(2, 3) == 5)
|
||||
assert(add(-1, 1) == 0, "zero")
|
||||
}
|
||||
|
||||
test "a failing assert" {
|
||||
x := add(1, 2)
|
||||
assert(x == 4, "x is \(x), not 4")
|
||||
}
|
||||
|
||||
test "a runtime error fails only this test" {
|
||||
print(second([1]))
|
||||
}
|
||||
|
||||
test "still runs after the others" {
|
||||
assert("\(1.5, 2) and \(add(1, 1))" == "1.50 and 2")
|
||||
}
|
||||
|
||||
fn main() {
|
||||
print("main is not run by finch test")
|
||||
}
|
||||
8
tests/tool/test_blocks.out
Normal file
8
tests/tool/test_blocks.out
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
ok add works
|
||||
FAIL a failing assert
|
||||
test_blocks.fch:17: runtime error: assert failed: x is 3, not 4
|
||||
FAIL a runtime error fails only this test
|
||||
test_blocks.fch:7: runtime error: index 1 is out of range (the length is 1)
|
||||
ok still runs after the others
|
||||
|
||||
2 passed, 2 failed
|
||||
16
tests/tool/trace.fch
Normal file
16
tests/tool/trace.fch
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
struct Enemy {
|
||||
[]int path
|
||||
}
|
||||
|
||||
fn Enemy.next(int i) -> int {
|
||||
return self.path[i]
|
||||
}
|
||||
|
||||
fn step(Enemy e) -> int {
|
||||
return e.next(3)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
e := Enemy(path: [1, 2])
|
||||
print(step(e))
|
||||
}
|
||||
4
tests/tool/trace.out
Normal file
4
tests/tool/trace.out
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
trace.fch:6: runtime error: index 3 is out of range (the length is 2)
|
||||
in Enemy.next (trace.fch:6)
|
||||
in step (trace.fch:10)
|
||||
in main (trace.fch:15)
|
||||
Loading…
Reference in a new issue