1use crate::types::{Effect, StackType, Type};
6
7use super::{Program, Statement, WordDef};
8
9impl Program {
10 pub fn new() -> Self {
11 Program {
12 includes: Vec::new(),
13 unions: Vec::new(),
14 words: Vec::new(),
15 }
16 }
17
18 pub fn find_word(&self, name: &str) -> Option<&WordDef> {
19 self.words.iter().find(|w| w.name == name)
20 }
21
22 pub fn validate_word_calls(&self) -> Result<(), String> {
24 self.validate_word_calls_with_externals(&[])
25 }
26
27 pub fn validate_word_calls_with_externals(
32 &self,
33 external_words: &[&str],
34 ) -> Result<(), String> {
35 let builtins = [
38 "io.write",
40 "io.write-line",
41 "io.read-line",
42 "io.read-n",
43 "int->string",
44 "symbol->string",
45 "string->symbol",
46 "args.count",
48 "args.at",
49 "file.slurp",
51 "file.exists?",
52 "file.for-each-line",
53 "file.spit",
54 "file.append",
55 "file.delete",
56 "file.size",
57 "dir.exists?",
59 "dir.make",
60 "dir.delete",
61 "dir.list",
62 "string.concat",
64 "string.length",
65 "string.byte-length",
66 "string.char-at",
67 "string.substring",
68 "char->string",
69 "string.find",
70 "string.split",
71 "string.contains",
72 "string.starts-with",
73 "string.empty?",
74 "string.trim",
75 "string.chomp",
76 "string.to-upper",
77 "string.to-lower",
78 "string.equal?",
79 "string.join",
80 "string.json-escape",
81 "string->int",
82 "symbol.=",
84 "encoding.base64-encode",
86 "encoding.base64-decode",
87 "encoding.base64url-encode",
88 "encoding.base64url-decode",
89 "encoding.hex-encode",
90 "encoding.hex-decode",
91 "crypto.sha256",
93 "crypto.hmac-sha256",
94 "crypto.constant-time-eq",
95 "crypto.random-bytes",
96 "crypto.random-int",
97 "crypto.uuid4",
98 "crypto.aes-gcm-encrypt",
99 "crypto.aes-gcm-decrypt",
100 "crypto.pbkdf2-sha256",
101 "crypto.ed25519-keypair",
102 "crypto.ed25519-sign",
103 "crypto.ed25519-verify",
104 "net.http.get",
106 "net.http.post",
107 "net.http.put",
108 "net.http.delete",
109 "list.make",
111 "list.push",
112 "list.get",
113 "list.set",
114 "list.map",
115 "list.filter",
116 "list.fold",
117 "list.each",
118 "list.length",
119 "list.empty?",
120 "list.reverse",
121 "list.first",
122 "list.last",
123 "map.make",
125 "map.get",
126 "map.set",
127 "map.has?",
128 "map.remove",
129 "map.keys",
130 "map.values",
131 "map.size",
132 "map.empty?",
133 "map.each",
134 "map.fold",
135 "variant.field-count",
137 "variant.tag",
138 "variant.field-at",
139 "variant.append",
140 "variant.first",
141 "variant.last",
142 "variant.init",
143 "variant.make-0",
144 "variant.make-1",
145 "variant.make-2",
146 "variant.make-3",
147 "variant.make-4",
148 "wrap-0",
150 "wrap-1",
151 "wrap-2",
152 "wrap-3",
153 "wrap-4",
154 "i.add",
156 "i.subtract",
157 "i.multiply",
158 "i.divide",
159 "i.modulo",
160 "i.+",
162 "i.-",
163 "i.*",
164 "i./",
165 "i.%",
166 "i.=",
168 "i.<",
169 "i.>",
170 "i.<=",
171 "i.>=",
172 "i.<>",
173 "i.eq",
175 "i.lt",
176 "i.gt",
177 "i.lte",
178 "i.gte",
179 "i.neq",
180 "dup",
182 "drop",
183 "swap",
184 "over",
185 "rot",
186 "nip",
187 "tuck",
188 "2dup",
189 "3drop",
190 "pick",
191 "roll",
192 ">aux",
194 "aux>",
195 "and",
197 "or",
198 "not",
199 "band",
201 "bor",
202 "bxor",
203 "bnot",
204 "i.neg",
205 "negate",
206 "+",
208 "-",
209 "*",
210 "/",
211 "%",
212 "=",
213 "<",
214 ">",
215 "<=",
216 ">=",
217 "<>",
218 "shl",
219 "shr",
220 "popcount",
221 "clz",
222 "ctz",
223 "int-bits",
224 "chan.make",
226 "chan.send",
227 "chan.receive",
228 "chan.close",
229 "chan.yield",
230 "call",
232 "dip",
234 "keep",
235 "bi",
236 "if",
237 "strand.spawn",
238 "strand.weave",
239 "strand.resume",
240 "strand.weave-cancel",
241 "yield",
242 "cond",
243 "net.tcp.listen",
245 "net.tcp.accept",
246 "net.tcp.read",
247 "net.tcp.write",
248 "net.tcp.close",
249 "fd->socket",
251 "socket->fd",
252 "net.udp.bind",
254 "net.udp.send-to",
255 "net.udp.receive-from",
256 "net.udp.close",
257 "os.getenv",
259 "os.home-dir",
260 "os.current-dir",
261 "os.path-exists",
262 "os.path-is-file",
263 "os.path-is-dir",
264 "os.path-join",
265 "os.path-parent",
266 "os.path-filename",
267 "os.exit",
268 "os.name",
269 "os.arch",
270 "signal.trap",
272 "signal.received?",
273 "signal.pending?",
274 "signal.default",
275 "signal.ignore",
276 "signal.clear",
277 "signal.SIGINT",
278 "signal.SIGTERM",
279 "signal.SIGHUP",
280 "signal.SIGPIPE",
281 "signal.SIGUSR1",
282 "signal.SIGUSR2",
283 "signal.SIGCHLD",
284 "signal.SIGALRM",
285 "signal.SIGCONT",
286 "terminal.raw-mode",
288 "terminal.read-char",
289 "terminal.read-char?",
290 "terminal.width",
291 "terminal.height",
292 "terminal.flush",
293 "f.add",
295 "f.subtract",
296 "f.multiply",
297 "f.divide",
298 "f.+",
300 "f.-",
301 "f.*",
302 "f./",
303 "f.=",
305 "f.<",
306 "f.>",
307 "f.<=",
308 "f.>=",
309 "f.<>",
310 "f.eq",
312 "f.lt",
313 "f.gt",
314 "f.lte",
315 "f.gte",
316 "f.neq",
317 "f.sqrt",
319 "f.cbrt",
320 "f.pow",
321 "f.exp",
323 "f.ln",
324 "f.log10",
325 "f.log2",
326 "f.sin",
328 "f.cos",
329 "f.tan",
330 "f.asin",
331 "f.acos",
332 "f.atan",
333 "f.atan2",
334 "f.floor",
336 "f.ceil",
337 "f.round",
338 "f.trunc",
339 "f.pi",
341 "f.e",
342 "f.tau",
343 "int->float",
345 "float->int",
346 "float->string",
347 "string->float",
348 "int.to-bytes-i32-be",
350 "float.to-bytes-f32-be",
351 "test.init",
353 "test.set-name",
354 "test.finish",
355 "test.has-failures",
356 "test.assert",
357 "test.assert-not",
358 "test.assert-eq",
359 "test.assert-eq-str",
360 "test.fail",
361 "test.pass-count",
362 "test.fail-count",
363 "time.now",
365 "time.nanos",
366 "time.sleep-ms",
367 "son.dump",
369 "son.dump-pretty",
370 "stack.dump",
372 "regex.match?",
374 "regex.find",
375 "regex.find-all",
376 "regex.replace",
377 "regex.replace-all",
378 "regex.captures",
379 "regex.split",
380 "regex.valid?",
381 "compress.gzip",
383 "compress.gzip-level",
384 "compress.gunzip",
385 "compress.zstd",
386 "compress.zstd-level",
387 "compress.unzstd",
388 ];
389
390 for word in &self.words {
391 self.validate_statements(&word.body, &word.name, &builtins, external_words)?;
392 }
393
394 Ok(())
395 }
396
397 fn validate_statements(
399 &self,
400 statements: &[Statement],
401 word_name: &str,
402 builtins: &[&str],
403 external_words: &[&str],
404 ) -> Result<(), String> {
405 for statement in statements {
406 match statement {
407 Statement::WordCall { name, .. } => {
408 if builtins.contains(&name.as_str()) {
410 continue;
411 }
412 if self.find_word(name).is_some() {
414 continue;
415 }
416 if external_words.contains(&name.as_str()) {
418 continue;
419 }
420 if let Some(replacement) = v7_renamed_to(name) {
424 return Err(format!(
425 "'{}' was renamed to '{}' in v7.0 (called in word '{}'). \
426 See docs/MIGRATION_7_0.md.",
427 name, replacement, word_name
428 ));
429 }
430 return Err(format!(
432 "Undefined word '{}' called in word '{}'. \
433 Did you forget to define it or misspell a built-in?",
434 name, word_name
435 ));
436 }
437 Statement::If {
438 then_branch,
439 else_branch,
440 span: _,
441 } => {
442 self.validate_statements(then_branch, word_name, builtins, external_words)?;
444 if let Some(eb) = else_branch {
445 self.validate_statements(eb, word_name, builtins, external_words)?;
446 }
447 }
448 Statement::Quotation { body, .. } => {
449 self.validate_statements(body, word_name, builtins, external_words)?;
451 }
452 Statement::Match { arms, span: _ } => {
453 for arm in arms {
455 self.validate_statements(&arm.body, word_name, builtins, external_words)?;
456 }
457 }
458 _ => {} }
460 }
461 Ok(())
462 }
463
464 pub const MAX_VARIANT_FIELDS: usize = 12;
468
469 pub fn generate_constructors(&mut self) -> Result<(), String> {
482 let mut new_words = Vec::new();
483
484 for union_def in &self.unions {
485 for variant in &union_def.variants {
486 let field_count = variant.fields.len();
487
488 if field_count > Self::MAX_VARIANT_FIELDS {
490 return Err(format!(
491 "Variant '{}' in union '{}' has {} fields, but the maximum is {}. \
492 Consider grouping fields into nested union types.",
493 variant.name,
494 union_def.name,
495 field_count,
496 Self::MAX_VARIANT_FIELDS
497 ));
498 }
499
500 let constructor_name = format!("Make-{}", variant.name);
502 let mut input_stack = StackType::RowVar("a".to_string());
503 for field in &variant.fields {
504 let field_type = parse_type_name(&field.type_name);
505 input_stack = input_stack.push(field_type);
506 }
507 let output_stack =
508 StackType::RowVar("a".to_string()).push(Type::Union(union_def.name.clone()));
509 let effect = Effect::new(input_stack, output_stack);
510 let body = vec![
511 Statement::Symbol(variant.name.clone()),
512 Statement::WordCall {
513 name: format!("variant.make-{}", field_count),
514 span: None,
515 },
516 ];
517 new_words.push(WordDef {
518 name: constructor_name,
519 effect: Some(effect),
520 body,
521 source: variant.source.clone(),
522 allowed_lints: vec![],
523 });
524
525 let predicate_name = format!("is-{}?", variant.name);
529 let predicate_input =
530 StackType::RowVar("a".to_string()).push(Type::Union(union_def.name.clone()));
531 let predicate_output = StackType::RowVar("a".to_string()).push(Type::Bool);
532 let predicate_effect = Effect::new(predicate_input, predicate_output);
533 let predicate_body = vec![
534 Statement::WordCall {
535 name: "variant.tag".to_string(),
536 span: None,
537 },
538 Statement::Symbol(variant.name.clone()),
539 Statement::WordCall {
540 name: "symbol.=".to_string(),
541 span: None,
542 },
543 ];
544 new_words.push(WordDef {
545 name: predicate_name,
546 effect: Some(predicate_effect),
547 body: predicate_body,
548 source: variant.source.clone(),
549 allowed_lints: vec![],
550 });
551
552 for (index, field) in variant.fields.iter().enumerate() {
556 let accessor_name = format!("{}-{}", variant.name, field.name);
557 let field_type = parse_type_name(&field.type_name);
558 let accessor_input = StackType::RowVar("a".to_string())
559 .push(Type::Union(union_def.name.clone()));
560 let accessor_output = StackType::RowVar("a".to_string()).push(field_type);
561 let accessor_effect = Effect::new(accessor_input, accessor_output);
562 let accessor_body = vec![
563 Statement::IntLiteral(index as i64),
564 Statement::WordCall {
565 name: "variant.field-at".to_string(),
566 span: None,
567 },
568 ];
569 new_words.push(WordDef {
570 name: accessor_name,
571 effect: Some(accessor_effect),
572 body: accessor_body,
573 source: variant.source.clone(), allowed_lints: vec![],
575 });
576 }
577 }
578 }
579
580 self.words.extend(new_words);
581 Ok(())
582 }
583
584 pub fn fixup_union_types(&mut self) {
593 let union_names: std::collections::HashSet<String> =
595 self.unions.iter().map(|u| u.name.clone()).collect();
596
597 for word in &mut self.words {
599 if let Some(ref mut effect) = word.effect {
600 Self::fixup_stack_type(&mut effect.inputs, &union_names);
601 Self::fixup_stack_type(&mut effect.outputs, &union_names);
602 }
603 }
604 }
605
606 fn fixup_stack_type(stack: &mut StackType, union_names: &std::collections::HashSet<String>) {
608 match stack {
609 StackType::Empty | StackType::RowVar(_) => {}
610 StackType::Cons { rest, top } => {
611 Self::fixup_type(top, union_names);
612 Self::fixup_stack_type(rest, union_names);
613 }
614 }
615 }
616
617 fn fixup_type(ty: &mut Type, union_names: &std::collections::HashSet<String>) {
619 match ty {
620 Type::Var(name) if union_names.contains(name) => {
621 *ty = Type::Union(name.clone());
622 }
623 Type::Quotation(effect) => {
624 Self::fixup_stack_type(&mut effect.inputs, union_names);
625 Self::fixup_stack_type(&mut effect.outputs, union_names);
626 }
627 Type::Closure { effect, captures } => {
628 Self::fixup_stack_type(&mut effect.inputs, union_names);
629 Self::fixup_stack_type(&mut effect.outputs, union_names);
630 for cap in captures {
631 Self::fixup_type(cap, union_names);
632 }
633 }
634 _ => {}
635 }
636 }
637}
638
639fn parse_type_name(name: &str) -> Type {
642 match name {
643 "Int" => Type::Int,
644 "Float" => Type::Float,
645 "Bool" => Type::Bool,
646 "String" => Type::String,
647 "Channel" => Type::Channel,
648 "Socket" => Type::Socket,
649 other => Type::Union(other.to_string()),
650 }
651}
652
653fn v7_renamed_to(name: &str) -> Option<&'static str> {
658 Some(match name {
659 "tcp.listen" => "net.tcp.listen",
660 "tcp.accept" => "net.tcp.accept",
661 "tcp.read" => "net.tcp.read",
662 "tcp.write" => "net.tcp.write",
663 "tcp.close" => "net.tcp.close",
664 "udp.bind" => "net.udp.bind",
665 "udp.send-to" => "net.udp.send-to",
666 "udp.receive-from" => "net.udp.receive-from",
667 "udp.close" => "net.udp.close",
668 "http.get" => "net.http.get",
669 "http.post" => "net.http.post",
670 "http.put" => "net.http.put",
671 "http.delete" => "net.http.delete",
672 "mod" => "i.modulo",
675 _ => return None,
676 })
677}
678
679impl Default for Program {
680 fn default() -> Self {
681 Self::new()
682 }
683}