@@ -10,6 +10,7 @@ overlay[local?]
1010module ;
1111
1212private import Location
13+ private import Strings
1314
1415/** Provides the input to `Make`. */
1516signature module InputSig< LocationSig Location> {
@@ -52,7 +53,43 @@ module Make<LocationSig Location, InputSig<Location> Input> {
5253 /** Gets the rank of element `e`, which is used internally in the string encoding. */
5354 int getRank ( Element e ) { e = DenseRank< DenseRankInput > :: denseRank ( result ) }
5455
55- private string encode ( Element e ) { result = getRank ( e ) .toString ( ) }
56+ /** Gets the ASCII printable excluding `.` with zero-based index `code`. */
57+ pragma [ nomagic]
58+ private string interpretAsciiCode ( int code ) {
59+ exists ( int dot , int c |
60+ c = code + 1 and
61+ // `.` is used as element separator, so cannot be used to encode elements
62+ dot = asciiPrintable ( "." ) and
63+ if c < dot then c = asciiPrintable ( result ) else c + 1 = asciiPrintable ( result )
64+ )
65+ }
66+
67+ private int asciiCodes ( ) { result = strictcount ( interpretAsciiCode ( _) ) }
68+
69+ /**
70+ * Gets the `i`th digit (modulo `asciiCodes()`) in a base-`asciiCodes()` integer
71+ * representation of `getRank(e)`.
72+ */
73+ private int getAsciiCodePart ( Element e , int i ) {
74+ result = getRank ( e ) and
75+ i = 0
76+ or
77+ exists ( int mid |
78+ mid = getAsciiCodePart ( e , i - 1 ) and
79+ result = mid / asciiCodes ( ) and
80+ result > 0
81+ )
82+ }
83+
84+ pragma [ nomagic]
85+ private string encode ( Element e ) {
86+ result =
87+ strictconcat ( string s , int i |
88+ s = interpretAsciiCode ( getAsciiCodePart ( e , i ) % asciiCodes ( ) )
89+ |
90+ s order by i
91+ )
92+ }
5693
5794 bindingset [ s]
5895 private Element decode ( string s ) { encode ( result ) = s }
@@ -88,7 +125,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
88125 // Same as
89126 // `result = count(this.indexOf("."))`
90127 // but performs better because it doesn't use an aggregate
91- result = this .regexpReplaceAll ( "[0-9 ]+" , "" ) .length ( )
128+ result = this .regexpReplaceAll ( "[^\\. ]+" , "" ) .length ( )
92129 }
93130
94131 /** Gets the list obtained by appending `suffix` onto this list. */
@@ -123,7 +160,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
123160 // `regexpCapture` will then always join in both groups, only to afterwards filter
124161 // based on the requested group (the group number is not part of the binding set
125162 // of `regexpCapture`)
126- elem = this .regexpCapture ( "^([0-9 ]+)\\..*$" , 1 ) and
163+ elem = this .regexpCapture ( "^([^\\. ]+)\\..*$" , 1 ) and
127164 e = decode ( elem ) and
128165 suffix = this .suffix ( elem .length ( ) + 1 )
129166 )
@@ -133,7 +170,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
133170 bindingset [ this ]
134171 predicate isSnoc ( UnboundList prefix , Element e ) {
135172 // same remark as above about not using multiple capture groups
136- prefix = this .regexpCapture ( "^(|.+\\.)[0-9 ]+\\.$" , 1 ) and
173+ prefix = this .regexpCapture ( "^(|.+\\.)[^\\. ]+\\.$" , 1 ) and
137174 e = decode ( this .substring ( prefix .stringLength ( ) , this .stringLength ( ) - 1 ) )
138175 }
139176
@@ -148,7 +185,7 @@ module Make<LocationSig Location, InputSig<Location> Input> {
148185 */
149186 bindingset [ this ]
150187 UnboundList getProperPrefix ( int i ) {
151- exists ( string regexp , int occurrenceOffset | regexp = "[0-9 ]+\\." |
188+ exists ( string regexp , int occurrenceOffset | regexp = "[^\\. ]+\\." |
152189 exists ( this .regexpFind ( regexp , i , occurrenceOffset ) ) and
153190 result = this .prefix ( occurrenceOffset )
154191 )
0 commit comments