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 {
76 self.advance();
77 }
78 }
79
80 DiscoveryResult {
81 types: type_registry,
82 policies: policy_registry,
83 }
84 }
85
86 fn check_block_header(&self, expected: BlockType) -> bool {
87 matches!(
88 self.tokens.get(self.pos),
89 Some(Token { kind: TokenType::BlockHeader { block_type }, .. })
90 if *block_type == expected
91 )
92 }
93
94 fn scan_definition_block(&mut self, registry: &mut TypeRegistry) {
95 while self.pos < self.tokens.len() {
97 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
98 break;
99 }
100
101 if self.check_article() {
103 self.try_parse_type_definition(registry);
104 } else {
105 self.advance();
106 }
107 }
108 }
109
110 fn scan_policy_block(&mut self, registry: &mut PolicyRegistry) {
115 while self.pos < self.tokens.len() {
116 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
117 break;
118 }
119
120 if self.check_newline() || self.check_indent() || self.check_dedent() {
122 self.advance();
123 continue;
124 }
125
126 if self.check_article() {
128 self.try_parse_policy_definition(registry);
129 } else {
130 self.advance();
131 }
132 }
133 }
134
135 fn try_parse_policy_definition(&mut self, registry: &mut PolicyRegistry) {
137 self.advance(); let subject_type = match self.consume_noun_or_proper() {
141 Some(sym) => sym,
142 None => return,
143 };
144
145 if self.check_copula() {
147 self.advance(); let predicate_name = match self.consume_noun_or_proper() {
152 Some(sym) => sym,
153 None => return,
154 };
155
156 if !self.check_word("if") {
158 self.skip_to_period();
159 return;
160 }
161 self.advance(); if self.check_colon() {
165 self.advance();
166 }
167 if self.check_newline() {
168 self.advance();
169 }
170 if self.check_indent() {
171 self.advance();
172 }
173
174 let condition = self.parse_policy_condition(subject_type, None);
176
177 registry.register_predicate(PredicateDef {
178 subject_type,
179 predicate_name,
180 condition,
181 });
182
183 self.skip_to_period();
184 } else if self.check_word("can") {
185 self.advance(); let action = match self.consume_noun_or_proper() {
190 Some(sym) => sym,
191 None => {
192 if let Some(Token { kind: TokenType::Verb { lemma, .. }, .. }) = self.peek() {
194 let sym = *lemma;
195 self.advance();
196 sym
197 } else {
198 return;
199 }
200 }
201 };
202
203 if self.check_article() {
205 self.advance();
206 }
207
208 let object_type = match self.consume_noun_or_proper() {
210 Some(sym) => sym,
211 None => return,
212 };
213
214 if !self.check_word("if") {
216 self.skip_to_period();
217 return;
218 }
219 self.advance(); if self.check_colon() {
223 self.advance();
224 }
225 if self.check_newline() {
226 self.advance();
227 }
228 if self.check_indent() {
229 self.advance();
230 }
231
232 let condition = self.parse_policy_condition(subject_type, Some(object_type));
233
234 registry.register_capability(CapabilityDef {
235 subject_type,
236 action,
237 object_type,
238 condition,
239 });
240
241 self.skip_policy_definition();
243 } else {
244 self.skip_to_period();
245 }
246 }
247
248 fn parse_policy_condition(&mut self, subject_type: Symbol, object_type: Option<Symbol>) -> PolicyCondition {
251 let first = self.parse_atomic_condition(subject_type, object_type);
252
253 loop {
255 while self.check_newline() {
257 self.advance();
258 }
259
260 if self.check_comma() {
262 self.advance(); while self.check_newline() {
265 self.advance();
266 }
267 }
268
269 if self.check_word("AND") {
270 self.advance();
271 while self.check_newline() {
273 self.advance();
274 }
275 let right = self.parse_atomic_condition(subject_type, object_type);
276 return PolicyCondition::And(Box::new(first), Box::new(right));
277 } else if self.check_word("OR") {
278 self.advance();
279 while self.check_newline() {
281 self.advance();
282 }
283 let right = self.parse_atomic_condition(subject_type, object_type);
284 return PolicyCondition::Or(Box::new(first), Box::new(right));
285 } else {
286 break;
287 }
288 }
289
290 first
291 }
292
293 fn parse_atomic_condition(&mut self, subject_type: Symbol, object_type: Option<Symbol>) -> PolicyCondition {
295 if self.check_article() {
297 self.advance();
298 }
299
300 let subject_ref = match self.consume_noun_or_proper() {
302 Some(sym) => sym,
303 None => return PolicyCondition::FieldEquals {
304 field: self.interner.intern("unknown"),
305 value: self.interner.intern("unknown"),
306 is_string_literal: false,
307 },
308 };
309
310 if self.check_possessive() {
312 self.advance(); let field = match self.consume_noun_or_proper() {
316 Some(sym) => sym,
317 None => return PolicyCondition::FieldEquals {
318 field: self.interner.intern("unknown"),
319 value: self.interner.intern("unknown"),
320 is_string_literal: false,
321 },
322 };
323
324 if self.check_word("equals") {
326 self.advance();
327
328 let (value, is_string_literal) = self.consume_value();
330
331 return PolicyCondition::FieldEquals { field, value, is_string_literal };
332 }
333 } else if self.check_copula() {
334 self.advance(); let predicate = match self.consume_noun_or_proper() {
339 Some(sym) => sym,
340 None => return PolicyCondition::FieldEquals {
341 field: self.interner.intern("unknown"),
342 value: self.interner.intern("unknown"),
343 is_string_literal: false,
344 },
345 };
346
347 return PolicyCondition::Predicate {
348 subject: subject_ref,
349 predicate,
350 };
351 } else if self.check_word("equals") {
352 self.advance(); if self.check_article() {
357 self.advance();
358 }
359
360 if let Some(obj_ref) = self.consume_noun_or_proper() {
362 if self.check_possessive() {
363 self.advance(); if let Some(field) = self.consume_noun_or_proper() {
365 return PolicyCondition::ObjectFieldEquals {
366 subject: subject_ref,
367 object: obj_ref,
368 field,
369 };
370 }
371 }
372 }
373 }
374
375 PolicyCondition::FieldEquals {
377 field: self.interner.intern("unknown"),
378 value: self.interner.intern("unknown"),
379 is_string_literal: false,
380 }
381 }
382
383 fn consume_value(&mut self) -> (Symbol, bool) {
385 if let Some(Token { kind: TokenType::StringLiteral(sym), .. }) = self.peek() {
386 let s = *sym;
387 self.advance();
388 (s, true)
389 } else if let Some(sym) = self.consume_noun_or_proper() {
390 (sym, false)
391 } else {
392 (self.interner.intern("unknown"), false)
393 }
394 }
395
396 fn check_possessive(&self) -> bool {
398 matches!(self.peek(), Some(Token { kind: TokenType::Possessive, .. }))
399 }
400
401 fn skip_policy_definition(&mut self) {
403 let mut depth = 0;
404 while self.pos < self.tokens.len() {
405 if self.check_indent() {
406 depth += 1;
407 } else if self.check_dedent() {
408 if depth == 0 {
409 break;
410 }
411 depth -= 1;
412 }
413 if self.check_period() && depth == 0 {
414 self.advance();
415 break;
416 }
417 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
418 break;
419 }
420 self.advance();
421 }
422 }
423
424 fn parse_type_definition_inline(&mut self, registry: &mut TypeRegistry) {
426 self.parse_type_definition_body(registry);
428 }
429
430 fn try_parse_type_definition(&mut self, registry: &mut TypeRegistry) {
431 self.advance(); self.parse_type_definition_body(registry);
433 }
434
435 fn parse_type_definition_body(&mut self, registry: &mut TypeRegistry) {
436 if let Some(name_sym) = self.consume_noun_or_proper() {
437 let type_params = if self.check_preposition("of") {
439 self.advance(); self.parse_type_params()
441 } else {
442 vec![]
443 };
444
445 let mut is_portable = false;
447 let mut is_shared = false;
448 if self.check_copula() {
449 let copula_pos = self.pos;
450 self.advance(); loop {
454 if self.check_portable() {
455 self.advance(); is_portable = true;
457 if self.check_word("and") {
458 self.advance(); }
460 } else if self.check_shared() {
461 self.advance(); is_shared = true;
463 if self.check_word("and") {
464 self.advance(); }
466 } else {
467 break;
468 }
469 }
470
471 if !is_portable && !is_shared {
473 self.pos = copula_pos;
474 }
475 }
476
477 if self.check_word("has") {
480 self.advance(); if self.check_colon() {
482 self.advance(); if self.check_newline() {
485 self.advance();
486 }
487 if self.check_indent() {
488 self.advance(); let fields = self.parse_struct_fields_with_params(&type_params);
490 registry.register(name_sym, TypeDef::Struct { fields, generics: type_params, is_portable, is_shared });
491 return;
492 }
493 }
494 }
495
496 if self.check_copula() {
498 self.advance(); let is_enum_pattern = if self.check_either() {
502 self.advance(); true
504 } else if self.check_word("one") {
505 self.advance(); if self.check_word("of") {
507 self.advance(); true
509 } else {
510 false
511 }
512 } else {
513 false
514 };
515
516 if is_enum_pattern {
517 if self.check_colon() {
518 self.advance(); if self.check_newline() {
521 self.advance();
522 }
523 if self.check_indent() {
524 self.advance(); let variants = self.parse_enum_variants_with_params(&type_params);
526 registry.register(name_sym, TypeDef::Enum { variants, generics: type_params, is_portable, is_shared });
527 return;
528 }
529 }
530 }
531
532 if self.check_article() {
533 self.advance(); if self.check_word("generic") {
537 registry.register(name_sym, TypeDef::Generic { param_count: 1 });
538 self.skip_to_period();
539 } else if self.check_word("record") || self.check_word("struct") || self.check_word("structure") {
540 registry.register(name_sym, TypeDef::Struct { fields: vec![], generics: vec![], is_portable: false, is_shared: false });
541 self.skip_to_period();
542 } else if self.check_word("sum") || self.check_word("enum") || self.check_word("choice") {
543 registry.register(name_sym, TypeDef::Enum { variants: vec![], generics: vec![], is_portable: false, is_shared: false });
544 self.skip_to_period();
545 }
546 }
547 } else if !type_params.is_empty() {
548 registry.register(name_sym, TypeDef::Generic { param_count: type_params.len() });
550 self.skip_to_period();
551 }
552 }
553 }
554
555 fn parse_enum_variants_with_params(&mut self, type_params: &[Symbol]) -> Vec<VariantDef> {
559 let mut variants = Vec::new();
560
561 while self.pos < self.tokens.len() {
562 if self.check_dedent() {
564 self.advance();
565 break;
566 }
567 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
568 break;
569 }
570
571 if self.check_newline() {
573 self.advance();
574 continue;
575 }
576
577 if self.check_article() {
580 self.advance(); }
582
583 if let Some(variant_name) = self.consume_noun_or_proper() {
585 let fields = if self.check_word("with") {
587 self.parse_variant_fields_natural_with_params(type_params)
589 } else if self.check_lparen() {
590 self.parse_variant_fields_concise_with_params(type_params)
592 } else {
593 vec![]
595 };
596
597 variants.push(VariantDef {
598 name: variant_name,
599 fields,
600 });
601
602 if self.check_period() {
604 self.advance();
605 }
606 } else {
607 self.advance(); }
609 }
610
611 variants
612 }
613
614 fn parse_enum_variants(&mut self) -> Vec<VariantDef> {
616 self.parse_enum_variants_with_params(&[])
617 }
618
619 fn parse_variant_fields_natural_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
624 let mut fields = Vec::new();
625
626 self.advance();
628
629 loop {
630 if self.check_article() {
632 self.advance();
633 }
634
635 if let Some(field_name) = self.consume_noun_or_proper() {
637 let ty = if self.check_comma() {
641 self.advance(); if self.check_word("which") {
644 self.advance();
645 }
646 if self.check_copula() {
648 self.advance();
649 }
650 self.consume_field_type_with_params(type_params)
651 } else {
652 self.consume_field_type_with_params(type_params)
654 };
655
656 fields.push(FieldDef {
657 name: field_name,
658 ty,
659 is_public: true, });
661
662 if self.check_comma() {
665 self.advance(); }
667 if self.check_word("and") {
668 self.advance();
669 continue;
670 }
671 }
672 break;
673 }
674
675 fields
676 }
677
678 fn parse_variant_fields_natural(&mut self) -> Vec<FieldDef> {
680 self.parse_variant_fields_natural_with_params(&[])
681 }
682
683 fn parse_variant_fields_concise_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
685 let mut fields = Vec::new();
686
687 self.advance();
689
690 loop {
691 if let Some(field_name) = self.consume_noun_or_proper() {
693 let ty = if self.check_colon() {
695 self.advance(); self.consume_field_type_with_params(type_params)
697 } else {
698 FieldType::Primitive(self.interner.intern("Unknown"))
699 };
700
701 fields.push(FieldDef {
702 name: field_name,
703 ty,
704 is_public: true, });
706
707 if self.check_comma() {
709 self.advance();
710 continue;
711 }
712 }
713 break;
714 }
715
716 if self.check_rparen() {
718 self.advance();
719 }
720
721 fields
722 }
723
724 fn parse_variant_fields_concise(&mut self) -> Vec<FieldDef> {
726 self.parse_variant_fields_concise_with_params(&[])
727 }
728
729 fn parse_struct_fields_with_params(&mut self, type_params: &[Symbol]) -> Vec<FieldDef> {
732 let mut fields = Vec::new();
733
734 while self.pos < self.tokens.len() {
735 if self.check_dedent() {
737 self.advance();
738 break;
739 }
740 if matches!(self.peek(), Some(Token { kind: TokenType::BlockHeader { .. }, .. })) {
741 break;
742 }
743
744 if self.check_newline() {
746 self.advance();
747 continue;
748 }
749
750 let has_article = self.check_article();
753 if has_article {
754 self.advance(); }
756
757 let has_public_keyword = if self.check_word("public") {
759 self.advance();
760 true
761 } else {
762 false
763 };
764 let mut is_public = has_public_keyword;
766
767 if let Some(field_name) = self.consume_noun_or_proper() {
769 let ty = if self.check_colon() {
773 is_public = true;
775 self.advance(); self.consume_field_type_with_params(type_params)
777 } else if self.check_comma() {
778 is_public = true;
780 self.advance(); if self.check_word("which") {
783 self.advance();
784 }
785 if self.check_copula() {
787 self.advance();
788 }
789 self.consume_field_type_with_params(type_params)
790 } else if !has_article {
791 continue;
793 } else {
794 FieldType::Primitive(self.interner.intern("Unknown"))
796 };
797
798 fields.push(FieldDef {
799 name: field_name,
800 ty,
801 is_public,
802 });
803
804 if self.check_period() {
806 self.advance();
807 }
808 } else if !has_article {
809 self.advance();
811 }
812 }
813
814 fields
815 }
816
817 fn parse_struct_fields(&mut self) -> Vec<FieldDef> {
819 self.parse_struct_fields_with_params(&[])
820 }
821
822 fn consume_field_type(&mut self) -> FieldType {
824 if self.check_article() {
826 self.advance();
827 }
828
829 if let Some(name) = self.consume_noun_or_proper() {
830 let name_str = self.interner.resolve(name);
831
832 let modified_name = if name_str == "SharedSet" || name_str == "ORSet" {
834 if self.check_lparen() {
835 self.advance(); let modifier = if self.check_removewins() {
837 self.advance(); Some("SharedSet_RemoveWins")
839 } else if self.check_addwins() {
840 self.advance(); Some("SharedSet_AddWins")
842 } else {
843 None
844 };
845 if self.check_rparen() {
846 self.advance(); }
848 modifier.map(|m| self.interner.intern(m))
849 } else {
850 None
851 }
852 } else if name_str == "SharedSequence" {
853 if self.check_lparen() {
855 self.advance(); let modifier = if self.check_yata() {
857 self.advance(); Some("SharedSequence_YATA")
859 } else {
860 None
861 };
862 if self.check_rparen() {
863 self.advance(); }
865 modifier.map(|m| self.interner.intern(m))
866 } else {
867 None
868 }
869 } else {
870 None
871 };
872
873 let final_name = modified_name.unwrap_or(name);
875 let final_name_str = self.interner.resolve(final_name);
876
877 if (final_name_str == "SharedMap" || final_name_str == "ORMap") && self.check_from() {
879 self.advance(); let key_type = self.consume_field_type();
881 if self.check_to() {
883 self.advance(); }
885 let value_type = self.consume_field_type();
886 return FieldType::Generic { base: final_name, params: vec![key_type, value_type] };
887 }
888
889 if self.check_preposition("of") {
891 self.advance();
892 let param = self.consume_field_type();
893 return FieldType::Generic { base: final_name, params: vec![param] };
894 }
895
896 if final_name_str == "Divergent" {
898 let param = self.consume_field_type();
900 return FieldType::Generic { base: final_name, params: vec![param] };
901 }
902
903 match final_name_str {
905 "Int" | "Nat" | "Text" | "Bool" | "Real" | "Unit" => FieldType::Primitive(final_name),
906 _ => FieldType::Named(final_name),
907 }
908 } else {
909 FieldType::Primitive(self.interner.intern("Unknown"))
910 }
911 }
912
913 fn peek(&self) -> Option<&Token> {
915 self.tokens.get(self.pos)
916 }
917
918 fn advance(&mut self) {
919 if self.pos < self.tokens.len() {
920 self.pos += 1;
921 }
922 }
923
924 fn check_article(&self) -> bool {
925 match self.peek() {
926 Some(Token { kind: TokenType::Article(_), .. }) => true,
927 Some(Token { kind: TokenType::ProperName(sym), .. }) => {
929 let text = self.interner.resolve(*sym);
930 text.eq_ignore_ascii_case("a") || text.eq_ignore_ascii_case("an")
931 }
932 _ => false,
933 }
934 }
935
936 fn check_copula(&self) -> bool {
937 match self.peek() {
938 Some(Token { kind: TokenType::Is | TokenType::Are, .. }) => true,
939 Some(Token { kind: TokenType::Verb { lemma, .. }, .. }) => {
941 let word = self.interner.resolve(*lemma).to_lowercase();
942 word == "is" || word == "are"
943 }
944 _ => false,
945 }
946 }
947
948 fn check_preposition(&self, word: &str) -> bool {
949 if let Some(Token { kind: TokenType::Preposition(sym), .. }) = self.peek() {
950 self.interner.resolve(*sym) == word
951 } else {
952 false
953 }
954 }
955
956 fn consume_noun_or_proper(&mut self) -> Option<Symbol> {
957 let t = self.peek()?;
958 match &t.kind {
959 TokenType::Noun(s) | TokenType::ProperName(s) => {
960 let sym = *s;
961 self.advance();
962 Some(sym)
963 }
964 TokenType::Adjective(s) => {
966 let sym = *s;
967 self.advance();
968 Some(sym)
969 }
970 TokenType::Performative(s) => {
972 let sym = *s;
973 self.advance();
974 Some(sym)
975 }
976 TokenType::Items | TokenType::Some => {
978 let sym = t.lexeme;
979 self.advance();
980 Some(sym)
981 }
982 TokenType::Verb { .. } => {
987 let sym = t.lexeme;
988 self.advance();
989 Some(sym)
990 }
991 TokenType::Tally => {
993 self.advance();
994 Some(self.interner.intern("Tally"))
995 }
996 TokenType::SharedSet => {
997 self.advance();
998 Some(self.interner.intern("SharedSet"))
999 }
1000 TokenType::SharedSequence => {
1001 self.advance();
1002 Some(self.interner.intern("SharedSequence"))
1003 }
1004 TokenType::CollaborativeSequence => {
1005 self.advance();
1006 Some(self.interner.intern("CollaborativeSequence"))
1007 }
1008 TokenType::SharedMap => {
1009 self.advance();
1010 Some(self.interner.intern("SharedMap"))
1011 }
1012 TokenType::Divergent => {
1013 self.advance();
1014 Some(self.interner.intern("Divergent"))
1015 }
1016 TokenType::Ambiguous { .. } => {
1019 let sym = t.lexeme;
1020 self.advance();
1021 Some(sym)
1022 }
1023 TokenType::Focus(_) => {
1025 let sym = t.lexeme;
1026 self.advance();
1027 Some(sym)
1028 }
1029 TokenType::Nothing => {
1031 let sym = t.lexeme;
1032 self.advance();
1033 Some(sym)
1034 }
1035 TokenType::Article(_) => {
1037 let sym = t.lexeme;
1038 self.advance();
1039 Some(sym)
1040 }
1041 TokenType::Either => {
1043 let sym = t.lexeme;
1044 self.advance();
1045 Some(sym)
1046 }
1047 _ => None
1048 }
1049 }
1050
1051 fn check_word(&self, word: &str) -> bool {
1052 if let Some(token) = self.peek() {
1053 self.interner.resolve(token.lexeme).eq_ignore_ascii_case(word)
1055 } else {
1056 false
1057 }
1058 }
1059
1060 fn skip_to_period(&mut self) {
1061 while self.pos < self.tokens.len() {
1062 if matches!(self.peek(), Some(Token { kind: TokenType::Period, .. })) {
1063 self.advance();
1064 break;
1065 }
1066 self.advance();
1067 }
1068 }
1069
1070 fn check_colon(&self) -> bool {
1071 matches!(self.peek(), Some(Token { kind: TokenType::Colon, .. }))
1072 }
1073
1074 fn check_newline(&self) -> bool {
1075 matches!(self.peek(), Some(Token { kind: TokenType::Newline, .. }))
1076 }
1077
1078 fn check_indent(&self) -> bool {
1079 matches!(self.peek(), Some(Token { kind: TokenType::Indent, .. }))
1080 }
1081
1082 fn check_dedent(&self) -> bool {
1083 matches!(self.peek(), Some(Token { kind: TokenType::Dedent, .. }))
1084 }
1085
1086 fn check_comma(&self) -> bool {
1087 matches!(self.peek(), Some(Token { kind: TokenType::Comma, .. }))
1088 }
1089
1090 fn check_period(&self) -> bool {
1091 matches!(self.peek(), Some(Token { kind: TokenType::Period, .. }))
1092 }
1093
1094 fn check_either(&self) -> bool {
1095 matches!(self.peek(), Some(Token { kind: TokenType::Either, .. }))
1096 }
1097
1098 fn check_lparen(&self) -> bool {
1099 matches!(self.peek(), Some(Token { kind: TokenType::LParen, .. }))
1100 }
1101
1102 fn check_rparen(&self) -> bool {
1103 matches!(self.peek(), Some(Token { kind: TokenType::RParen, .. }))
1104 }
1105
1106 fn check_addwins(&self) -> bool {
1108 matches!(self.peek(), Some(Token { kind: TokenType::AddWins, .. }))
1109 }
1110
1111 fn check_removewins(&self) -> bool {
1113 matches!(self.peek(), Some(Token { kind: TokenType::RemoveWins, .. }))
1114 }
1115
1116 fn check_yata(&self) -> bool {
1118 matches!(self.peek(), Some(Token { kind: TokenType::YATA, .. }))
1119 }
1120
1121 fn check_to(&self) -> bool {
1123 match self.peek() {
1124 Some(Token { kind: TokenType::To, .. }) => true,
1125 Some(Token { kind: TokenType::Preposition(sym), .. }) => {
1126 self.interner.resolve(*sym) == "to"
1127 }
1128 _ => false,
1129 }
1130 }
1131
1132 fn check_from(&self) -> bool {
1134 match self.peek() {
1135 Some(Token { kind: TokenType::From, .. }) => true,
1136 Some(Token { kind: TokenType::Preposition(sym), .. }) => {
1137 self.interner.resolve(*sym) == "from"
1138 }
1139 _ => false,
1140 }
1141 }
1142
1143 fn check_portable(&self) -> bool {
1145 matches!(self.peek(), Some(Token { kind: TokenType::Portable, .. }))
1146 }
1147
1148 fn check_shared(&self) -> bool {
1150 matches!(self.peek(), Some(Token { kind: TokenType::Shared, .. }))
1151 }
1152
1153 fn check_lbracket(&self) -> bool {
1155 matches!(self.peek(), Some(Token { kind: TokenType::LBracket, .. }))
1156 }
1157
1158 fn check_rbracket(&self) -> bool {
1159 matches!(self.peek(), Some(Token { kind: TokenType::RBracket, .. }))
1160 }
1161
1162 fn parse_type_params(&mut self) -> Vec<Symbol> {
1164 let mut params = Vec::new();
1165
1166 loop {
1167 if self.check_lbracket() {
1168 self.advance(); if let Some(param) = self.consume_noun_or_proper() {
1170 params.push(param);
1171 }
1172 if self.check_rbracket() {
1173 self.advance(); }
1175 }
1176
1177 if self.check_word("and") {
1179 self.advance();
1180 continue;
1181 }
1182 break;
1183 }
1184 params
1185 }
1186
1187 fn consume_field_type_with_params(&mut self, type_params: &[Symbol]) -> FieldType {
1189 if let Some(Token { kind: TokenType::Article(_), lexeme, .. }) = self.peek() {
1192 let text = self.interner.resolve(*lexeme);
1193 for ¶m_sym in type_params {
1195 let param_name = self.interner.resolve(param_sym);
1196 if text.eq_ignore_ascii_case(param_name) {
1197 self.advance(); return FieldType::TypeParam(param_sym);
1199 }
1200 }
1201 self.advance();
1203 }
1204
1205 if let Some(name) = self.consume_noun_or_proper() {
1206 if type_params.contains(&name) {
1208 return FieldType::TypeParam(name);
1209 }
1210
1211 let name_str = self.interner.resolve(name);
1212
1213 let modified_name = if name_str == "SharedSet" || name_str == "ORSet" {
1215 if self.check_lparen() {
1216 self.advance(); let modifier = if self.check_removewins() {
1218 self.advance(); Some("SharedSet_RemoveWins")
1220 } else if self.check_addwins() {
1221 self.advance(); Some("SharedSet_AddWins")
1223 } else {
1224 None
1225 };
1226 if self.check_rparen() {
1227 self.advance(); }
1229 modifier.map(|m| self.interner.intern(m))
1230 } else {
1231 None
1232 }
1233 } else if name_str == "SharedSequence" {
1234 if self.check_lparen() {
1236 self.advance(); let modifier = if self.check_yata() {
1238 self.advance(); Some("SharedSequence_YATA")
1240 } else {
1241 None
1242 };
1243 if self.check_rparen() {
1244 self.advance(); }
1246 modifier.map(|m| self.interner.intern(m))
1247 } else {
1248 None
1249 }
1250 } else {
1251 None
1252 };
1253
1254 let final_name = modified_name.unwrap_or(name);
1256 let final_name_str = self.interner.resolve(final_name);
1257
1258 if (final_name_str == "SharedMap" || final_name_str == "ORMap") && self.check_from() {
1260 self.advance(); let key_type = self.consume_field_type_with_params(type_params);
1262 if self.check_to() {
1264 self.advance(); }
1266 let value_type = self.consume_field_type_with_params(type_params);
1267 return FieldType::Generic { base: final_name, params: vec![key_type, value_type] };
1268 }
1269
1270 if self.check_preposition("of") {
1272 self.advance();
1273 let param = self.consume_field_type_with_params(type_params);
1274 return FieldType::Generic { base: final_name, params: vec![param] };
1275 }
1276
1277 if final_name_str == "Divergent" {
1279 let param = self.consume_field_type_with_params(type_params);
1281 return FieldType::Generic { base: final_name, params: vec![param] };
1282 }
1283
1284 match final_name_str {
1286 "Int" | "Nat" | "Text" | "Bool" | "Real" | "Unit" => FieldType::Primitive(final_name),
1287 _ => FieldType::Named(final_name),
1288 }
1289 } else {
1290 FieldType::Primitive(self.interner.intern("Unknown"))
1291 }
1292 }
1293}
1294
1295#[cfg(test)]
1299mod tests {
1300 use super::*;
1301 use crate::Lexer;
1302 use crate::mwe;
1303
1304 fn make_tokens(source: &str, interner: &mut Interner) -> Vec<Token> {
1305 let mut lexer = Lexer::new(source, interner);
1306 let tokens = lexer.tokenize();
1307 let mwe_trie = mwe::build_mwe_trie();
1308 mwe::apply_mwe_pipeline(tokens, &mwe_trie, interner)
1309 }
1310
1311 #[test]
1312 fn discovery_finds_generic_in_definition_block() {
1313 let source = "## Definition\nA Stack is a generic collection.";
1314 let mut interner = Interner::new();
1315 let tokens = make_tokens(source, &mut interner);
1316
1317 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1318 let registry = discovery.run();
1319
1320 let stack = interner.intern("Stack");
1321 assert!(registry.is_generic(stack), "Stack should be discovered as generic");
1322 }
1323
1324 #[test]
1325 fn discovery_parses_struct_with_fields() {
1326 let source = r#"## Definition
1327A Point has:
1328 an x, which is Int.
1329 a y, which is Int.
1330"#;
1331 let mut interner = Interner::new();
1332 let tokens = make_tokens(source, &mut interner);
1333
1334 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1335 let registry = discovery.run();
1336
1337 let point = interner.intern("Point");
1338 assert!(registry.is_type(point), "Point should be registered");
1339
1340 if let Some(TypeDef::Struct { fields, generics, .. }) = registry.get(point) {
1341 assert_eq!(fields.len(), 2, "Point should have 2 fields, got {:?}", fields);
1342 assert_eq!(interner.resolve(fields[0].name), "x");
1343 assert_eq!(interner.resolve(fields[1].name), "y");
1344 assert!(generics.is_empty(), "Point should have no generics");
1345 } else {
1346 panic!("Point should be a struct with fields");
1347 }
1348 }
1349
1350 #[test]
1351 fn discovery_works_with_markdown_header() {
1352 let source = r#"# Geometry
1354
1355## Definition
1356A Point has:
1357 an x, which is Int.
1358"#;
1359 let mut interner = Interner::new();
1360 let tokens = make_tokens(source, &mut interner);
1361
1362 for (i, tok) in tokens.iter().enumerate() {
1364 eprintln!("Token {}: {:?}", i, tok.kind);
1365 }
1366
1367 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1368 let registry = discovery.run();
1369 let point = interner.intern("Point");
1370 assert!(registry.is_type(point), "Point should be discovered even with # header");
1371 }
1372
1373 #[test]
1374 fn discovery_parses_portable_enum() {
1375 let source = r#"## Definition
1376A Command is Portable and is either:
1377 a Start.
1378 a Stop.
1379 a Pause.
1380"#;
1381 let mut interner = Interner::new();
1382 let tokens = make_tokens(source, &mut interner);
1383
1384 eprintln!("Tokens for portable enum:");
1386 for (i, tok) in tokens.iter().enumerate() {
1387 eprintln!("Token {}: {:?} ({})", i, tok.kind, interner.resolve(tok.lexeme));
1388 }
1389
1390 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1391 let registry = discovery.run();
1392
1393 let command = interner.intern("Command");
1394 assert!(registry.is_type(command), "Command should be registered as type");
1395
1396 if let Some(TypeDef::Enum { variants, is_portable, .. }) = registry.get(command) {
1397 eprintln!("Command is_portable: {}", is_portable);
1398 eprintln!("Variants: {:?}", variants.iter().map(|v| interner.resolve(v.name)).collect::<Vec<_>>());
1399 assert!(*is_portable, "Command should be portable");
1400 assert_eq!(variants.len(), 3, "Command should have 3 variants");
1401 } else {
1402 panic!("Command should be an enum, got: {:?}", registry.get(command));
1403 }
1404 }
1405
1406 #[test]
1407 fn discovery_parses_lww_int_field() {
1408 let source = r#"## Definition
1409A Setting is Shared and has:
1410 a volume, which is LastWriteWins of Int.
1411"#;
1412 let mut interner = Interner::new();
1413 let tokens = make_tokens(source, &mut interner);
1414
1415 eprintln!("Tokens for LWW of Int:");
1417 for (i, tok) in tokens.iter().enumerate() {
1418 eprintln!("{:3}: {:?} ({})", i, tok.kind, interner.resolve(tok.lexeme));
1419 }
1420
1421 let mut discovery = DiscoveryPass::new(&tokens, &mut interner);
1422 let registry = discovery.run();
1423
1424 let setting = interner.intern("Setting");
1425 assert!(registry.is_type(setting), "Setting should be registered");
1426
1427 if let Some(TypeDef::Struct { fields, is_shared, .. }) = registry.get(setting) {
1428 eprintln!("is_shared: {}", is_shared);
1429 eprintln!("Fields: {:?}", fields.len());
1430 for f in fields {
1431 eprintln!(" field: {} = {:?}", interner.resolve(f.name), f.ty);
1432 }
1433 assert!(*is_shared, "Setting should be shared");
1434 assert_eq!(fields.len(), 1, "Setting should have 1 field");
1435 } else {
1436 panic!("Setting should be a struct, got: {:?}", registry.get(setting));
1437 }
1438 }
1439}