@shd101wyy/yo 0.1.21 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/skills/yo-core-patterns/core-patterns-cheatsheet.md +2 -0
- package/.github/skills/yo-syntax/syntax-cheatsheet.md +3 -1
- package/out/cjs/index.cjs +560 -548
- package/out/cjs/yo-cli.cjs +710 -698
- package/out/cjs/yo-lsp.cjs +579 -567
- package/out/esm/index.mjs +546 -534
- package/out/types/src/codegen/exprs/drop-dup.d.ts +1 -0
- package/out/types/src/evaluator/context.d.ts +6 -0
- package/out/types/src/evaluator/trait-checking.d.ts +11 -1
- package/out/types/src/evaluator/values/impl.d.ts +1 -0
- package/out/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/scripts/install.ps1 +143 -0
- package/scripts/install.sh +139 -0
- package/std/cli/arg_parser.yo +148 -24
- package/std/collections/array_list.yo +77 -0
- package/std/collections/hash_map.yo +87 -0
- package/std/collections/hash_set.yo +44 -0
- package/std/collections/ordered_map.yo +244 -0
- package/std/{process.yo → env.yo} +27 -78
- package/std/fs/temp.yo +2 -1
- package/std/{glob/index.yo → glob.yo} +2 -2
- package/std/{log/index.yo → log.yo} +3 -3
- package/std/os/env.yo +2 -1
- package/std/prelude.yo +404 -3
- package/std/process/command.yo +303 -0
- package/std/process/index.yo +45 -0
- package/std/string/index.yo +3 -1
- package/std/string/string.yo +245 -1
- package/std/string/string_builder.yo +173 -0
|
@@ -673,6 +673,32 @@ impl(forall(K : Type, V : Type), HashMap(K, V), Index(K)(
|
|
|
673
673
|
})
|
|
674
674
|
));
|
|
675
675
|
|
|
676
|
+
/// Clone implementation for HashMap — deep-clones all key-value entries.
|
|
677
|
+
impl(forall(K : Type, V : Type),
|
|
678
|
+
where(K <: (Clone, Eq(K), Hash), V <: Clone),
|
|
679
|
+
HashMap(K, V),
|
|
680
|
+
Clone(
|
|
681
|
+
clone : (fn(self: *(Self)) -> Self)(
|
|
682
|
+
{
|
|
683
|
+
result := Self.new();
|
|
684
|
+
it := self.iter();
|
|
685
|
+
running := true;
|
|
686
|
+
while running, {
|
|
687
|
+
nxt := it.next();
|
|
688
|
+
match(nxt,
|
|
689
|
+
.None => { running = false; },
|
|
690
|
+
.Some(bucket_ptr) => {
|
|
691
|
+
k := (&(bucket_ptr.*.key)).clone();
|
|
692
|
+
v := (&(bucket_ptr.*.value)).clone();
|
|
693
|
+
result.set(k, v);
|
|
694
|
+
}
|
|
695
|
+
);
|
|
696
|
+
};
|
|
697
|
+
result
|
|
698
|
+
}
|
|
699
|
+
)
|
|
700
|
+
));
|
|
701
|
+
|
|
676
702
|
export
|
|
677
703
|
HashMap,
|
|
678
704
|
HashMapError,
|
|
@@ -682,3 +708,64 @@ export
|
|
|
682
708
|
HashMapKeys,
|
|
683
709
|
HashMapValues
|
|
684
710
|
;
|
|
711
|
+
|
|
712
|
+
// =============================================================================
|
|
713
|
+
// hash_map! literal macro
|
|
714
|
+
// =============================================================================
|
|
715
|
+
|
|
716
|
+
/// Internal helper: build a list of `tmp.set(k, v).unwrap()` statements from
|
|
717
|
+
/// a sequence of `k => v` arrow expressions.
|
|
718
|
+
__hash_map_build_sets :: (fn(comptime(tmp) : Expr, comptime(entries) : ExprList) -> comptime(ExprList))(
|
|
719
|
+
cond(
|
|
720
|
+
(entries.len() == usize(0)) => ExprList(),
|
|
721
|
+
true => {
|
|
722
|
+
first :: entries.car();
|
|
723
|
+
rest :: entries.cdr();
|
|
724
|
+
__ :: comptime_assert(first.is_fn_call(), "hash_map entries must use 'k => v'");
|
|
725
|
+
callee :: first.get_callee();
|
|
726
|
+
__1 :: comptime_assert((callee.is_atom() && (callee == quote(=>))), "hash_map entries must use 'k => v'");
|
|
727
|
+
pair :: first.get_args();
|
|
728
|
+
__2 :: comptime_assert((pair.len() == usize(2)), "hash_map entries must use 'k => v'");
|
|
729
|
+
key :: pair.car();
|
|
730
|
+
val :: pair.cdr().car();
|
|
731
|
+
stmt :: quote((unquote(tmp).set(unquote(key), unquote(val)).unwrap()));
|
|
732
|
+
stmt.cons(recur(tmp, rest))
|
|
733
|
+
}
|
|
734
|
+
)
|
|
735
|
+
);
|
|
736
|
+
|
|
737
|
+
/// `hash_map` macro — construct a `HashMap(K, V)` literal.
|
|
738
|
+
///
|
|
739
|
+
/// `K` and `V` are inferred from the first entry's key and value via `typeof`,
|
|
740
|
+
/// so at least one entry is required.
|
|
741
|
+
///
|
|
742
|
+
/// # Example
|
|
743
|
+
/// ```rust
|
|
744
|
+
/// m := hash_map(`a` => i32(1), `b` => i32(2));
|
|
745
|
+
/// ```
|
|
746
|
+
hash_map :: (fn(...(quote(entries))) -> unquote(Expr)) {
|
|
747
|
+
__ :: comptime_assert((entries.len() > usize(0)),
|
|
748
|
+
"hash_map requires at least one 'k => v' entry to infer K and V");
|
|
749
|
+
first :: entries.car();
|
|
750
|
+
__1 :: comptime_assert(first.is_fn_call(), "hash_map entries must use 'k => v'");
|
|
751
|
+
callee :: first.get_callee();
|
|
752
|
+
__2 :: comptime_assert((callee.is_atom() && (callee == quote(=>))), "hash_map entries must use 'k => v'");
|
|
753
|
+
pair :: first.get_args();
|
|
754
|
+
__3 :: comptime_assert((pair.len() == usize(2)), "hash_map entries must use 'k => v'");
|
|
755
|
+
first_key :: pair.car();
|
|
756
|
+
first_val :: pair.cdr().car();
|
|
757
|
+
rest :: entries.cdr();
|
|
758
|
+
tmp :: gensym("hash_map");
|
|
759
|
+
k_tmp :: gensym("hash_map_k0");
|
|
760
|
+
v_tmp :: gensym("hash_map_v0");
|
|
761
|
+
rest_sets :: __hash_map_build_sets(tmp, rest);
|
|
762
|
+
quote {
|
|
763
|
+
unquote(k_tmp) := unquote(first_key);
|
|
764
|
+
unquote(v_tmp) := unquote(first_val);
|
|
765
|
+
unquote(tmp) := HashMap(typeof(unquote(k_tmp)), typeof(unquote(v_tmp))).new();
|
|
766
|
+
unquote(tmp).set(unquote(k_tmp), unquote(v_tmp)).unwrap();
|
|
767
|
+
unquote_splicing(rest_sets);
|
|
768
|
+
unquote(tmp)
|
|
769
|
+
}
|
|
770
|
+
};
|
|
771
|
+
export hash_map;
|
|
@@ -833,3 +833,47 @@ export
|
|
|
833
833
|
HashSetIter,
|
|
834
834
|
HashSetIterPtr
|
|
835
835
|
;
|
|
836
|
+
|
|
837
|
+
// =============================================================================
|
|
838
|
+
// hash_set! literal macro
|
|
839
|
+
// =============================================================================
|
|
840
|
+
|
|
841
|
+
/// Internal helper: build a list of `tmp.add(elem).unwrap()` statements from `elems`.
|
|
842
|
+
__hash_set_build_adds :: (fn(comptime(tmp) : Expr, comptime(elems) : ExprList) -> comptime(ExprList))(
|
|
843
|
+
cond(
|
|
844
|
+
(elems.len() == usize(0)) => ExprList(),
|
|
845
|
+
true => {
|
|
846
|
+
first :: elems.car();
|
|
847
|
+
rest :: elems.cdr();
|
|
848
|
+
stmt :: quote((unquote(tmp).add(unquote(first)).unwrap()));
|
|
849
|
+
stmt.cons(recur(tmp, rest))
|
|
850
|
+
}
|
|
851
|
+
)
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
/// `hash_set` macro — construct a `HashSet(T)` literal.
|
|
855
|
+
///
|
|
856
|
+
/// `T` is inferred from the first element via `typeof`, so at least one
|
|
857
|
+
/// element is required.
|
|
858
|
+
///
|
|
859
|
+
/// # Example
|
|
860
|
+
/// ```rust
|
|
861
|
+
/// s := hash_set(i32(1), i32(2), i32(3));
|
|
862
|
+
/// ```
|
|
863
|
+
hash_set :: (fn(...(quote(elems))) -> unquote(Expr)) {
|
|
864
|
+
__ :: comptime_assert((elems.len() > usize(0)),
|
|
865
|
+
"hash_set requires at least one element to infer the element type");
|
|
866
|
+
first :: elems.car();
|
|
867
|
+
rest :: elems.cdr();
|
|
868
|
+
tmp :: gensym("hash_set");
|
|
869
|
+
first_tmp :: gensym("hash_set_first");
|
|
870
|
+
rest_adds :: __hash_set_build_adds(tmp, rest);
|
|
871
|
+
quote {
|
|
872
|
+
unquote(first_tmp) := unquote(first);
|
|
873
|
+
unquote(tmp) := HashSet(typeof(unquote(first_tmp))).new();
|
|
874
|
+
unquote(tmp).add(unquote(first_tmp)).unwrap();
|
|
875
|
+
unquote_splicing(rest_adds);
|
|
876
|
+
unquote(tmp)
|
|
877
|
+
}
|
|
878
|
+
};
|
|
879
|
+
export hash_set;
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
//! Insertion-ordered map.
|
|
2
|
+
//!
|
|
3
|
+
//! `OrderedMap(K, V)` preserves the order in which keys were first inserted,
|
|
4
|
+
//! similar to JavaScript's `Map` or Python's `dict` (≥ 3.7). Backed by a
|
|
5
|
+
//! `HashMap` for O(1) average-time lookup and an `ArrayList` of keys for
|
|
6
|
+
//! deterministic iteration order.
|
|
7
|
+
//!
|
|
8
|
+
//! # Complexity
|
|
9
|
+
//!
|
|
10
|
+
//! - `set` (new key): O(1) amortized
|
|
11
|
+
//! - `set` (existing key): O(1) — value updated in place, order unchanged
|
|
12
|
+
//! - `get` / `contains_key`: O(1) average
|
|
13
|
+
//! - `remove`: O(n) — must rebuild the key order list to skip the removed key
|
|
14
|
+
//! - `iter` / `keys` / `values`: O(n), in insertion order
|
|
15
|
+
//!
|
|
16
|
+
//! # Example
|
|
17
|
+
//!
|
|
18
|
+
//! ```rust
|
|
19
|
+
//! { OrderedMap } :: import "std/collections/ordered_map";
|
|
20
|
+
//!
|
|
21
|
+
//! m := OrderedMap(String, i32).new();
|
|
22
|
+
//! m.set(`alpha`, i32(1));
|
|
23
|
+
//! m.set(`beta`, i32(2));
|
|
24
|
+
//! m.set(`gamma`, i32(3));
|
|
25
|
+
//!
|
|
26
|
+
//! // Iteration yields keys in insertion order.
|
|
27
|
+
//! it := m.keys();
|
|
28
|
+
//! while true, {
|
|
29
|
+
//! match(it.next(),
|
|
30
|
+
//! .Some(k) => println(k),
|
|
31
|
+
//! .None => break
|
|
32
|
+
//! );
|
|
33
|
+
//! };
|
|
34
|
+
//! ```
|
|
35
|
+
|
|
36
|
+
open import "./array_list";
|
|
37
|
+
open import "./hash_map";
|
|
38
|
+
|
|
39
|
+
/// Ordered map preserving insertion order of keys.
|
|
40
|
+
/// Keys must implement `Eq` and `Hash`.
|
|
41
|
+
OrderedMap :: (fn(
|
|
42
|
+
comptime(K) : Type,
|
|
43
|
+
comptime(V) : Type,
|
|
44
|
+
where(K <: (Eq(K), Hash))
|
|
45
|
+
) -> comptime(Type))
|
|
46
|
+
object(
|
|
47
|
+
_map : HashMap(K, V),
|
|
48
|
+
_order : ArrayList(K)
|
|
49
|
+
)
|
|
50
|
+
;
|
|
51
|
+
|
|
52
|
+
impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), OrderedMap(K, V),
|
|
53
|
+
/// Create an empty `OrderedMap`.
|
|
54
|
+
new : (fn() -> Self)(
|
|
55
|
+
Self(
|
|
56
|
+
_map: HashMap(K, V).new(),
|
|
57
|
+
_order: ArrayList(K).new()
|
|
58
|
+
)
|
|
59
|
+
),
|
|
60
|
+
|
|
61
|
+
/// Number of entries.
|
|
62
|
+
len : (fn(self : Self) -> usize)(self._map.len()),
|
|
63
|
+
|
|
64
|
+
/// Whether the map is empty.
|
|
65
|
+
is_empty : (fn(self : Self) -> bool)(self._map.is_empty()),
|
|
66
|
+
|
|
67
|
+
/// Returns `true` if `key` is present.
|
|
68
|
+
contains_key : (fn(self : Self, key : K) -> bool)(self._map.contains_key(key)),
|
|
69
|
+
|
|
70
|
+
/// Returns the value for `key`, or `.None`.
|
|
71
|
+
get : (fn(self : Self, key : K) -> Option(V))(self._map.get(key)),
|
|
72
|
+
|
|
73
|
+
/// Insert or update `key -> value`.
|
|
74
|
+
///
|
|
75
|
+
/// - For a new key, appends `key` to the insertion order.
|
|
76
|
+
/// - For an existing key, only updates the value; order is preserved.
|
|
77
|
+
///
|
|
78
|
+
/// Returns `Ok(.Some(old_value))` if key already existed, `Ok(.None)` for
|
|
79
|
+
/// a new key, or `.Err(_)` on allocation failure.
|
|
80
|
+
set : (fn(self : Self, key : K, value : V) -> Result(Option(V), HashMapError))({
|
|
81
|
+
is_new := !(self._map.contains_key(key));
|
|
82
|
+
result := self._map.set(key, value);
|
|
83
|
+
cond(
|
|
84
|
+
is_new => match(result,
|
|
85
|
+
.Ok(_) => { self._order.push(key); },
|
|
86
|
+
.Err(_) => ()
|
|
87
|
+
),
|
|
88
|
+
true => ()
|
|
89
|
+
);
|
|
90
|
+
return result;
|
|
91
|
+
}),
|
|
92
|
+
|
|
93
|
+
/// Remove `key`. Returns the removed value or `.None`.
|
|
94
|
+
/// O(n) — rebuilds the key order list to drop the removed key.
|
|
95
|
+
remove : (fn(self : Self, key : K) -> Option(V))({
|
|
96
|
+
removed := self._map.remove(key);
|
|
97
|
+
match(removed,
|
|
98
|
+
.Some(_) => {
|
|
99
|
+
new_order := ArrayList(K).new();
|
|
100
|
+
i := usize(0);
|
|
101
|
+
n := self._order.len();
|
|
102
|
+
while (i < n), (i = (i + usize(1))), {
|
|
103
|
+
match(self._order.get(i),
|
|
104
|
+
.Some(k) => {
|
|
105
|
+
cond(
|
|
106
|
+
(k == key) => (),
|
|
107
|
+
true => { new_order.push(k); }
|
|
108
|
+
);
|
|
109
|
+
},
|
|
110
|
+
.None => ()
|
|
111
|
+
);
|
|
112
|
+
};
|
|
113
|
+
self._order = new_order;
|
|
114
|
+
},
|
|
115
|
+
.None => ()
|
|
116
|
+
);
|
|
117
|
+
return removed;
|
|
118
|
+
}),
|
|
119
|
+
|
|
120
|
+
/// Remove all entries.
|
|
121
|
+
clear : (fn(self : Self) -> unit)({
|
|
122
|
+
self._map.clear();
|
|
123
|
+
self._order = ArrayList(K).new();
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
/// Iterator over the keys of an `OrderedMap` in insertion order.
|
|
128
|
+
OrderedMapKeys :: (fn(
|
|
129
|
+
comptime(K) : Type,
|
|
130
|
+
comptime(V) : Type,
|
|
131
|
+
where(K <: (Eq(K), Hash))
|
|
132
|
+
) -> comptime(Type))
|
|
133
|
+
object(
|
|
134
|
+
_map : OrderedMap(K, V),
|
|
135
|
+
_index : usize
|
|
136
|
+
)
|
|
137
|
+
;
|
|
138
|
+
|
|
139
|
+
impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), OrderedMapKeys(K, V),
|
|
140
|
+
next : (fn(self : *(Self)) -> Option(K))({
|
|
141
|
+
cond(
|
|
142
|
+
(self._index >= self._map._order.len()) => { return .None; },
|
|
143
|
+
true => ()
|
|
144
|
+
);
|
|
145
|
+
k := self._map._order.get(self._index);
|
|
146
|
+
self._index = (self._index + usize(1));
|
|
147
|
+
return k;
|
|
148
|
+
})
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
/// Iterator over the values of an `OrderedMap` in insertion order.
|
|
152
|
+
OrderedMapValues :: (fn(
|
|
153
|
+
comptime(K) : Type,
|
|
154
|
+
comptime(V) : Type,
|
|
155
|
+
where(K <: (Eq(K), Hash))
|
|
156
|
+
) -> comptime(Type))
|
|
157
|
+
object(
|
|
158
|
+
_map : OrderedMap(K, V),
|
|
159
|
+
_index : usize
|
|
160
|
+
)
|
|
161
|
+
;
|
|
162
|
+
|
|
163
|
+
impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), OrderedMapValues(K, V),
|
|
164
|
+
next : (fn(self : *(Self)) -> Option(V))({
|
|
165
|
+
cond(
|
|
166
|
+
(self._index >= self._map._order.len()) => { return .None; },
|
|
167
|
+
true => ()
|
|
168
|
+
);
|
|
169
|
+
match(self._map._order.get(self._index),
|
|
170
|
+
.Some(k) => {
|
|
171
|
+
self._index = (self._index + usize(1));
|
|
172
|
+
return self._map._map.get(k);
|
|
173
|
+
},
|
|
174
|
+
.None => { return .None; }
|
|
175
|
+
);
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
/// Key-value pair yielded by `OrderedMapIter`.
|
|
180
|
+
OrderedMapEntry :: (fn(
|
|
181
|
+
comptime(K) : Type,
|
|
182
|
+
comptime(V) : Type
|
|
183
|
+
) -> comptime(Type))
|
|
184
|
+
struct(
|
|
185
|
+
key : K,
|
|
186
|
+
value : V
|
|
187
|
+
)
|
|
188
|
+
;
|
|
189
|
+
|
|
190
|
+
/// Iterator over `(key, value)` pairs of an `OrderedMap` in insertion order.
|
|
191
|
+
OrderedMapIter :: (fn(
|
|
192
|
+
comptime(K) : Type,
|
|
193
|
+
comptime(V) : Type,
|
|
194
|
+
where(K <: (Eq(K), Hash))
|
|
195
|
+
) -> comptime(Type))
|
|
196
|
+
object(
|
|
197
|
+
_map : OrderedMap(K, V),
|
|
198
|
+
_index : usize
|
|
199
|
+
)
|
|
200
|
+
;
|
|
201
|
+
|
|
202
|
+
impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), OrderedMapIter(K, V),
|
|
203
|
+
next : (fn(self : *(Self)) -> Option(OrderedMapEntry(K, V)))({
|
|
204
|
+
cond(
|
|
205
|
+
(self._index >= self._map._order.len()) => { return .None; },
|
|
206
|
+
true => ()
|
|
207
|
+
);
|
|
208
|
+
match(self._map._order.get(self._index),
|
|
209
|
+
.Some(k) => {
|
|
210
|
+
self._index = (self._index + usize(1));
|
|
211
|
+
match(self._map._map.get(k),
|
|
212
|
+
.Some(v) => { return .Some(OrderedMapEntry(K, V)(key: k, value: v)); },
|
|
213
|
+
.None => { return .None; }
|
|
214
|
+
);
|
|
215
|
+
},
|
|
216
|
+
.None => { return .None; }
|
|
217
|
+
);
|
|
218
|
+
})
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
impl(forall(K : Type, V : Type), where(K <: (Eq(K), Hash)), OrderedMap(K, V),
|
|
222
|
+
/// Iterator over keys in insertion order.
|
|
223
|
+
keys : (fn(self : Self) -> OrderedMapKeys(K, V))(
|
|
224
|
+
OrderedMapKeys(K, V)(_map: self, _index: usize(0))
|
|
225
|
+
),
|
|
226
|
+
|
|
227
|
+
/// Iterator over values in insertion order.
|
|
228
|
+
values : (fn(self : Self) -> OrderedMapValues(K, V))(
|
|
229
|
+
OrderedMapValues(K, V)(_map: self, _index: usize(0))
|
|
230
|
+
),
|
|
231
|
+
|
|
232
|
+
/// Iterator over `(key, value)` pairs in insertion order.
|
|
233
|
+
iter : (fn(self : Self) -> OrderedMapIter(K, V))(
|
|
234
|
+
OrderedMapIter(K, V)(_map: self, _index: usize(0))
|
|
235
|
+
)
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
export
|
|
239
|
+
OrderedMap,
|
|
240
|
+
OrderedMapEntry,
|
|
241
|
+
OrderedMapKeys,
|
|
242
|
+
OrderedMapValues,
|
|
243
|
+
OrderedMapIter
|
|
244
|
+
;
|
|
@@ -1,43 +1,16 @@
|
|
|
1
|
-
//! Process
|
|
1
|
+
//! Process environment: command-line arguments, environment variables,
|
|
2
|
+
//! and current working directory.
|
|
3
|
+
//!
|
|
4
|
+
//! Mirrors the layout of Rust's `std::env`. Platform/architecture detection
|
|
5
|
+
//! and child-process spawning live in `std/process`.
|
|
2
6
|
|
|
3
7
|
open import "./collections/array_list";
|
|
4
8
|
open import "./string";
|
|
5
9
|
open import "./path";
|
|
10
|
+
{ platform, Platform } :: import "./process";
|
|
6
11
|
{ GlobalAllocator } :: import "./allocator";
|
|
7
12
|
{ malloc, free } :: GlobalAllocator;
|
|
8
13
|
|
|
9
|
-
/// Current target platform as a compile-time string.
|
|
10
|
-
/// One of: "linux", "macos", "windows", "freebsd", "emscripten", "wasi".
|
|
11
|
-
platform :: __yo_process_platform();
|
|
12
|
-
export platform;
|
|
13
|
-
|
|
14
|
-
/// Platform constants for compile-time platform comparisons.
|
|
15
|
-
Platform :: {
|
|
16
|
-
Linux : "linux",
|
|
17
|
-
Macos : "macos",
|
|
18
|
-
Windows : "windows",
|
|
19
|
-
FreeBSD : "freebsd",
|
|
20
|
-
Emscripten : "emscripten",
|
|
21
|
-
Wasi : "wasi"
|
|
22
|
-
};
|
|
23
|
-
export Platform;
|
|
24
|
-
|
|
25
|
-
/// Current target architecture as a compile-time string.
|
|
26
|
-
/// One of: "x86_64", "aarch64", "x86", "arm", "wasm32".
|
|
27
|
-
arch :: __yo_process_arch();
|
|
28
|
-
export arch;
|
|
29
|
-
|
|
30
|
-
/// Architecture constants for compile-time architecture comparisons.
|
|
31
|
-
Arch :: {
|
|
32
|
-
X86_64 : "x86_64",
|
|
33
|
-
Aarch64 : "aarch64",
|
|
34
|
-
X86 : "x86",
|
|
35
|
-
Arm : "arm",
|
|
36
|
-
Wasm32 : "wasm32"
|
|
37
|
-
};
|
|
38
|
-
export Arch;
|
|
39
|
-
|
|
40
|
-
|
|
41
14
|
// === Low-level C interface ===
|
|
42
15
|
|
|
43
16
|
extern "Yo",
|
|
@@ -71,11 +44,11 @@ export argv;
|
|
|
71
44
|
|
|
72
45
|
/// Get command-line arguments as an `ArrayList(String)`.
|
|
73
46
|
/// The first element is the program name.
|
|
74
|
-
args :: (fn() -> ArrayList(String)) {
|
|
47
|
+
args :: (fn() -> ArrayList(String)) {
|
|
75
48
|
raw := raw_args();
|
|
76
49
|
i := usize(0);
|
|
77
50
|
len := usize(argc());
|
|
78
|
-
|
|
51
|
+
|
|
79
52
|
result := ArrayList(String).with_capacity(len);
|
|
80
53
|
|
|
81
54
|
while i < len, {
|
|
@@ -94,7 +67,7 @@ env :: impl {
|
|
|
94
67
|
|
|
95
68
|
/// Get the value of an environment variable by name.
|
|
96
69
|
/// Returns `.None` if the variable is not set.
|
|
97
|
-
get :: (fn(name : String) -> Option(String)) {
|
|
70
|
+
get :: (fn(name : String) -> Option(String)) {
|
|
98
71
|
name_cstr := name.to_cstr().ptr().unwrap();
|
|
99
72
|
val_ptr := getenv(*(char)(name_cstr));
|
|
100
73
|
match(val_ptr,
|
|
@@ -109,28 +82,24 @@ env :: impl {
|
|
|
109
82
|
set :: (fn(name : String, value : String, (overwrite : bool) ?= true) -> bool) {
|
|
110
83
|
name_cstr := name.to_cstr().ptr().unwrap();
|
|
111
84
|
value_cstr := value.to_cstr().ptr().unwrap();
|
|
112
|
-
|
|
85
|
+
|
|
113
86
|
result := cond(
|
|
114
87
|
(platform == Platform.Windows) => {
|
|
115
|
-
// Windows: use _putenv_s (doesn't have overwrite parameter)
|
|
116
88
|
{ _putenv_s } :: import "./libc/windows";
|
|
117
|
-
|
|
89
|
+
|
|
118
90
|
cond(
|
|
119
91
|
overwrite =>
|
|
120
|
-
// Always set on Windows (no native "don't overwrite" option)
|
|
121
92
|
_putenv_s(*(char)(name_cstr), *(char)(value_cstr)),
|
|
122
93
|
true => {
|
|
123
|
-
// Check if variable exists first
|
|
124
94
|
existing := getenv(*(char)(name_cstr));
|
|
125
95
|
match(existing,
|
|
126
|
-
.Some(_) => int(0),
|
|
96
|
+
.Some(_) => int(0),
|
|
127
97
|
.None => _putenv_s(*(char)(name_cstr), *(char)(value_cstr))
|
|
128
98
|
)
|
|
129
99
|
}
|
|
130
100
|
)
|
|
131
101
|
},
|
|
132
102
|
true => {
|
|
133
|
-
// Unix: use setenv
|
|
134
103
|
overwrite_int := cond(
|
|
135
104
|
overwrite => int(1),
|
|
136
105
|
true => int(0)
|
|
@@ -138,7 +107,7 @@ env :: impl {
|
|
|
138
107
|
setenv(*(char)(name_cstr), *(char)(value_cstr), overwrite_int)
|
|
139
108
|
}
|
|
140
109
|
);
|
|
141
|
-
|
|
110
|
+
|
|
142
111
|
return result == int(0);
|
|
143
112
|
};
|
|
144
113
|
export set;
|
|
@@ -150,28 +119,22 @@ export env;
|
|
|
150
119
|
cwd :: (fn() -> Result(Path, String))(
|
|
151
120
|
cond(
|
|
152
121
|
(platform == Platform.Windows) => {
|
|
153
|
-
// Windows implementation using GetCurrentDirectoryW
|
|
154
122
|
{ GetCurrentDirectoryW, WideCharToMultiByte, CP_UTF8, WCHAR, DWORD } :: import "./libc/windows";
|
|
155
|
-
|
|
156
|
-
// First, get the required buffer size
|
|
123
|
+
|
|
157
124
|
required_size := GetCurrentDirectoryW(ulong(0), .None);
|
|
158
125
|
cond(
|
|
159
126
|
(required_size == ulong(0)) => .Err(`Failed to get current working directory on Windows`),
|
|
160
127
|
true => {
|
|
161
|
-
// Allocate buffer for wide characters
|
|
162
128
|
wbuf := *(WCHAR)(malloc((usize(required_size) * usize(2))).unwrap());
|
|
163
|
-
|
|
164
|
-
// Get the current directory in UTF-16
|
|
129
|
+
|
|
165
130
|
actual_size := GetCurrentDirectoryW(required_size, .Some(wbuf));
|
|
166
|
-
|
|
131
|
+
|
|
167
132
|
cond(
|
|
168
133
|
(actual_size == ulong(0)) => {
|
|
169
134
|
free(.Some(*(void)(wbuf)));
|
|
170
135
|
.Err(`Failed to get current working directory on Windows`)
|
|
171
136
|
},
|
|
172
137
|
true => {
|
|
173
|
-
// Convert UTF-16 to UTF-8
|
|
174
|
-
// First, get the required buffer size for UTF-8
|
|
175
138
|
utf8_size := WideCharToMultiByte(
|
|
176
139
|
CP_UTF8,
|
|
177
140
|
ulong(0),
|
|
@@ -182,17 +145,15 @@ cwd :: (fn() -> Result(Path, String))(
|
|
|
182
145
|
.None,
|
|
183
146
|
.None
|
|
184
147
|
);
|
|
185
|
-
|
|
148
|
+
|
|
186
149
|
cond(
|
|
187
150
|
(utf8_size <= i32(0)) => {
|
|
188
151
|
free(.Some(*(void)(wbuf)));
|
|
189
152
|
.Err(`Failed to convert directory path to UTF-8`)
|
|
190
153
|
},
|
|
191
154
|
true => {
|
|
192
|
-
// Allocate buffer for UTF-8 string
|
|
193
155
|
utf8_buf := *(u8)(malloc((usize(utf8_size) + usize(1))).unwrap());
|
|
194
|
-
|
|
195
|
-
// Convert to UTF-8
|
|
156
|
+
|
|
196
157
|
result := WideCharToMultiByte(
|
|
197
158
|
CP_UTF8,
|
|
198
159
|
ulong(0),
|
|
@@ -203,16 +164,15 @@ cwd :: (fn() -> Result(Path, String))(
|
|
|
203
164
|
.None,
|
|
204
165
|
.None
|
|
205
166
|
);
|
|
206
|
-
|
|
167
|
+
|
|
207
168
|
free(.Some(*(void)(wbuf)));
|
|
208
|
-
|
|
169
|
+
|
|
209
170
|
cond(
|
|
210
171
|
(result <= i32(0)) => {
|
|
211
172
|
free(.Some(*(void)(utf8_buf)));
|
|
212
173
|
.Err(`Failed to convert directory path to UTF-8`)
|
|
213
174
|
},
|
|
214
175
|
true => {
|
|
215
|
-
// Add null terminator
|
|
216
176
|
(utf8_buf &+ usize(result)).* = u8(0);
|
|
217
177
|
cwd_str := String.from_cstr(utf8_buf).unwrap();
|
|
218
178
|
free(.Some(*(void)(utf8_buf)));
|
|
@@ -227,15 +187,13 @@ cwd :: (fn() -> Result(Path, String))(
|
|
|
227
187
|
)
|
|
228
188
|
},
|
|
229
189
|
true => {
|
|
230
|
-
// Unix-like implementation using getcwd
|
|
231
190
|
{ getcwd } :: import "./libc/unistd";
|
|
232
|
-
|
|
233
|
-
// Allocate a buffer for the path (PATH_MAX is typically 4096)
|
|
191
|
+
|
|
234
192
|
buf_size := usize(4096);
|
|
235
193
|
buf := *(char)(malloc(buf_size).unwrap());
|
|
236
|
-
|
|
194
|
+
|
|
237
195
|
result_ptr := getcwd(buf, buf_size);
|
|
238
|
-
|
|
196
|
+
|
|
239
197
|
match(result_ptr,
|
|
240
198
|
.None => {
|
|
241
199
|
free(.Some(*(void)(buf)));
|
|
@@ -257,26 +215,24 @@ export cwd;
|
|
|
257
215
|
chdir :: (fn(path: Path) -> Result(unit, String))(
|
|
258
216
|
cond(
|
|
259
217
|
(platform == Platform.Windows) => {
|
|
260
|
-
// Windows implementation using SetCurrentDirectoryA
|
|
261
218
|
{ SetCurrentDirectoryA, BOOL } :: import "./libc/windows";
|
|
262
|
-
|
|
219
|
+
|
|
263
220
|
path_str := path.to_string();
|
|
264
221
|
path_cstr := path_str.to_cstr().ptr().unwrap();
|
|
265
222
|
result := SetCurrentDirectoryA(*(char)(path_cstr));
|
|
266
|
-
|
|
223
|
+
|
|
267
224
|
cond(
|
|
268
225
|
(result == int(0)) => .Err(`Failed to change directory to: ${path_str}`),
|
|
269
226
|
true => .Ok(())
|
|
270
227
|
)
|
|
271
228
|
},
|
|
272
229
|
true => {
|
|
273
|
-
// Unix-like implementation using chdir
|
|
274
230
|
{ chdir : unix_chdir } :: import "./libc/unistd";
|
|
275
|
-
|
|
231
|
+
|
|
276
232
|
path_str := path.to_string();
|
|
277
233
|
path_cstr := path_str.to_cstr().ptr().unwrap();
|
|
278
234
|
result := unix_chdir(*(char)(path_cstr));
|
|
279
|
-
|
|
235
|
+
|
|
280
236
|
cond(
|
|
281
237
|
(result != int(0)) => .Err(`Failed to change directory to: ${path_str}`),
|
|
282
238
|
true => .Ok(())
|
|
@@ -285,10 +241,3 @@ chdir :: (fn(path: Path) -> Result(unit, String))(
|
|
|
285
241
|
)
|
|
286
242
|
);
|
|
287
243
|
export chdir;
|
|
288
|
-
|
|
289
|
-
/// Exit the process with the given status code.
|
|
290
|
-
exit :: (fn(code : usize) -> unit) {
|
|
291
|
-
{ exit : _exit } :: import "./libc/stdlib";
|
|
292
|
-
_exit(int(code));
|
|
293
|
-
};
|
|
294
|
-
export exit;
|
package/std/fs/temp.yo
CHANGED
|
@@ -30,7 +30,8 @@ _dir_mod :: import "./dir";
|
|
|
30
30
|
IO_temp :: import "../sys/temp";
|
|
31
31
|
IO_file :: import "../sys/file";
|
|
32
32
|
{ memcpy } :: import "../libc/string";
|
|
33
|
-
{ platform, Platform
|
|
33
|
+
{ platform, Platform } :: import "../process";
|
|
34
|
+
{ env } :: import "../env";
|
|
34
35
|
|
|
35
36
|
_default_tmp_dir :: (fn() -> String)(
|
|
36
37
|
cond(
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
//! Glob pattern matching for file paths.
|
|
2
2
|
|
|
3
|
-
open import "
|
|
4
|
-
{ ArrayList } :: import "
|
|
3
|
+
open import "./string";
|
|
4
|
+
{ ArrayList } :: import "./collections/array_list";
|
|
5
5
|
|
|
6
6
|
// Recursive byte-level glob pattern matching
|
|
7
7
|
_glob_match_impl :: (fn(pb: ArrayList(u8), pi: usize, tb: ArrayList(u8), ti: usize) -> bool)({
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
//! Structured logging with configurable level and output destination.
|
|
2
2
|
|
|
3
|
-
open import "
|
|
4
|
-
{ ToString } :: import "
|
|
5
|
-
{ fwrite, stdout, stderr, FILE } :: import "
|
|
3
|
+
open import "./string";
|
|
4
|
+
{ ToString } :: import "./fmt";
|
|
5
|
+
{ fwrite, stdout, stderr, FILE } :: import "./libc/stdio";
|
|
6
6
|
|
|
7
7
|
/// Log severity level.
|
|
8
8
|
Level :: enum(Trace, Debug, Info, Warn, Error);
|
package/std/os/env.yo
CHANGED
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
//! ```
|
|
13
13
|
|
|
14
14
|
open import "../string";
|
|
15
|
-
{ platform, Platform
|
|
15
|
+
{ platform, Platform } :: import "../process";
|
|
16
|
+
{ env } :: import "../env";
|
|
16
17
|
|
|
17
18
|
// ============================================================================
|
|
18
19
|
// Directory helpers
|