1use crate::token::{Token, TokenType, BlockType};
20use logicaffeine_base::{Interner, Symbol};
21use super::registry::{TypeRegistry, TypeDef, FieldDef, FieldType, VariantDef};
22use super::policy::{PolicyRegistry, PredicateDef, CapabilityDef, PolicyCondition};
23use super::dependencies::scan_dependencies;
24
25pub struct DiscoveryResult {
27 pub types: TypeRegistry,
28 pub policies: PolicyRegistry,
29}
30
31pub struct DiscoveryPass<'a> {
40 tokens: &'a [Token],
41 pos: usize,
42 interner: &'a mut Interner,
43}
44
45impl<'a> DiscoveryPass<'a> {
46 pub fn new(tokens: &'a [Token], interner: &'a mut Interner) -> Self {
47 Self { tokens, pos: 0, interner }
48 }
49
50 pub fn run(&mut self) -> TypeRegistry {
53 self.run_full().types
54 }
55
56 pub fn run_full(&mut self) -> DiscoveryResult {
58 let mut type_registry = TypeRegistry::with_primitives(self.interner);
59 let mut policy_registry = PolicyRegistry::new();
60
61 while self.pos < self.tokens.len() {
62 if self.check_block_header(BlockType::Definition) {
64 self.advance(); self.scan_definition_block(&mut type_registry);
66 } else if self.check_block_header(BlockType::TypeDef) {
67 self.advance(); self.parse_type_definition_inline(&mut type_registry);
71 } else if self.check_block_header(BlockType::Policy) {
72 self.advance(); self.scan_policy_block(&mut policy_registry);
75 } else if self.check_block_header(BlockType::Requires) {
76 self.advance(); while self.pos < self.tokens.len() {
80 if matches!(self.tokens.get(self.pos), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
81 break;
82 }
83 self.advance();
84 }
85 } else {
86 self.advance();
87 }
88 }
89
90 DiscoveryResult {
91 types: type_registry,
92 policies: policy_registry,
93 }
94 }
95
96 fn check_block_header(&self, expected: BlockType) -> bool {
97 matches!(
98 self.tokens.get(self.pos),
99 Some(Token { kind: TokenType::BlockHeader { block_type }, .. })
100 if *block_type == expected
101 )
102 }
103
104 fn scan_definition_block(&mut self, registry: &mut TypeRegistry) {
105 while self.pos < self.tokens.len() {
107 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
108 break;
109 }
110
111 if self.check_article() {
113 self.try_parse_type_definition(registry);
114 } else {
115 self.advance();
116 }
117 }
118 }
119
120 fn scan_policy_block(&mut self, registry: &mut PolicyRegistry) {
125 while self.pos < self.tokens.len() {
126 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
127 break;
128 }
129
130 if self.check_newline() || self.check_indent() || self.check_dedent() {
132 self.advance();
133 continue;
134 }
135
136 if self.check_article() {
138 self.try_parse_policy_definition(registry);
139 } else {
140 self.advance();
141 }
142 }
143 }
144
145 fn try_parse_policy_definition(&mut self, registry: &mut PolicyRegistry) {
147 self.advance(); let subject_type = match self.consume_noun_or_proper() {
151 Some(sym) => sym,
152 None => return,
153 };
154
155 if self.check_copula() {
157 self.advance(); let predicate_name = match self.consume_noun_or_proper() {
162 Some(sym) => sym,
163 None => return,
164 };
165
166 if !self.check_word("if") {
168 self.skip_to_period();
169 return;
170 }
171 self.advance(); if self.check_colon() {
175 self.advance();
176 }
177 if self.check_newline() {
178 self.advance();
179 }
180 if self.check_indent() {
181 self.advance();
182 }
183
184 let condition = self.parse_policy_condition(subject_type, None);
186
187 registry.register_predicate(PredicateDef {
188 subject_type,
189 predicate_name,
190 condition,
191 });
192
193 self.skip_to_period();
194 } else if self.check_word("can") {
195 self.advance(); let action = match self.peek() {
204 Some(Token { kind: TokenType::Verb { lemma, .. }, .. }) => {
205 let sym = *lemma;
206 self.advance();
207 sym
208 }
209 _ => match self.consume_noun_or_proper() {
210 Some(sym) => sym,
211 None => return,
212 },
213 };
214
215 if self.check_article() {
217 self.advance();
218 }
219
220 let object_type = match self.consume_noun_or_proper() {
222 Some(sym) => sym,
223 None => return,
224 };
225
226 if !self.check_word("if") {
228 self.skip_to_period();
229 return;
230 }
231 self.advance(); if self.check_colon() {
235 self.advance();
236 }
237 if self.check_newline() {
238 self.advance();
239 }
240 if self.check_indent() {
241 self.advance();
242 }
243
244 let condition = self.parse_policy_condition(subject_type, Some(object_type));
245
246 registry.register_capability(CapabilityDef {
247 subject_type,
248 action,
249 object_type,
250 condition,
251 });
252
253 self.skip_policy_definition();
255 } else {
256 self.skip_to_period();
257 }
258 }
259
260 fn parse_policy_condition(&mut self, subject_type: Symbol, object_type: Option<Symbol>) -> PolicyCondition {
263 let mut acc = self.parse_atomic_condition(subject_type, object_type);
267
268 loop {
270 while self.check_newline() {
272 self.advance();
273 }
274
275 if self.check_comma() {
277 self.advance(); while self.check_newline() {
280 self.advance();
281 }
282 }
283
284 if self.check_word("AND") {
285 self.advance();
286 while self.check_newline() {
288 self.advance();
289 }
290 let right = self.parse_atomic_condition(subject_type, object_type);
291 acc = PolicyCondition::And(Box::new(acc), Box::new(right));
292 } else if self.check_word("OR") {
293 self.advance();
294 while self.check_newline() {
296 self.advance();
297 }
298 let right = self.parse_atomic_condition(subject_type, object_type);
299 acc = PolicyCondition::Or(Box::new(acc), Box::new(right));
300 } else {
301 break;
302 }
303 }
304
305 acc
306 }
307
308 fn parse_atomic_condition(&mut self, subject_type: Symbol, object_type: Option<Symbol>) -> PolicyCondition {
310 if self.check_article() {
312 self.advance();
313 }
314
315 let subject_ref = match self.consume_noun_or_proper() {
317 Some(sym) => sym,
318 None => return PolicyCondition::FieldEquals {
319 field: self.interner.intern("unknown"),
320 value: self.interner.intern("unknown"),
321 is_string_literal: false,
322 },
323 };
324
325 if self.check_possessive() {
327 self.advance(); let field = match self.consume_noun_or_proper() {
331 Some(sym) => sym,
332 None => return PolicyCondition::FieldEquals {
333 field: self.interner.intern("unknown"),
334 value: self.interner.intern("unknown"),
335 is_string_literal: false,
336 },
337 };
338
339 if self.check_word("equals") {
341 self.advance();
342
343 let checkpoint = self.pos;
347 if self.check_article() {
348 self.advance();
349 }
350 if let Some(obj_ref) = self.consume_noun_or_proper() {
351 if self.check_possessive() {
352 self.advance(); if let Some(obj_field) = self.consume_noun_or_proper() {
354 return PolicyCondition::SubjectFieldEqualsObjectField {
355 subject_field: field,
356 object: obj_ref,
357 object_field: obj_field,
358 };
359 }
360 }
361 }
362 self.pos = checkpoint;
364 let (value, is_string_literal) = self.consume_value();
365
366 return PolicyCondition::FieldEquals { field, value, is_string_literal };
367 }
368 } else if self.check_copula() {
369 self.advance(); let predicate = match self.consume_noun_or_proper() {
374 Some(sym) => sym,
375 None => return PolicyCondition::FieldEquals {
376 field: self.interner.intern("unknown"),
377 value: self.interner.intern("unknown"),
378 is_string_literal: false,
379 },
380 };
381
382 return PolicyCondition::Predicate {
383 subject: subject_ref,
384 predicate,
385 };
386 } else if self.check_word("equals") {
387 self.advance(); if self.check_article() {
392 self.advance();
393 }
394
395 if let Some(obj_ref) = self.consume_noun_or_proper() {
397 if self.check_possessive() {
398 self.advance(); if let Some(field) = self.consume_noun_or_proper() {
400 return PolicyCondition::ObjectFieldEquals {
401 subject: subject_ref,
402 object: obj_ref,
403 field,
404 };
405 }
406 }
407 }
408 }
409
410 PolicyCondition::FieldEquals {
412 field: self.interner.intern("unknown"),
413 value: self.interner.intern("unknown"),
414 is_string_literal: false,
415 }
416 }
417
418 fn consume_value(&mut self) -> (Symbol, bool) {
420 if let Some(Token { kind: TokenType::StringLiteral(sym), .. }) = self.peek() {
421 let s = *sym;
422 self.advance();
423 (s, true)
424 } else if let Some(sym) = self.consume_noun_or_proper() {
425 (sym, false)
426 } else {
427 (self.interner.intern("unknown"), false)
428 }
429 }
430
431 fn check_possessive(&self) -> bool {
433 matches!(self.peek(), Some(Token { kind: TokenType::Possessive, .. }))
434 }
435
436 fn skip_policy_definition(&mut self) {
438 let mut depth = 0;
439 while self.pos < self.tokens.len() {
440 if self.check_indent() {
441 depth += 1;
442 } else if self.check_dedent() {
443 if depth == 0 {
444 break;
445 }
446 depth -= 1;
447 }
448 if self.check_period() && depth == 0 {
449 self.advance();
450 break;
451 }
452 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
453 break;
454 }
455 self.advance();
456 }
457 }
458
459 fn parse_type_definition_inline(&mut self, registry: &mut TypeRegistry) {
461 self.parse_type_definition_body(registry);
463 }
464
465 fn try_parse_type_definition(&mut self, registry: &mut TypeRegistry) {
466 self.advance(); self.parse_type_definition_body(registry);
468 }
469
470 fn parse_type_definition_body(&mut self, registry: &mut TypeRegistry) {
471 let mut is_portable = false;
473 let mut is_shared = false;
474 loop {
475 if self.check_portable() {
476 is_portable = true;
477 self.advance();
478 } else if self.check_shared() {
479 is_shared = true;
480 self.advance();
481 } else {
482 break;
483 }
484 }
485
486 if let Some(name_sym) = self.consume_noun_or_proper() {
487 let type_params = if self.check_preposition("of") {
489 self.advance(); self.parse_type_params()
491 } else {
492 vec![]
493 };
494 if self.check_copula() {
495 let copula_pos = self.pos;
496 self.advance(); loop {
500 if self.check_portable() {
501 self.advance(); is_portable = true;
503 if self.check_word("and") {
504 self.advance(); }
506 } else if self.check_shared() {
507 self.advance(); is_shared = true;
509 if self.check_word("and") {
510 self.advance(); }
512 } else {
513 break;
514 }
515 }
516
517 if !is_portable && !is_shared {
519 self.pos = copula_pos;
520 }
521 }
522
523 if self.check_word("has") {
526 self.advance(); if self.check_colon() {
528 self.advance(); if self.check_newline() {
531 self.advance();
532 }
533 if self.check_indent() {
534 self.advance(); let fields = self.parse_struct_fields_with_params(&type_params);
536 registry.register(name_sym, TypeDef::Struct { fields, generics: type_params, is_portable, is_shared });
537 return;
538 }
539 }
540 }
541
542 if self.check_copula() {
544 self.advance(); let is_enum_pattern = if self.check_either() {
548 self.advance(); true
550 } else if self.check_word("one") {
551 self.advance(); if self.check_word("of") {
553 self.advance(); true
555 } else {
556 false
557 }
558 } else {
559 false
560 };
561
562 if is_enum_pattern {
563 if self.check_colon() {
564 self.advance(); if self.check_newline() {
567 self.advance();
568 }
569 if self.check_indent() {
570 self.advance(); let variants = self.parse_enum_variants_with_params(&type_params);
572 registry.register(name_sym, TypeDef::Enum { variants, generics: type_params, is_portable, is_shared });
573 return;
574 }
575 }
576 }
577
578 if self.check_article() {
579 self.advance(); if self.check_word("generic") {
583 registry.register(name_sym, TypeDef::Generic { param_count: 1 });
584 self.skip_to_period();
585 } else if self.check_word("record") || self.check_word("struct") || self.check_word("structure") {
586 registry.register(name_sym, TypeDef::Struct { fields: vec![], generics: vec![], is_portable: false, is_shared: false });
587 self.skip_to_period();
588 } else if self.check_word("sum") || self.check_word("enum") || self.check_word("choice") {
589 registry.register(name_sym, TypeDef::Enum { variants: vec![], generics: vec![], is_portable: false, is_shared: false });
590 self.skip_to_period();
591 }
592 }
593 } else if !type_params.is_empty() {
594 registry.register(name_sym, TypeDef::Generic { param_count: type_params.len() });
596 self.skip_to_period();
597 }
598 }
599 }
600
601 fn parse_enum_variants_with_params(&mut self, type_params: &[Symbol]) -> Vec<VariantDef> {
605 let mut variants = Vec::new();
606
607 while self.pos < self.tokens.len() {
608 if self.check_dedent() {
610 self.advance();
611 break;
612 }
613 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
614 break;
615 }
616
617 if self.check_newline() {
619 self.advance();
620 continue;
621 }
622
623 if self.check_article() {
626 self.advance(); }
628
629 if let Some(variant_name) = self.consume_noun_or_proper() {
631 let fields = if self.check_word("with") {
633 self.parse_variant_fields_natural_with_params(type_params)
635 } else if self.check_lparen() {
636 self.parse_variant_fields_concise_with_params(type_params)
638 } else {
639 vec![]
641 };
642
643 variants.push(VariantDef {
644 name: variant_name,
645 fields,
646 });
647
648 if self.check_period() {
650 self.advance();
651 }
652 } else {
653 self.advance(); }
655 }
656
657 variants
658 }
659
660 fn parse_enum_variants(&mut self) -> Vec<VariantDef> {
662 self.parse_enum_variants_with_params(&[])
663 }
664
665 fn parse_variant_fields_natural_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
670 let mut fields = Vec::new();
671
672 self.advance();
674
675 loop {
676 if self.check_article() {
678 self.advance();
679 }
680
681 if let Some(field_name) = self.consume_noun_or_proper() {
683 let ty = if self.check_comma() {
687 self.advance(); if self.check_word("which") {
690 self.advance();
691 }
692 if self.check_copula() {
694 self.advance();
695 }
696 self.consume_field_type_with_params(type_params)
697 } else if self.check_colon() {
698 self.advance(); self.consume_field_type_with_params(type_params)
700 } else {
701 self.consume_field_type_with_params(type_params)
703 };
704
705 fields.push(FieldDef {
706 name: field_name,
707 ty,
708 is_public: true, });
710
711 if self.check_comma() {
714 self.advance(); }
716 if self.check_word("and") {
717 self.advance();
718 continue;
719 }
720 }
721 break;
722 }
723
724 fields
725 }
726
727 fn parse_variant_fields_natural(&mut self) -> Vec<FieldDef> {
729 self.parse_variant_fields_natural_with_params(&[])
730 }
731
732 fn parse_variant_fields_concise_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
734 let mut fields = Vec::new();
735
736 self.advance();
738
739 loop {
740 if let Some(field_name) = self.consume_noun_or_proper() {
742 let ty = if self.check_colon() {
744 self.advance(); self.consume_field_type_with_params(type_params)
746 } else {
747 FieldType::Primitive(self.interner.intern("Unknown"))
748 };
749
750 fields.push(FieldDef {
751 name: field_name,
752 ty,
753 is_public: true, });
755
756 if self.check_comma() {
758 self.advance();
759 continue;
760 }
761 }
762 break;
763 }
764
765 if self.check_rparen() {
767 self.advance();
768 }
769
770 fields
771 }
772
773 fn parse_variant_fields_concise(&mut self) -> Vec<FieldDef> {
775 self.parse_variant_fields_concise_with_params(&[])
776 }
777
778 fn parse_struct_fields_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
781 let mut fields = Vec::new();
782
783 while self.pos < self.tokens.len() {
784 if self.check_dedent() {
786 self.advance();
787 break;
788 }
789 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
790 break;
791 }
792
793 if self.check_newline() {
795 self.advance();
796 continue;
797 }
798
799 let has_article = self.check_article();
802 if has_article {
803 self.advance(); }
805
806 let has_public_keyword = if self.check_word("public") {
808 self.advance();
809 true
810 } else {
811 false
812 };
813 let mut is_public = has_public_keyword;
815
816 if let Some(field_name) = self.consume_noun_or_proper() {
818 let ty = if self.check_colon() {
822 is_public = true;
824 self.advance(); self.consume_field_type_with_params(type_params)
826 } else if self.check_comma() {
827 is_public = true;
829 self.advance(); if self.check_word("which") {
832 self.advance();
833 }
834 if self.check_copula() {
836 self.advance();
837 }
838 self.consume_field_type_with_params(type_params)
839 } else if !has_article {
840 continue;
842 } else {
843 FieldType::Primitive(self.interner.intern("Unknown"))
845 };
846
847 fields.push(FieldDef {
848 name: field_name,
849 ty,
850 is_public,
851 });
852
853 if self.check_period() {
855 self.advance();
856 }
857 } else if !has_article {
858 self.advance();
860 }
861 }
862
863 fields
864 }
865
866 fn parse_struct_fields(&mut self) -> Vec<FieldDef> {
868 self.parse_struct_fields_with_params(&[])
869 }
870
871 fn consume_field_type(&mut self) -> FieldType {
873 if self.check_lparen() {
875 self.advance(); let inner_type = self.consume_field_type();
877 if self.check_rparen() {
878 self.advance(); }
880 return inner_type;
881 }
882
883 if self.check_article() {
885 self.advance();
886 }
887
888 if let Some(name) = self.consume_noun_or_proper() {
889 let name_str = self.interner.resolve(name);
890
891 let modified_name = if name_str == "SharedSet" || name_str == "ORSet" {
893 if self.check_lparen() {
894 self.advance(); let modifier = if self.check_removewins() {
896 self.advance(); Some("SharedSet_RemoveWins")
898 } else if self.check_addwins() {
899 self.advance(); Some("SharedSet_AddWins")
901 } else {
902 None
903 };
904 if self.check_rparen() {
905 self.advance(); }
907 modifier.map(|m| self.interner.intern(m))
908 } else {
909 None
910 }
911 } else if name_str == "SharedSequence" {
912 if self.check_lparen() {
914 self.advance(); let modifier = if self.check_yata() {
916 self.advance(); Some("SharedSequence_YATA")
918 } else {
919 None
920 };
921 if self.check_rparen() {
922 self.advance(); }
924 modifier.map(|m| self.interner.intern(m))
925 } else {
926 None
927 }
928 } else {
929 None
930 };
931
932 let final_name = modified_name.unwrap_or(name);
934 let final_name_str = self.interner.resolve(final_name);
935
936 if (final_name_str == "SharedMap" || final_name_str == "ORMap") && self.check_from() {
938 self.advance(); let key_type = self.consume_field_type();
940 if self.check_to() {
942 self.advance(); }
944 let value_type = self.consume_field_type();
945 return FieldType::Generic { base: final_name, params: vec![key_type, value_type] };
946 }
947
948 if self.check_preposition("of") {
950 let is_map_type = final_name_str == "Map" || final_name_str == "HashMap";
952
953 self.advance();
954 let first_param = self.consume_field_type();
955
956 if is_map_type && self.check_to() {
958 self.advance(); let second_param = self.consume_field_type();
960 return FieldType::Generic { base: final_name, params: vec![first_param, second_param] };
961 }
962
963 return FieldType::Generic { base: final_name, params: vec![first_param] };
964 }
965
966 if final_name_str == "Divergent" {
968 let param = self.consume_field_type();
970 return FieldType::Generic { base: final_name, params: vec![param] };
971 }
972
973 match final_name_str {
975 "Int" | "Nat" | "Text" | "Bool" | "Real" | "Unit" | "Word8" | "Word16" | "Word32"
976 | "Word64" | "Lanes8Word32" | "Lanes4Word32" | "Lanes16Word8" | "Lanes4Word64" | "Lanes16Word16" => {
977 FieldType::Primitive(final_name)
978 }
979 _ => FieldType::Named(final_name),
980 }
981 } else {
982 FieldType::Primitive(self.interner.intern("Unknown"))
983 }
984 }
985
986 fn peek(&self) -> Option<&Token> {
988 self.tokens.get(self.pos)
989 }
990
991 fn advance(&mut self) {
992 if self.pos < self.tokens.len() {
993 self.pos += 1;
994 }
995 }
996
997 fn check_article(&self) -> bool {
998 match self.peek() {
999 Some(Token { kind: TokenType::Article(_), .. }) => true,
1000 Some(Token { kind: TokenType::ProperName(sym), .. }) => {
1002 let text = self.interner.resolve(*sym);
1003 text.eq_ignore_ascii_case("a") || text.eq_ignore_ascii_case("an")
1004 }
1005 _ => false,
1006 }
1007 }
1008
1009 fn check_copula(&self) -> bool {
1010 match self.peek() {
1011 Some(Token { kind: TokenType::Is | TokenType::Are, .. }) => true,
1012 Some(Token { kind: TokenType::Verb { lemma, .. }, .. }) => {
1014 let word = self.interner.resolve(*lemma).to_lowercase();
1015 word == "is" || word == "are"
1016 }
1017 _ => false,
1018 }
1019 }
1020
1021 fn check_preposition(&self, word: &str) -> bool {
1022 if let Some(Token { kind: TokenType::Preposition(sym), .. }) = self.peek() {
1023 self.interner.resolve(*sym) == word
1024 } else {
1025 false
1026 }
1027 }
1028
1029 fn consume_noun_or_proper(&mut self) -> Option<Symbol> {
1030 let t = self.peek()?;
1031 match &t.kind {
1032 TokenType::Noun(s) | TokenType::ProperName(s) => {
1033 let sym = *s;
1034 self.advance();
1035 Some(sym)
1036 }
1037 TokenType::Adjective(s) => {
1039 let sym = *s;
1040 self.advance();
1041 Some(sym)
1042 }
1043 TokenType::Adverb(_) => {
1045 let sym = t.lexeme;
1046 self.advance();
1047 Some(sym)
1048 }
1049 TokenType::Performative(s) => {
1051 let sym = *s;
1052 self.advance();
1053 Some(sym)
1054 }
1055 TokenType::Items | TokenType::Some => {
1057 let sym = t.lexeme;
1058 self.advance();
1059 Some(sym)
1060 }
1061 TokenType::Verb { .. } => {
1066 let sym = t.lexeme;
1067 self.advance();
1068 Some(sym)
1069 }
1070 TokenType::Tally => {
1072 self.advance();
1073 Some(self.interner.intern("Tally"))
1074 }
1075 TokenType::SharedSet => {
1076 self.advance();
1077 Some(self.interner.intern("SharedSet"))
1078 }
1079 TokenType::SharedSequence => {
1080 self.advance();
1081 Some(self.interner.intern("SharedSequence"))
1082 }
1083 TokenType::CollaborativeSequence => {
1084 self.advance();
1085 Some(self.interner.intern("CollaborativeSequence"))
1086 }
1087 TokenType::SharedMap => {
1088 self.advance();
1089 Some(self.interner.intern("SharedMap"))
1090 }
1091 TokenType::Divergent => {
1092 self.advance();
1093 Some(self.interner.intern("Divergent"))
1094 }
1095 TokenType::Ambiguous { .. } => {
1098 let sym = t.lexeme;
1099 self.advance();
1100 Some(sym)
1101 }
1102 TokenType::Escape => {
1104 let sym = t.lexeme;
1105 self.advance();
1106 Some(sym)
1107 }
1108 TokenType::Particle(_) => {
1110 let sym = t.lexeme;
1111 self.advance();
1112 Some(sym)
1113 }
1114 TokenType::Preposition(_) => {
1116 let sym = t.lexeme;
1117 self.advance();
1118 Some(sym)
1119 }
1120 TokenType::Focus(_) => {
1122 let sym = t.lexeme;
1123 self.advance();
1124 Some(sym)
1125 }
1126 TokenType::Nothing => {
1128 let sym = t.lexeme;
1129 self.advance();
1130 Some(sym)
1131 }
1132 TokenType::Article(_) => {
1134 let sym = t.lexeme;
1135 self.advance();
1136 Some(sym)
1137 }
1138 TokenType::Either => {
1140 let sym = t.lexeme;
1141 self.advance();
1142 Some(sym)
1143 }
1144 TokenType::CalendarUnit(_) => {
1146 let sym = t.lexeme;
1147 self.advance();
1148 Some(sym)
1149 }
1150 _ => None
1151 }
1152 }
1153
1154 fn check_word(&self, word: &str) -> bool {
1155 if let Some(token) = self.peek() {
1156 self.interner.resolve(token.lexeme).eq_ignore_ascii_case(word)
1158 } else {
1159 false
1160 }
1161 }
1162
1163 fn skip_to_period(&mut self) {
1164 while self.pos < self.tokens.len() {
1165 if matches!(self.peek(), Some(Token { kind: TokenType::Period, .. })) {
1166 self.advance();
1167 break;
1168 }
1169 self.advance();
1170 }
1171 }
1172
1173 fn check_colon(&self) -> bool {
1174 matches!(self.peek(), Some(Token { kind: TokenType::Colon, .. }))
1175 }
1176
1177 fn check_newline(&self) -> bool {
1178 matches!(self.peek(), Some(Token { kind: TokenType::Newline, .. }))
1179 }
1180
1181 fn check_indent(&self) -> bool {
1182 matches!(self.peek(), Some(Token { kind: TokenType::Indent, .. }))
1183 }
1184
1185 fn check_dedent(&self) -> bool {
1186 matches!(self.peek(), Some(Token { kind: TokenType::Dedent, .. }))
1187 }
1188
1189 fn check_comma(&self) -> bool {
1190 matches!(self.peek(), Some(Token { kind: TokenType::Comma, .. }))
1191 }
1192
1193 fn check_period(&self) -> bool {
1194 matches!(self.peek(), Some(Token { kind: TokenType::Period, .. }))
1195 }
1196
1197 fn check_either(&self) -> bool {
1198 matches!(self.peek(), Some(Token { kind: TokenType::Either, .. }))
1199 }
1200
1201 fn check_lparen(&self) -> bool {
1202 matches!(self.peek(), Some(Token { kind: TokenType::LParen, .. }))
1203 }
1204
1205 fn check_rparen(&self) -> bool {
1206 matches!(self.peek(), Some(Token { kind: TokenType::RParen, .. }))
1207 }
1208
1209 fn check_addwins(&self) -> bool {
1211 matches!(self.peek(), Some(Token { kind: TokenType::AddWins, .. }))
1212 }
1213
1214 fn check_removewins(&self) -> bool {
1216 matches!(self.peek(), Some(Token { kind: TokenType::RemoveWins, .. }))
1217 }
1218
1219 fn check_yata(&self) -> bool {
1221 matches!(self.peek(), Some(Token { kind: TokenType::YATA, .. }))
1222 }
1223
1224 fn check_to(&self) -> bool {
1226 match self.peek() {
1227 Some(Token { kind: TokenType::To, .. }) => true,
1228 Some(Token { kind: TokenType::Preposition(sym), .. }) => {
1229 self.interner.resolve(*sym) == "to"
1230 }
1231 _ => false,
1232 }
1233 }
1234
1235 fn check_from(&self) -> bool {
1237 match self.peek() {
1238 Some(Token { kind: TokenType::From, .. }) => true,
1239 Some(Token { kind: TokenType::Preposition(sym), .. }) => {
1240 self.interner.resolve(*sym) == "from"
1241 }
1242 _ => false,
1243 }
1244 }
1245
1246 fn check_portable(&self) -> bool {
1248 matches!(self.peek(), Some(Token { kind: TokenType::Portable, .. }))
1249 }
1250
1251 fn check_shared(&self) -> bool {
1253 matches!(self.peek(), Some(Token { kind: TokenType::Shared, .. }))
1254 }
1255
1256 fn check_lbracket(&self) -> bool {
1258 matches!(self.peek(), Some(Token { kind: TokenType::LBracket, .. }))
1259 }
1260
1261 fn check_rbracket(&self) -> bool {
1262 matches!(self.peek(), Some(Token { kind: TokenType::RBracket, .. }))
1263 }
1264
1265 fn parse_type_params(&mut self) -> Vec<Symbol> {
1267 let mut params = Vec::new();
1268
1269 loop {
1270 if self.check_lbracket() {
1271 self.advance(); if let Some(param) = self.consume_noun_or_proper() {
1273 params.push(param);
1274 }
1275 if self.check_rbracket() {
1276 self.advance(); }
1278 }
1279
1280 if self.check_word("and") {
1282 self.advance();
1283 continue;
1284 }
1285 break;
1286 }
1287 params
1288 }
1289
1290 fn consume_field_type_with_params(&mut self, type_params: &[Symbol]) -> FieldType {
1292 if self.check_lparen() {
1294 self.advance(); let inner_type = self.consume_field_type_with_params(type_params);
1296 if self.check_rparen() {
1297 self.advance(); }
1299 return inner_type;
1300 }
1301
1302 if let Some(Token { kind: TokenType::Article(_), lexeme, .. }) = self.peek() {
1305 let text = self.interner.resolve(*lexeme);
1306 for ¶m_sym in type_params {
1308 let param_name = self.interner.resolve(param_sym);
1309 if text.eq_ignore_ascii_case(param_name) {
1310 self.advance(); return FieldType::TypeParam(param_sym);
1312 }
1313 }
1314 self.advance();
1316 }
1317
1318 if let Some(name) = self.consume_noun_or_proper() {
1319 if type_params.contains(&name) {
1321 return FieldType::TypeParam(name);
1322 }
1323
1324 let name_str = self.interner.resolve(name);
1325
1326 let modified_name = if name_str == "SharedSet" || name_str == "ORSet" {
1328 if self.check_lparen() {
1329 self.advance(); let modifier = if self.check_removewins() {
1331 self.advance(); Some("SharedSet_RemoveWins")
1333 } else if self.check_addwins() {
1334 self.advance(); Some("SharedSet_AddWins")
1336 } else {
1337 None
1338 };
1339 if self.check_rparen() {
1340 self.advance(); }
1342 modifier.map(|m| self.interner.intern(m))
1343 } else {
1344 None
1345 }
1346 } else if name_str == "SharedSequence" {
1347 if self.check_lparen() {
1349 self.advance(); let modifier = if self.check_yata() {
1351 self.advance(); Some("SharedSequence_YATA")
1353 } else {
1354 None
1355 };
1356 if self.check_rparen() {
1357 self.advance(); }
1359 modifier.map(|m| self.interner.intern(m))
1360 } else {
1361 None
1362 }
1363 } else {
1364 None
1365 };
1366
1367 let final_name = modified_name.unwrap_or(name);
1369 let final_name_str = self.interner.resolve(final_name);
1370
1371 if (final_name_str == "SharedMap" || final_name_str == "ORMap") && self.check_from() {
1373 self.advance(); let key_type = self.consume_field_type_with_params(type_params);
1375 if self.check_to() {
1377 self.advance(); }
1379 let value_type = self.consume_field_type_with_params(type_params);
1380 return FieldType::Generic { base: final_name, params: vec![key_type, value_type] };
1381 }
1382
1383 if self.check_preposition("of") {
1385 let is_map_type = final_name_str == "Map" || final_name_str == "HashMap";
1387
1388 self.advance();
1389 let first_param = self.consume_field_type_with_params(type_params);
1390
1391 if is_map_type && self.check_to() {
1393 self.advance(); let second_param = self.consume_field_type_with_params(type_params);
1395 return FieldType::Generic { base: final_name, params: vec![first_param, second_param] };
1396 }
1397
1398 return FieldType::Generic { base: final_name, params: vec![first_param] };
1399 }
1400
1401 if final_name_str == "Divergent" {
1403 let param = self.consume_field_type_with_params(type_params);
1405 return FieldType::Generic { base: final_name, params: vec![param] };
1406 }
1407
1408 match final_name_str {
1410 "Int" | "Nat" | "Text" | "Bool" | "Real" | "Unit" | "Word8" | "Word16" | "Word32"
1411 | "Word64" | "Lanes8Word32" | "Lanes4Word32" | "Lanes16Word8" | "Lanes4Word64" | "Lanes16Word16" => {
1412 FieldType::Primitive(final_name)
1413 }
1414 _ => FieldType::Named(final_name),
1415 }
1416 } else {
1417 FieldType::Primitive(self.interner.intern("Unknown"))
1418 }
1419 }
1420}
1421
1422#[cfg(test)]
1426mod tests {
1427 use super::*;
1428 use crate::Lexer;
1429 use crate::mwe;
1430
1431 fn make_tokens(source: &str, interner: &mut Interner) -> Vec<Token> {
1432 let mut lexer = Lexer::new(source, interner);
1433 let tokens = lexer.tokenize();
1434 let mwe_trie = mwe::build_mwe_trie();
1435 mwe::apply_mwe_pipeline(tokens, &mwe_trie, interner)
1436 }
1437
1438 #[test]
1439 fn discovery_finds_generic_in_definition_block() {
1440 let source = "## Definition\nA Stack is a generic collection.";
1441 let mut interner = Interner::new();
1442 let tokens = make_tokens(source, &mut interner);
1443
1444 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1445 let registry = discovery.run();
1446
1447 let stack = interner.intern("Stack");
1448 assert!(registry.is_generic(stack), "Stack should be discovered as generic");
1449 }
1450
1451 #[test]
1452 fn discovery_parses_struct_with_fields() {
1453 let source = r#"## Definition
1454A Point has:
1455 an x, which is Int.
1456 a y, which is Int.
1457"#;
1458 let mut interner = Interner::new();
1459 let tokens = make_tokens(source, &mut interner);
1460
1461 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1462 let registry = discovery.run();
1463
1464 let point = interner.intern("Point");
1465 assert!(registry.is_type(point), "Point should be registered");
1466
1467 if let Some(TypeDef::Struct { fields, generics, .. }) = registry.get(point) {
1468 assert_eq!(fields.len(), 2, "Point should have 2 fields, got {:?}", fields);
1469 assert_eq!(interner.resolve(fields[0].name), "x");
1470 assert_eq!(interner.resolve(fields[1].name), "y");
1471 assert!(generics.is_empty(), "Point should have no generics");
1472 } else {
1473 panic!("Point should be a struct with fields");
1474 }
1475 }
1476
1477 #[test]
1478 fn discovery_works_with_markdown_header() {
1479 let source = r#"# Geometry
1481
1482## Definition
1483A Point has:
1484 an x, which is Int.
1485"#;
1486 let mut interner = Interner::new();
1487 let tokens = make_tokens(source, &mut interner);
1488
1489 for (i, tok) in tokens.iter().enumerate() {
1491 eprintln!("Token {}: {:?}", i, tok.kind);
1492 }
1493
1494 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1495 let registry = discovery.run();
1496 let point = interner.intern("Point");
1497 assert!(registry.is_type(point), "Point should be discovered even with # header");
1498 }
1499
1500 #[test]
1501 fn discovery_parses_portable_enum() {
1502 let source = r#"## Definition
1503A Command is Portable and is either:
1504 a Start.
1505 a Stop.
1506 a Pause.
1507"#;
1508 let mut interner = Interner::new();
1509 let tokens = make_tokens(source, &mut interner);
1510
1511 eprintln!("Tokens for portable enum:");
1513 for (i, tok) in tokens.iter().enumerate() {
1514 eprintln!("Token {}: {:?} ({})", i, tok.kind, interner.resolve(tok.lexeme));
1515 }
1516
1517 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1518 let registry = discovery.run();
1519
1520 let command = interner.intern("Command");
1521 assert!(registry.is_type(command), "Command should be registered as type");
1522
1523 if let Some(TypeDef::Enum { variants, is_portable, .. }) = registry.get(command) {
1524 eprintln!("Command is_portable: {}", is_portable);
1525 eprintln!("Variants: {:?}", variants.iter().map(|v| interner.resolve(v.name)).collect::<Vec<_>>());
1526 assert!(*is_portable, "Command should be portable");
1527 assert_eq!(variants.len(), 3, "Command should have 3 variants");
1528 } else {
1529 panic!("Command should be an enum, got: {:?}", registry.get(command));
1530 }
1531 }
1532
1533 #[test]
1534 fn discovery_parses_lww_int_field() {
1535 let source = r#"## Definition
1536A Setting is Shared and has:
1537 a volume, which is LastWriteWins of Int.
1538"#;
1539 let mut interner = Interner::new();
1540 let tokens = make_tokens(source, &mut interner);
1541
1542 eprintln!("Tokens for LWW of Int:");
1544 for (i, tok) in tokens.iter().enumerate() {
1545 eprintln!("{:3}: {:?} ({})", i, tok.kind, interner.resolve(tok.lexeme));
1546 }
1547
1548 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1549 let registry = discovery.run();
1550
1551 let setting = interner.intern("Setting");
1552 assert!(registry.is_type(setting), "Setting should be registered");
1553
1554 if let Some(TypeDef::Struct { fields, is_shared, .. }) = registry.get(setting) {
1555 eprintln!("is_shared: {}", is_shared);
1556 eprintln!("Fields: {:?}", fields.len());
1557 for f in fields {
1558 eprintln!(" field: {} = {:?}", interner.resolve(f.name), f.ty);
1559 }
1560 assert!(*is_shared, "Setting should be shared");
1561 assert_eq!(fields.len(), 1, "Setting should have 1 field");
1562 } else {
1563 panic!("Setting should be a struct, got: {:?}", registry.get(setting));
1564 }
1565 }
1566}