moxml 0.5.175 → 0.5.176

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14463613d8081db0efee985379ddcae7740f8044d9f0f7a8192f5f1828337a27
4
- data.tar.gz: 7fcb65b5d65fedc5d9766d31f409441eafba5f78b8212478a854bcd17ffdf167
3
+ metadata.gz: b00ba396700e1ee797651d8db90cc283733f062021e05d8c807c751a07442414
4
+ data.tar.gz: f0f35b75c740842a6bb4a21c0fde3b944e5994c2935a1de0e57b6671d02eb2fe
5
5
  SHA512:
6
- metadata.gz: b7c402c813be0883260893a9e47290653e61104cf870702d7d46b155299d87752f8ec5662455ebb0b75c4b776d8962502d7ed223a3b5fca0764fb2600ae67488
7
- data.tar.gz: cc0a50d550e12d1496c0f31a623c9e1c3a7569fe1d65b7796b97a1a30d63d994812d02c28cd6cfcfd22352a11d0fc9ed25d4a2488bda768ac0898342dad7837a
6
+ metadata.gz: 8302fa56d49d9e6965559bf2be0db79afc61b5d6696cbe34aa5f6ff07381af94261b2988f858d76d5625cb7796b84c70999a6e899e7d5b5c61cdd58134dba462
7
+ data.tar.gz: 349a9bf1e50540698ee21572f29c93db127d206bca9ab622fed3c989ed601af0109181b9b107ac06c27f17432cf98402ab2118d3ba6f8b0976b8ee7542fd74d8
data/Rakefile CHANGED
@@ -355,13 +355,44 @@ namespace :conformance do
355
355
  task :export do
356
356
  require "json"
357
357
  require_relative "lib/moxml"
358
- dataset = Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES
358
+ lexical = Moxml::XSD::BuiltinLexical::CONFORMANCE_CASES
359
+ regex = Moxml::XSD::SchemaRules::XsdRegex::CONFORMANCE_CASES
360
+ wildcards = Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES
361
+ entities = Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES
359
362
  payload = {
360
- schema: 1,
363
+ schema: 3,
361
364
  generator: "moxml #{Moxml::VERSION}",
362
365
  cases: {
363
- "builtin_lexical" => dataset.map do |type, lexical, expected|
364
- { "type" => type, "lexical" => lexical, "valid" => expected }
366
+ "builtin_lexical" => lexical.map do |type, lexical_value, expected|
367
+ { "type" => type, "lexical" => lexical_value, "valid" => expected }
368
+ end,
369
+ "xsd_regex_translation" => regex.map do |pattern, value, matches, translatable|
370
+ {
371
+ "pattern" => pattern, "value" => value,
372
+ "matches" => matches, "translatable" => translatable
373
+ }
374
+ end,
375
+ "schema_wildcards" => wildcards.map do |row|
376
+ constraint, process, not_namespace, not_qname, item_ns, item_qname,
377
+ defined, sibling_names, covers, exclusion = row
378
+ {
379
+ "constraint" => constraint, "process" => process,
380
+ "not_namespace" => not_namespace, "not_qname" => not_qname,
381
+ "item_namespace" => item_ns, "item_qname" => item_qname,
382
+ "defined" => defined, "sibling_names" => sibling_names,
383
+ "covers" => covers, "exclusion" => exclusion
384
+ }
385
+ end,
386
+ "entity_registry" => entities.map do |row|
387
+ seed, qname, qcp, restore_entities, mode, declared, codepoint,
388
+ names, primary, standard, restore = row
389
+ {
390
+ "seed" => seed, "qname" => qname, "qcp" => qcp,
391
+ "restore_entities" => restore_entities, "mode" => mode,
392
+ "declared" => declared, "codepoint" => codepoint,
393
+ "names" => names, "primary" => primary,
394
+ "standard" => standard, "restore" => restore
395
+ }
365
396
  end,
366
397
  },
367
398
  }
@@ -369,7 +400,8 @@ namespace :conformance do
369
400
  FileUtils.mkdir_p(dir)
370
401
  target = File.join(dir, "moxml-conformance.json")
371
402
  File.write(target, "#{JSON.pretty_generate(payload)}\n")
372
- puts "conformance: exported #{dataset.size} cases to #{target}"
403
+ total = lexical.size + regex.size + wildcards.size + entities.size
404
+ puts "conformance: exported #{total} cases to #{target}"
373
405
  end
374
406
  end
375
407
 
@@ -1,6 +1,6 @@
1
1
  {
2
- "schema": 1,
3
- "generator": "moxml 0.5.171",
2
+ "schema": 3,
3
+ "generator": "moxml 0.5.175",
4
4
  "cases": {
5
5
  "builtin_lexical": [
6
6
  {
@@ -408,6 +408,631 @@
408
408
  "lexical": "a:b",
409
409
  "valid": false
410
410
  }
411
+ ],
412
+ "xsd_regex_translation": [
413
+ {
414
+ "pattern": "\\p{IsBasicLatin}+",
415
+ "value": "abc",
416
+ "matches": true,
417
+ "translatable": true
418
+ },
419
+ {
420
+ "pattern": "\\p{IsBasicLatin}+",
421
+ "value": "€",
422
+ "matches": false,
423
+ "translatable": true
424
+ },
425
+ {
426
+ "pattern": "\\P{IsLatin-1Supplement}",
427
+ "value": "a",
428
+ "matches": true,
429
+ "translatable": true
430
+ },
431
+ {
432
+ "pattern": "\\P{IsLatin-1Supplement}",
433
+ "value": "é",
434
+ "matches": false,
435
+ "translatable": true
436
+ },
437
+ {
438
+ "pattern": "\\p{IsGreek}",
439
+ "value": "α",
440
+ "matches": true,
441
+ "translatable": true
442
+ },
443
+ {
444
+ "pattern": "\\p{IsGreek}",
445
+ "value": "a",
446
+ "matches": false,
447
+ "translatable": true
448
+ },
449
+ {
450
+ "pattern": "\\p{L}",
451
+ "value": "a",
452
+ "matches": true,
453
+ "translatable": true
454
+ },
455
+ {
456
+ "pattern": "\\p{L}",
457
+ "value": "1",
458
+ "matches": false,
459
+ "translatable": true
460
+ },
461
+ {
462
+ "pattern": "\\p{Nd}{1,3}",
463
+ "value": "5",
464
+ "matches": true,
465
+ "translatable": true
466
+ },
467
+ {
468
+ "pattern": "\\p{Nd}{1,3}",
469
+ "value": "abc",
470
+ "matches": false,
471
+ "translatable": true
472
+ },
473
+ {
474
+ "pattern": "\\d{1,3}",
475
+ "value": "5",
476
+ "matches": false,
477
+ "translatable": false
478
+ },
479
+ {
480
+ "pattern": "\\d{1,3}",
481
+ "value": "abc",
482
+ "matches": false,
483
+ "translatable": false
484
+ },
485
+ {
486
+ "pattern": "[0-9]{1,3}",
487
+ "value": "42",
488
+ "matches": true,
489
+ "translatable": true
490
+ },
491
+ {
492
+ "pattern": "[a-z]*",
493
+ "value": "",
494
+ "matches": true,
495
+ "translatable": true
496
+ },
497
+ {
498
+ "pattern": "a|b",
499
+ "value": "b",
500
+ "matches": true,
501
+ "translatable": true
502
+ },
503
+ {
504
+ "pattern": "a|b",
505
+ "value": "c",
506
+ "matches": false,
507
+ "translatable": true
508
+ },
509
+ {
510
+ "pattern": "a.c",
511
+ "value": "abc",
512
+ "matches": true,
513
+ "translatable": true
514
+ },
515
+ {
516
+ "pattern": "a\\.c",
517
+ "value": "a.c",
518
+ "matches": true,
519
+ "translatable": true
520
+ },
521
+ {
522
+ "pattern": "\\i\\c*",
523
+ "value": "x",
524
+ "matches": false,
525
+ "translatable": false
526
+ },
527
+ {
528
+ "pattern": "[\\p{IsBasicLatin}-[abc]]+",
529
+ "value": "a",
530
+ "matches": false,
531
+ "translatable": false
532
+ },
533
+ {
534
+ "pattern": "\\p{IsNoSuchBlock}",
535
+ "value": "a",
536
+ "matches": false,
537
+ "translatable": false
538
+ }
539
+ ],
540
+ "schema_wildcards": [
541
+ {
542
+ "constraint": "##any",
543
+ "process": "strict",
544
+ "not_namespace": null,
545
+ "not_qname": [],
546
+ "item_namespace": "local",
547
+ "item_qname": "{}x",
548
+ "defined": false,
549
+ "sibling_names": [],
550
+ "covers": true,
551
+ "exclusion": null
552
+ },
553
+ {
554
+ "constraint": "##any",
555
+ "process": "strict",
556
+ "not_namespace": null,
557
+ "not_qname": [],
558
+ "item_namespace": "tns",
559
+ "item_qname": "{}x",
560
+ "defined": false,
561
+ "sibling_names": [],
562
+ "covers": true,
563
+ "exclusion": null
564
+ },
565
+ {
566
+ "constraint": "##other",
567
+ "process": "strict",
568
+ "not_namespace": null,
569
+ "not_qname": [],
570
+ "item_namespace": "local",
571
+ "item_qname": "{}x",
572
+ "defined": false,
573
+ "sibling_names": [],
574
+ "covers": false,
575
+ "exclusion": null
576
+ },
577
+ {
578
+ "constraint": "##other",
579
+ "process": "strict",
580
+ "not_namespace": null,
581
+ "not_qname": [],
582
+ "item_namespace": "tns",
583
+ "item_qname": "{}x",
584
+ "defined": false,
585
+ "sibling_names": [],
586
+ "covers": false,
587
+ "exclusion": null
588
+ },
589
+ {
590
+ "constraint": "##other",
591
+ "process": "strict",
592
+ "not_namespace": null,
593
+ "not_qname": [],
594
+ "item_namespace": "urn:other",
595
+ "item_qname": "{}x",
596
+ "defined": false,
597
+ "sibling_names": [],
598
+ "covers": true,
599
+ "exclusion": null
600
+ },
601
+ {
602
+ "constraint": "##local",
603
+ "process": "strict",
604
+ "not_namespace": null,
605
+ "not_qname": [],
606
+ "item_namespace": "local",
607
+ "item_qname": "{}x",
608
+ "defined": false,
609
+ "sibling_names": [],
610
+ "covers": true,
611
+ "exclusion": null
612
+ },
613
+ {
614
+ "constraint": "##local",
615
+ "process": "strict",
616
+ "not_namespace": null,
617
+ "not_qname": [],
618
+ "item_namespace": "urn:b",
619
+ "item_qname": "{}x",
620
+ "defined": false,
621
+ "sibling_names": [],
622
+ "covers": false,
623
+ "exclusion": null
624
+ },
625
+ {
626
+ "constraint": "##targetNamespace urn:b",
627
+ "process": "strict",
628
+ "not_namespace": null,
629
+ "not_qname": [],
630
+ "item_namespace": "tns",
631
+ "item_qname": "{}x",
632
+ "defined": false,
633
+ "sibling_names": [],
634
+ "covers": true,
635
+ "exclusion": null
636
+ },
637
+ {
638
+ "constraint": "##targetNamespace urn:b",
639
+ "process": "strict",
640
+ "not_namespace": null,
641
+ "not_qname": [],
642
+ "item_namespace": "local",
643
+ "item_qname": "{}x",
644
+ "defined": false,
645
+ "sibling_names": [],
646
+ "covers": false,
647
+ "exclusion": null
648
+ },
649
+ {
650
+ "constraint": "urn:a urn:b",
651
+ "process": "strict",
652
+ "not_namespace": null,
653
+ "not_qname": [],
654
+ "item_namespace": "urn:a",
655
+ "item_qname": "{}x",
656
+ "defined": false,
657
+ "sibling_names": [],
658
+ "covers": true,
659
+ "exclusion": null
660
+ },
661
+ {
662
+ "constraint": "urn:a urn:b",
663
+ "process": "strict",
664
+ "not_namespace": null,
665
+ "not_qname": [],
666
+ "item_namespace": "urn:c",
667
+ "item_qname": "{}x",
668
+ "defined": false,
669
+ "sibling_names": [],
670
+ "covers": false,
671
+ "exclusion": null
672
+ },
673
+ {
674
+ "constraint": "urn:a",
675
+ "process": "lax",
676
+ "not_namespace": null,
677
+ "not_qname": [],
678
+ "item_namespace": "urn:a",
679
+ "item_qname": "{}x",
680
+ "defined": false,
681
+ "sibling_names": [],
682
+ "covers": true,
683
+ "exclusion": null
684
+ },
685
+ {
686
+ "constraint": "urn:a",
687
+ "process": "skip",
688
+ "not_namespace": null,
689
+ "not_qname": [],
690
+ "item_namespace": "urn:a",
691
+ "item_qname": "{}x",
692
+ "defined": false,
693
+ "sibling_names": [],
694
+ "covers": true,
695
+ "exclusion": null
696
+ },
697
+ {
698
+ "constraint": "##any",
699
+ "process": "strict",
700
+ "not_namespace": null,
701
+ "not_qname": [
702
+ "x"
703
+ ],
704
+ "item_namespace": "local",
705
+ "item_qname": "{}x",
706
+ "defined": false,
707
+ "sibling_names": [],
708
+ "covers": true,
709
+ "exclusion": "not_qname"
710
+ },
711
+ {
712
+ "constraint": "##any",
713
+ "process": "strict",
714
+ "not_namespace": null,
715
+ "not_qname": [
716
+ "x"
717
+ ],
718
+ "item_namespace": "local",
719
+ "item_qname": "{}y",
720
+ "defined": false,
721
+ "sibling_names": [],
722
+ "covers": true,
723
+ "exclusion": null
724
+ },
725
+ {
726
+ "constraint": "##any",
727
+ "process": "lax",
728
+ "not_namespace": null,
729
+ "not_qname": [
730
+ "x"
731
+ ],
732
+ "item_namespace": "local",
733
+ "item_qname": "{}x",
734
+ "defined": true,
735
+ "sibling_names": [],
736
+ "covers": true,
737
+ "exclusion": "not_qname"
738
+ },
739
+ {
740
+ "constraint": "##any",
741
+ "process": "skip",
742
+ "not_namespace": null,
743
+ "not_qname": [
744
+ "x"
745
+ ],
746
+ "item_namespace": "local",
747
+ "item_qname": "{}x",
748
+ "defined": true,
749
+ "sibling_names": [],
750
+ "covers": true,
751
+ "exclusion": "not_qname"
752
+ },
753
+ {
754
+ "constraint": "##any",
755
+ "process": "strict",
756
+ "not_namespace": null,
757
+ "not_qname": [
758
+ "##defined"
759
+ ],
760
+ "item_namespace": "local",
761
+ "item_qname": "{}x",
762
+ "defined": false,
763
+ "sibling_names": [],
764
+ "covers": true,
765
+ "exclusion": null
766
+ },
767
+ {
768
+ "constraint": "##any",
769
+ "process": "strict",
770
+ "not_namespace": null,
771
+ "not_qname": [
772
+ "##defined"
773
+ ],
774
+ "item_namespace": "local",
775
+ "item_qname": "{}x",
776
+ "defined": true,
777
+ "sibling_names": [],
778
+ "covers": true,
779
+ "exclusion": "not_qname"
780
+ },
781
+ {
782
+ "constraint": "##any",
783
+ "process": "strict",
784
+ "not_namespace": null,
785
+ "not_qname": [
786
+ "##definedSibling"
787
+ ],
788
+ "item_namespace": "local",
789
+ "item_qname": "{}x",
790
+ "defined": false,
791
+ "sibling_names": [
792
+ "{}y"
793
+ ],
794
+ "covers": true,
795
+ "exclusion": null
796
+ },
797
+ {
798
+ "constraint": "##any",
799
+ "process": "strict",
800
+ "not_namespace": null,
801
+ "not_qname": [
802
+ "##definedSibling"
803
+ ],
804
+ "item_namespace": "local",
805
+ "item_qname": "{}x",
806
+ "defined": false,
807
+ "sibling_names": [
808
+ "{}x"
809
+ ],
810
+ "covers": true,
811
+ "exclusion": "not_qname"
812
+ },
813
+ {
814
+ "constraint": "urn:a",
815
+ "process": "strict",
816
+ "not_namespace": "urn:z",
817
+ "not_qname": [],
818
+ "item_namespace": "urn:a",
819
+ "item_qname": "{}x",
820
+ "defined": false,
821
+ "sibling_names": [],
822
+ "covers": true,
823
+ "exclusion": null
824
+ },
825
+ {
826
+ "constraint": "urn:a",
827
+ "process": "strict",
828
+ "not_namespace": "urn:z",
829
+ "not_qname": [],
830
+ "item_namespace": "urn:z",
831
+ "item_qname": "{}x",
832
+ "defined": false,
833
+ "sibling_names": [],
834
+ "covers": false,
835
+ "exclusion": "not_namespace"
836
+ },
837
+ {
838
+ "constraint": "urn:a",
839
+ "process": "lax",
840
+ "not_namespace": "urn:z",
841
+ "not_qname": [],
842
+ "item_namespace": "urn:z",
843
+ "item_qname": "{}x",
844
+ "defined": false,
845
+ "sibling_names": [],
846
+ "covers": false,
847
+ "exclusion": "not_namespace"
848
+ }
849
+ ],
850
+ "entity_registry": [
851
+ {
852
+ "seed": {
853
+ "amp": "&"
854
+ },
855
+ "qname": "amp",
856
+ "qcp": 38,
857
+ "restore_entities": false,
858
+ "mode": "lenient",
859
+ "declared": true,
860
+ "codepoint": 38,
861
+ "names": [
862
+ "amp"
863
+ ],
864
+ "primary": "amp",
865
+ "standard": true,
866
+ "restore": true
867
+ },
868
+ {
869
+ "seed": {
870
+ "NonBreakingSpace": " ",
871
+ "nbsp": " "
872
+ },
873
+ "qname": "nbsp",
874
+ "qcp": 160,
875
+ "restore_entities": true,
876
+ "mode": "lenient",
877
+ "declared": true,
878
+ "codepoint": 160,
879
+ "names": [
880
+ "NonBreakingSpace",
881
+ "nbsp"
882
+ ],
883
+ "primary": "nbsp",
884
+ "standard": false,
885
+ "restore": true
886
+ },
887
+ {
888
+ "seed": {
889
+ "nbsp": " "
890
+ },
891
+ "qname": "nbsp",
892
+ "qcp": 160,
893
+ "restore_entities": false,
894
+ "mode": "lenient",
895
+ "declared": true,
896
+ "codepoint": 160,
897
+ "names": [
898
+ "nbsp"
899
+ ],
900
+ "primary": "nbsp",
901
+ "standard": false,
902
+ "restore": false
903
+ },
904
+ {
905
+ "seed": {
906
+ "nbsp": " "
907
+ },
908
+ "qname": "nbsp",
909
+ "qcp": 160,
910
+ "restore_entities": true,
911
+ "mode": "strict",
912
+ "declared": true,
913
+ "codepoint": 160,
914
+ "names": [
915
+ "nbsp"
916
+ ],
917
+ "primary": "nbsp",
918
+ "standard": false,
919
+ "restore": true
920
+ },
921
+ {
922
+ "seed": {
923
+ "AElig": "Æ"
924
+ },
925
+ "qname": "AElig",
926
+ "qcp": 198,
927
+ "restore_entities": true,
928
+ "mode": "lenient",
929
+ "declared": true,
930
+ "codepoint": 198,
931
+ "names": [
932
+ "AElig"
933
+ ],
934
+ "primary": "AElig",
935
+ "standard": false,
936
+ "restore": true
937
+ },
938
+ {
939
+ "seed": {
940
+ "fjlig": "fj"
941
+ },
942
+ "qname": "fjlig",
943
+ "qcp": 102,
944
+ "restore_entities": true,
945
+ "mode": "lenient",
946
+ "declared": true,
947
+ "codepoint": 102,
948
+ "names": [
949
+ "fjlig"
950
+ ],
951
+ "primary": "fjlig",
952
+ "standard": false,
953
+ "restore": true
954
+ },
955
+ {
956
+ "seed": {
957
+ "DotDot": " ⃜"
958
+ },
959
+ "qname": "DotDot",
960
+ "qcp": 32,
961
+ "restore_entities": true,
962
+ "mode": "lenient",
963
+ "declared": true,
964
+ "codepoint": 32,
965
+ "names": [
966
+ "DotDot"
967
+ ],
968
+ "primary": "DotDot",
969
+ "standard": false,
970
+ "restore": true
971
+ },
972
+ {
973
+ "seed": {
974
+ "NotEqualTilde": "≂̸"
975
+ },
976
+ "qname": "NotEqualTilde",
977
+ "qcp": 8770,
978
+ "restore_entities": true,
979
+ "mode": "lenient",
980
+ "declared": true,
981
+ "codepoint": 8770,
982
+ "names": [
983
+ "NotEqualTilde"
984
+ ],
985
+ "primary": "NotEqualTilde",
986
+ "standard": false,
987
+ "restore": true
988
+ },
989
+ {
990
+ "seed": {
991
+ "esc": "\\u00A0"
992
+ },
993
+ "qname": "esc",
994
+ "qcp": 160,
995
+ "restore_entities": true,
996
+ "mode": "lenient",
997
+ "declared": true,
998
+ "codepoint": 160,
999
+ "names": [
1000
+ "esc"
1001
+ ],
1002
+ "primary": "esc",
1003
+ "standard": false,
1004
+ "restore": true
1005
+ },
1006
+ {
1007
+ "seed": {
1008
+ "bad": ""
1009
+ },
1010
+ "qname": "bad",
1011
+ "qcp": 0,
1012
+ "restore_entities": true,
1013
+ "mode": "lenient",
1014
+ "declared": false,
1015
+ "codepoint": null,
1016
+ "names": [],
1017
+ "primary": null,
1018
+ "standard": false,
1019
+ "restore": false
1020
+ },
1021
+ {
1022
+ "seed": {
1023
+ "nbsp": " "
1024
+ },
1025
+ "qname": "nope",
1026
+ "qcp": 9999,
1027
+ "restore_entities": true,
1028
+ "mode": "lenient",
1029
+ "declared": false,
1030
+ "codepoint": null,
1031
+ "names": [],
1032
+ "primary": null,
1033
+ "standard": false,
1034
+ "restore": false
1035
+ }
411
1036
  ]
412
1037
  }
413
1038
  }
@@ -30,6 +30,58 @@ module Moxml
30
30
  # Standard XML predefined entities (XML spec §4.6)
31
31
  STANDARD_CODEPOINTS = Set[0x26, 0x3C, 0x3E, 0x22, 0x27].freeze
32
32
 
33
+ # The entity-registry conformance dataset (#496): lookup and
34
+ # restoration answers as data, pinned BY the Ruby suite
35
+ # (spec/moxml/entity_registry_conformance_spec.rb) and exported
36
+ # for the Rust/TS runners. Row shape: [seed, qname, qcp,
37
+ # restore_entities, restoration_mode, declared, codepoint, names,
38
+ # primary, standard, restore]. Each row seeds a fresh :custom
39
+ # registry through the public provider path — so the seed values
40
+ # are LEXICAL forms (raw characters, multi-character sequences,
41
+ # "\\uXXXX" escapes), exercising parse_codepoint exactly as a
42
+ # custom provider does — then queries one name and one codepoint
43
+ # and pins every face answer:
44
+ #
45
+ # * declared?/codepoint_for_name on qname
46
+ # * names_for_codepoint/primary_name_for_codepoint on qcp
47
+ # * standard_entity?/should_restore? on qcp under the row's config
48
+ #
49
+ # The committed semantics the rows pin: a multi-character lexical
50
+ # (an entity naming a sequence, e.g. "fjlig" -> "fj") maps to its
51
+ # FIRST character's codepoint; an empty lexical is dropped; the
52
+ # reverse map keeps insertion order with the lowercase name
53
+ # preferred as primary (falling back to the first name); the five
54
+ # predefined entities restore regardless of config; every other
55
+ # known codepoint restores only with restore_entities on (the
56
+ # strict mode answers as lenient until DTD parsing exists); an
57
+ # unknown codepoint never restores.
58
+ ENTITY_CONFORMANCE_CASES = [
59
+ [{ "amp" => "&" }, "amp", 38, false, "lenient",
60
+ true, 38, ["amp"], "amp", true, true],
61
+ [{ "NonBreakingSpace" => "\u00A0", "nbsp" => "\u00A0" },
62
+ "nbsp", 160, true, "lenient",
63
+ true, 160, ["NonBreakingSpace", "nbsp"], "nbsp", false, true],
64
+ [{ "nbsp" => "\u00A0" }, "nbsp", 160, false, "lenient",
65
+ true, 160, ["nbsp"], "nbsp", false, false],
66
+ [{ "nbsp" => "\u00A0" }, "nbsp", 160, true, "strict",
67
+ true, 160, ["nbsp"], "nbsp", false, true],
68
+ [{ "AElig" => "Æ" }, "AElig", 198, true, "lenient",
69
+ true, 198, ["AElig"], "AElig", false, true],
70
+ [{ "fjlig" => "fj" }, "fjlig", 102, true, "lenient",
71
+ true, 102, ["fjlig"], "fjlig", false, true],
72
+ [{ "DotDot" => "\u0020\u20DC" }, "DotDot", 32, true, "lenient",
73
+ true, 32, ["DotDot"], "DotDot", false, true],
74
+ [{ "NotEqualTilde" => "≂̸" }, "NotEqualTilde", 8770, true,
75
+ "lenient",
76
+ true, 8770, ["NotEqualTilde"], "NotEqualTilde", false, true],
77
+ [{ "esc" => "\\u00A0" }, "esc", 160, true, "lenient",
78
+ true, 160, ["esc"], "esc", false, true],
79
+ [{ "bad" => "" }, "bad", 0, true, "lenient",
80
+ false, nil, [], nil, false, false],
81
+ [{ "nbsp" => "\u00A0" }, "nope", 9999, true, "lenient",
82
+ false, nil, [], nil, false, false],
83
+ ].map(&:freeze).freeze
84
+
33
85
  class << self
34
86
  # Get the raw entity data from the bundled JSON source
35
87
  # @return [Hash{String => String}] entity name to character mapping
data/lib/moxml/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Moxml
4
- VERSION = "0.5.175"
4
+ VERSION = "0.5.176"
5
5
  end
@@ -78,17 +78,83 @@ module Moxml
78
78
  nil
79
79
  end
80
80
 
81
+ # The wildcard-face conformance dataset (#496): admission and
82
+ # negative-exclusion answers as data, pinned BY the Ruby suite
83
+ # (spec/moxml/wildcard_conformance_spec.rb) and exported for
84
+ # the Rust/TS runners. Row shape: [constraint, process,
85
+ # not_namespace, not_qname tokens, item_namespace, item_qname,
86
+ # defined, sibling_names, covers, exclusion]. Disallowed
87
+ # entries keep the pre-resolution token forms the constructor
88
+ # receives; item qnames are in the Clark form the face
89
+ # receives; item namespaces use the face's spellings
90
+ # ("local", "tns", or the URI).
91
+ WILDCARD_CASES = [
92
+ ["##any", "strict", nil, [], "local", "{}x", false, [], true, nil],
93
+ ["##any", "strict", nil, [], "tns", "{}x", false, [], true, nil],
94
+ ["##other", "strict", nil, [], "local", "{}x", false, [], false, nil],
95
+ ["##other", "strict", nil, [], "tns", "{}x", false, [], false, nil],
96
+ ["##other", "strict", nil, [], "urn:other", "{}x", false, [], true, nil],
97
+ ["##local", "strict", nil, [], "local", "{}x", false, [], true, nil],
98
+ ["##local", "strict", nil, [], "urn:b", "{}x", false, [], false, nil],
99
+ ["##targetNamespace urn:b", "strict", nil, [], "tns", "{}x", false, [],
100
+ true, nil],
101
+ ["##targetNamespace urn:b", "strict", nil, [], "local", "{}x", false,
102
+ [], false, nil],
103
+ ["urn:a urn:b", "strict", nil, [], "urn:a", "{}x", false, [], true,
104
+ nil],
105
+ ["urn:a urn:b", "strict", nil, [], "urn:c", "{}x", false, [], false,
106
+ nil],
107
+ ["urn:a", "lax", nil, [], "urn:a", "{}x", false, [], true, nil],
108
+ ["urn:a", "skip", nil, [], "urn:a", "{}x", false, [], true, nil],
109
+ ["##any", "strict", nil, ["x"], "local", "{}x", false, [], true,
110
+ "not_qname"],
111
+ ["##any", "strict", nil, ["x"], "local", "{}y", false, [], true, nil],
112
+ ["##any", "lax", nil, ["x"], "local", "{}x", true, [], true,
113
+ "not_qname"],
114
+ ["##any", "skip", nil, ["x"], "local", "{}x", true, [], true,
115
+ "not_qname"],
116
+ ["##any", "strict", nil, ["##defined"], "local", "{}x", false, [],
117
+ true, nil],
118
+ ["##any", "strict", nil, ["##defined"], "local", "{}x", true, [],
119
+ true, "not_qname"],
120
+ ["##any", "strict", nil, ["##definedSibling"], "local", "{}x", false,
121
+ ["{}y"], true, nil],
122
+ ["##any", "strict", nil, ["##definedSibling"], "local", "{}x", false,
123
+ ["{}x"], true, "not_qname"],
124
+ ["urn:a", "strict", "urn:z", [], "urn:a", "{}x", false, [], true,
125
+ nil],
126
+ ["urn:a", "strict", "urn:z", [], "urn:z", "{}x", false, [], false,
127
+ "not_namespace"],
128
+ ["urn:a", "lax", "urn:z", [], "urn:z", "{}x", false, [], false,
129
+ "not_namespace"],
130
+ ].map(&:freeze).freeze
131
+
81
132
  private
82
133
 
83
134
  def namespace_constraint(value, not_namespace)
84
135
  if not_namespace
85
- [:not, not_namespace.split(/\s+/).reject(&:empty?)]
136
+ [:not, namespace_entries(not_namespace)]
86
137
  elsif value.nil? || value == "##any" || value == ""
87
138
  [:any, []]
88
139
  elsif value == "##other"
89
140
  [:other, []]
90
141
  else
91
- [:list, value.split(/\s+/).reject(&:empty?)]
142
+ [:list, namespace_entries(value)]
143
+ end
144
+ end
145
+
146
+ # Namespace entries carry one internal convention: the
147
+ # special tokens keep their string spellings (entry_covers?
148
+ # maps them to :tns/:local); real URIs become Symbols, the
149
+ # same shape covers? receives. A String entry would never
150
+ # equal a Symbol namespace — the comparison would silently
151
+ # under-report on every :list and :not wildcard.
152
+ def namespace_entries(value)
153
+ value.to_s.split(/\s+/).reject(&:empty?).map do |entry|
154
+ case entry
155
+ when "##targetNamespace", "##local" then entry
156
+ else entry.to_sym
157
+ end
92
158
  end
93
159
  end
94
160
 
@@ -189,6 +255,35 @@ module Moxml
189
255
  nil
190
256
  end
191
257
 
258
+ # The cross-language conformance dataset (#496): the
259
+ # translator's answers as data, pinned BY the Ruby suite
260
+ # (spec/moxml/xsd_regex_conformance_spec.rb) and exported to
261
+ # conformance/moxml-conformance.json for the Rust/TS runners.
262
+ # Each row: [pattern, value, matches, translatable].
263
+ CONFORMANCE_CASES = [
264
+ ["\\p{IsBasicLatin}+", "abc", true, true],
265
+ ["\\p{IsBasicLatin}+", "€", false, true],
266
+ ["\\P{IsLatin-1Supplement}", "a", true, true],
267
+ ["\\P{IsLatin-1Supplement}", "é", false, true],
268
+ ["\\p{IsGreek}", "α", true, true],
269
+ ["\\p{IsGreek}", "a", false, true],
270
+ ["\\p{L}", "a", true, true],
271
+ ["\\p{L}", "1", false, true],
272
+ ["\\p{Nd}{1,3}", "5", true, true],
273
+ ["\\p{Nd}{1,3}", "abc", false, true],
274
+ ["\\d{1,3}", "5", false, false],
275
+ ["\\d{1,3}", "abc", false, false],
276
+ ["[0-9]{1,3}", "42", true, true],
277
+ ["[a-z]*", "", true, true],
278
+ ["a|b", "b", true, true],
279
+ ["a|b", "c", false, true],
280
+ ["a.c", "abc", true, true],
281
+ ["a\\.c", "a.c", true, true],
282
+ ["\\i\\c*", "x", false, false],
283
+ ["[\\p{IsBasicLatin}-[abc]]+", "a", false, false],
284
+ ["\\p{IsNoSuchBlock}", "a", false, false],
285
+ ].map(&:freeze).freeze
286
+
192
287
  def scan(pattern)
193
288
  out = +""
194
289
  index = 0
data/lib/moxml.rb CHANGED
@@ -101,7 +101,7 @@ module Moxml
101
101
  autoload :Adapter, "moxml/adapter"
102
102
  autoload :XPath, "moxml/xpath"
103
103
  autoload :SAX, "moxml/sax"
104
- autoload :Signature, "moxml/signature"
104
+ autoload :Signature, "moxml/signature" unless RUBY_ENGINE == "opal"
105
105
  autoload :C14n, "moxml/c14n"
106
106
  autoload :Reference, "moxml/reference"
107
107
  autoload :XSLT, "moxml/xslt"
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Entities (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The entity-registry conformance dataset (#496): lookup and
8
+ # restoration answers AS DATA, pinned by the Ruby suite and exported
9
+ # for the Rust/TS runners. A row failing here breaks the
10
+ # cross-language contract. Rows seed a fresh :custom registry through
11
+ # the public provider path — seed values are lexical forms — then pin
12
+ # every face answer for one name and one codepoint query.
13
+ RSpec.describe "entity-registry conformance dataset" do
14
+ it "carries the dataset" do
15
+ expect(Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES.size).to be >= 10
16
+ end
17
+
18
+ it "answers every row exactly as the dataset expects" do
19
+ Moxml::EntityRegistry::ENTITY_CONFORMANCE_CASES.each do |row|
20
+ seed, qname, qcp, restore_entities, restoration_mode, declared,
21
+ codepoint, names, primary, standard, restore = row
22
+ registry = Moxml::EntityRegistry.new(
23
+ mode: :custom,
24
+ entity_provider: -> { seed },
25
+ )
26
+
27
+ label = "seed #{seed.inspect}, qname #{qname.inspect}, qcp #{qcp}"
28
+
29
+ expect(registry.declared?(qname)).to eql(declared),
30
+ "declared?(#{qname.inspect}) [#{label}]"
31
+ expect(registry.codepoint_for_name(qname)).to eql(codepoint),
32
+ "codepoint_for_name(" \
33
+ "#{qname.inspect}) " \
34
+ "[#{label}]"
35
+ expect(registry.names_for_codepoint(qcp)).to eql(names),
36
+ "names_for_codepoint(" \
37
+ "#{qcp}) [#{label}]"
38
+ expect(registry.primary_name_for_codepoint(qcp)).to eql(primary),
39
+ "primary_name_" \
40
+ "for_codepoint(" \
41
+ "#{qcp}) [#{label}]"
42
+ expect(registry.standard_entity?(qcp)).to eql(standard),
43
+ "standard_entity?(#{qcp}) " \
44
+ "[#{label}]"
45
+ config = Moxml::Config.new
46
+ config.restore_entities = restore_entities
47
+ config.entity_restoration_mode = restoration_mode.to_sym
48
+ expect(registry.should_restore?(qcp, config: config)).to eql(restore),
49
+ "should_restore?(" \
50
+ "#{qcp}) " \
51
+ "[#{label}]"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require "open3"
5
+ require "rbconfig"
6
+ require "tempfile"
7
+ require "opal"
8
+
9
+ RSpec.describe "Signature autoload boundary", :native_fs do
10
+ let(:lib) { File.expand_path("../../lib", __dir__) }
11
+
12
+ it "keeps the native signature module lazy and usable on Ruby" do
13
+ source = <<~RUBY_CODE
14
+ require "moxml"
15
+ abort "signature was loaded eagerly" if $LOADED_FEATURES.any? { |path| path.end_with?("moxml/signature.rb") }
16
+ abort "signature autoload missing" unless Moxml.autoload?(:Signature) == "moxml/signature"
17
+ abort "signature module unavailable" unless Moxml::Signature::DSIG_NS == "http://www.w3.org/2000/09/xmldsig#"
18
+ abort "OpenSSL unavailable" unless defined?(OpenSSL::PKey::RSA)
19
+ puts "native signature available"
20
+ RUBY_CODE
21
+ output, error, status = Open3.capture3(RbConfig.ruby, "-I", lib, "-e", source)
22
+ expect(status.success?).to be(true), error
23
+ expect(output).to include("native signature available")
24
+ end
25
+
26
+ it "does not compile the native OpenSSL dependency into Opal" do
27
+ # Compile the actual entrypoint declaration. This isolates the optional
28
+ # signature boundary from the other modules loaded by the Opal boot file.
29
+ declaration = File.readlines(File.join(lib, "moxml.rb"))
30
+ .find { |line| line.include?('autoload :Signature, "moxml/signature"') }
31
+ expect(declaration).not_to be_nil
32
+
33
+ builder = Opal::Builder.new
34
+ builder.append_paths(lib)
35
+ builder.build("opal")
36
+ builder.build_str(<<~RUBY_CODE, "signature-autoload-boundary")
37
+ module Moxml
38
+ #{declaration}
39
+ end
40
+ raise "native Signature loaded under Opal" if Moxml.const_defined?(:Signature, false)
41
+ puts "Opal signature boundary passed"
42
+ RUBY_CODE
43
+
44
+ Tempfile.create(["moxml-signature-boundary", ".js"]) do |file|
45
+ file.write(builder.to_s)
46
+ file.flush
47
+ output, error, status = Open3.capture3("node", file.path)
48
+ expect(status.success?).to be(true), error
49
+ expect(output).to include("Opal signature boundary passed")
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Validation (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The wildcard-face conformance dataset (#496): admission (covers?)
8
+ # and negative-exclusion answers AS DATA, pinned by the Ruby suite
9
+ # and exported for the Rust/TS runners. A row failing here breaks the
10
+ # cross-language contract. Rows drive the real Wildcard through its
11
+ # public constructor — disallowed entries in their pre-resolution
12
+ # token form, item qnames in the Clark form the face receives.
13
+ RSpec.describe "wildcard-face conformance dataset" do
14
+ it "carries the dataset" do
15
+ expect(Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES.size).to be >= 20
16
+ end
17
+
18
+ it "answers every row exactly as the dataset expects" do
19
+ Moxml::XSD::SchemaRules::Wildcard::WILDCARD_CASES.each do |row|
20
+ constraint, process, not_namespace, not_qname, item_ns, item_qname,
21
+ defined, sibling_names, covers, exclusion = row
22
+ wildcard = Moxml::XSD::SchemaRules::Wildcard.new(
23
+ constraint, process,
24
+ not_namespace: not_namespace, not_qname: not_qname.join(" "),
25
+ element: nil
26
+ )
27
+
28
+ covers_answer = wildcard.covers?(item_ns.to_sym)
29
+ expect(covers_answer).to eql(covers),
30
+ "covers?(#{item_ns.inspect}) under " \
31
+ "#{constraint.inspect}: got " \
32
+ "#{covers_answer.inspect}, dataset expects " \
33
+ "#{covers.inspect}"
34
+
35
+ exclusion_answer = wildcard.negative_exclusion(
36
+ item_qname, item_ns.to_sym,
37
+ defined: defined, sibling_names: sibling_names
38
+ )
39
+ expected_exclusion = exclusion&.to_sym
40
+ expect(exclusion_answer).to eql(expected_exclusion),
41
+ "negative_exclusion(#{item_qname.inspect}, " \
42
+ "#{item_ns.inspect}) under " \
43
+ "#{constraint.inspect}: got " \
44
+ "#{exclusion_answer.inspect}, dataset expects " \
45
+ "#{expected_exclusion.inspect}"
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Processing model §Validation (normative): pinned here.
4
+
5
+ require "spec_helper"
6
+
7
+ # The regex-translator conformance dataset (#496): the translator's
8
+ # answers AS DATA — translatable-or-not, and what the translated
9
+ # regex does to sample values — pinned by the Ruby suite and exported
10
+ # for the Rust/TS runners. A row failing here breaks the
11
+ # cross-language contract.
12
+ RSpec.describe "XSD-regex translator conformance dataset" do
13
+ let(:translator) { Moxml::XSD::SchemaRules::XsdRegex }
14
+
15
+ it "carries the dataset" do
16
+ expect(translator::CONFORMANCE_CASES.size).to be >= 20
17
+ end
18
+
19
+ it "answers every row exactly as the dataset expects" do
20
+ translator::CONFORMANCE_CASES.each do |pattern, value, matches, translatable|
21
+ regex = translator.translate(pattern)
22
+ if translatable
23
+ expect(regex).not_to be_nil,
24
+ "#{pattern.inspect}: dataset says translatable, got nil"
25
+ answer = regex.match?(value)
26
+ expect(answer).to eql(matches),
27
+ "#{pattern.inspect} on #{value.inspect}: got " \
28
+ "#{answer.inspect}, dataset expects #{matches.inspect}"
29
+ else
30
+ expect(regex).to be_nil,
31
+ "#{pattern.inspect}: dataset says NOT translatable, " \
32
+ "got #{regex.inspect}"
33
+ end
34
+ end
35
+ end
36
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: moxml
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.175
4
+ version: 0.5.176
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
@@ -557,6 +557,7 @@ files:
557
557
  - spec/moxml/element_spec.rb
558
558
  - spec/moxml/entity_preservation_spec.rb
559
559
  - spec/moxml/entity_reference_spec.rb
560
+ - spec/moxml/entity_registry_conformance_spec.rb
560
561
  - spec/moxml/entity_registry_spec.rb
561
562
  - spec/moxml/entity_spec.rb
562
563
  - spec/moxml/error_spec.rb
@@ -606,11 +607,13 @@ files:
606
607
  - spec/moxml/signature/key_extractor_spec.rb
607
608
  - spec/moxml/signature/model_spec.rb
608
609
  - spec/moxml/signature/round_trip_spec.rb
610
+ - spec/moxml/signature_autoload_spec.rb
609
611
  - spec/moxml/struct_plan_spec.rb
610
612
  - spec/moxml/struct_plan_typed_spec.rb
611
613
  - spec/moxml/text_spec.rb
612
614
  - spec/moxml/to_html_spec.rb
613
615
  - spec/moxml/version_spec.rb
616
+ - spec/moxml/wildcard_conformance_spec.rb
614
617
  - spec/moxml/xhtml_output_spec.rb
615
618
  - spec/moxml/xml_emitter_spec.rb
616
619
  - spec/moxml/xml_utils/.gitkeep
@@ -643,6 +646,7 @@ files:
643
646
  - spec/moxml/xsd_engine_meta_spec.rb
644
647
  - spec/moxml/xsd_error_contract_spec.rb
645
648
  - spec/moxml/xsd_handle_mismatch_spec.rb
649
+ - spec/moxml/xsd_regex_conformance_spec.rb
646
650
  - spec/moxml/xsd_regex_spec.rb
647
651
  - spec/moxml/xsd_release_spec.rb
648
652
  - spec/moxml/xsd_set_spec.rb