plum

#treesitter#compiler#wasm

git clone https://git.pyrossh.dev/plum

A statically typed, imperative programming language inspired by rust, python


7277762 — Peter John 2026-11-04T22:32:18+05:30
tests: delete generator-only test and fixture (T11)
plum-checker/tests/fixtures/g3_generic_funstar.plum DELETED
@@ -1,34 +0,0 @@
1
- module g3
2
-
3
- enum Link[T] =
4
- | Nil
5
- | Cons(head: T, tail: Link[T])
6
-
7
- fun length(self) -> Int = match self
8
- Nil => 0
9
- Cons(_, t) => 1 + t.length()
10
-
11
- fun* items(self) *T =
12
- cur := self
13
- while true
14
- if Cons(h, t) := cur
15
- cur = t
16
- yield h
17
- else
18
- break
19
-
20
- fun* collect(xs: Link[T]) *T -> Link[T] =
21
- out: Link[T] := Nil
22
- for x := range xs.items()
23
- v := yield x
24
- out = Cons(head: v, tail: out)
25
- out
26
-
27
- fun both() -> Int =
28
- a: Link[Int] := Cons(head: 1, tail: Cons(head: 2, tail: Nil))
29
- b: Link[Str] := Cons(head: "x", tail: Nil)
30
- ints := for x := range collect(a)
31
- x
32
- strs := for s := range collect(b)
33
- s
34
- ints.length() + strs.length()
plum-core/tests/forms_generic_test.rs DELETED
@@ -1,128 +0,0 @@
1
- #![allow(non_snake_case)]
2
- //! Generic `fun*` bodies: the iterator's own type params are bound from the
3
- //! argument types at the `for` site and substituted in the expanded body.
4
- //! (plum-core cannot run wasm; the checker end of this is covered by
5
- //! `./target/debug/plum test` on the repro and plum-examples.)
6
- use plum_core::ast::*;
7
- use plum_core::forms;
8
-
9
- fn parse(src: &str) -> Source {
10
- plum_test_support::parse(src)
11
- }
12
-
13
- const DEFS: &str = "\
14
- enum Link[T] = Nil | Cons(head: T, tail: Link[T])
15
-
16
- fun* collect(xs: Link[T]) *T -> Link[T] =
17
- out: Link[T] := Nil
18
- cur := xs
19
- while true
20
- if Cons(h, t) := cur
21
- v := yield h
22
- out = Cons(head: v, tail: out)
23
- cur = t
24
- else
25
- break
26
- out
27
- ";
28
-
29
- fn ty(name: &str, arg: &str) -> Type {
30
- Type {
31
- name: name.into(),
32
- generics: vec![Type { name: arg.into(), generics: vec![] }],
33
- }
34
- }
35
-
36
- fn forLoops(stmts: &[Stmt], out: &mut Vec<ForLoop>) {
37
- for s in stmts {
38
- match s {
39
- Stmt::ForLoop(f) => out.push(f.clone()),
40
- Stmt::If(i) => forLoops(&i.body.stmts, out),
41
- _ => {}
42
- }
43
- }
44
- }
45
-
46
- /// Annotated local types (`out: Link[X]`) in the expansion of every `for` of fn `f`,
47
- /// where `argTy` gives each call's argument type by the `Var` name.
48
- fn annotations(src: &str, f: &str, argTy: &dyn std::ops::Fn(&Expr) -> Option<Type>) -> Vec<Type> {
49
- let source = parse(&format!("{DEFS}\n{src}"));
50
- forms::begin(&source);
51
- let func = source
52
- .items
53
- .iter()
54
- .find_map(|i| match i {
55
- Item::Fn(x) if x.name == f => Some(x.clone()),
56
- _ => None,
57
- })
58
- .expect("fn");
59
- let FnBody::Block(b) = &func.body else { panic!("block body") };
60
- let mut loops = Vec::new();
61
- forLoops(&b.stmts, &mut loops);
62
- assert!(!loops.is_empty(), "no for loop found");
63
- let mut found = Vec::new();
64
- for fl in &loops {
65
- let lowered = forms::lowerLoopTyped(fl, &|_| None, argTy);
66
- let mut stmts: Vec<Stmt> = lowered.to_vec();
67
- let mut visit = |s: &mut Stmt| {
68
- if let Stmt::Assign(a) = s {
69
- if let Some(t) = &a.ty {
70
- found.push(t.clone());
71
- }
72
- }
73
- };
74
- forms::walkStmts(&mut stmts, &mut visit);
75
- }
76
- found
77
- }
78
-
79
- #[test]
80
- fn generic_funstar_body_local() {
81
- let tys = annotations(
82
- "fun main() =\n ys := for x := range collect(a)\n x\n",
83
- "main",
84
- &|_| Some(ty("Link", "Int")),
85
- );
86
- assert_eq!(tys, vec![ty("Link", "Int")]);
87
- }
88
-
89
- #[test]
90
- fn generic_funstar_two_types() {
91
- let tys = annotations(
92
- "fun main() =\n ys := for x := range collect(a)\n x\n zs := for x := range collect(b)\n x\n",
93
- "main",
94
- &|e| match e {
95
- Expr::Var(n) if n == "a" => Some(ty("Link", "Int")),
96
- Expr::Var(n) if n == "b" => Some(ty("Link", "Str")),
97
- _ => None,
98
- },
99
- );
100
- assert_eq!(tys, vec![ty("Link", "Int"), ty("Link", "Str")]);
101
- }
102
-
103
- #[test]
104
- fn generic_funstar_in_generic_fn() {
105
- // Inside a generic fn the caller's own `U` is what gets bound, not left as `T`.
106
- let tys = annotations(
107
- "fun main() =\n ys := for x := range collect(a)\n x\n",
108
- "main",
109
- &|_| Some(ty("Link", "U")),
110
- );
111
- assert_eq!(tys, vec![ty("Link", "U")]);
112
- }
113
-
114
- #[test]
115
- fn untyped_lowering_keeps_generic_param() {
116
- let source = parse(&format!(
117
- "{DEFS}\nfun main() =\n ys := for x := range collect(a)\n x\n"
118
- ));
119
- forms::begin(&source);
120
- let FnBody::Block(b) = &source.items.iter().find_map(|i| match i {
121
- Item::Fn(x) if x.name == "main" => Some(x.clone()),
122
- _ => None,
123
- }).unwrap().body else { panic!() };
124
- let mut loops = Vec::new();
125
- forLoops(&b.stmts, &mut loops);
126
- let lowered = forms::lowerLoop(&loops[0], &|_| None);
127
- assert!(!lowered.is_empty());
128
- }