1 module hunt.markdown.internal.util.Parsing;
2 import hunt.Char;
3 import hunt.util.StringBuilder;
4 import hunt.text.Common;
5 import hunt.logging;
6 alias Character = Char;
7 
8 class Parsing {
9 
10     private const string TAGNAME = "[A-Za-z][A-Za-z0-9-]*";
11     private const string ATTRIBUTENAME = "[a-zA-Z_:][a-zA-Z0-9:._-]*";
12     private const string UNQUOTEDVALUE = "[^\"'=<>`\\x00-\\x20]+";
13     private const string SINGLEQUOTEDVALUE = "'[^']*'";
14     private const string DOUBLEQUOTEDVALUE = "\"[^\"]*\"";
15 
16     private const string ATTRIBUTEVALUE = "(?:" ~ UNQUOTEDVALUE ~ "|" ~ SINGLEQUOTEDVALUE ~ "|" ~ DOUBLEQUOTEDVALUE ~ ")";
17     private const string ATTRIBUTEVALUESPEC = "(?:" ~ "\\s*=" ~ "\\s*" ~ ATTRIBUTEVALUE ~ ")";
18     private const string ATTRIBUTE = "(?:" ~ "\\s+" ~ ATTRIBUTENAME ~ ATTRIBUTEVALUESPEC ~ "?)";
19 
20     public enum string OPENTAG = "<" ~ TAGNAME ~ ATTRIBUTE ~ "*" ~ "\\s*/?>";
21     public enum string CLOSETAG = "</" ~ TAGNAME ~ "\\s*[>]";
22 
23     public enum int CODE_BLOCK_INDENT = 4;
24 
25     public static int columnsToNextTabStop(int column) {
26         // Tab stop is 4
27         return 4 - (column % 4);
28     }
29 
30     public static int find(char c, string s, int startIndex) {
31         int length = cast(int)(s.length);
32         for (int i = startIndex; i < length; i++) {
33             if (s[i] == c) {
34                 return i;
35             }
36         }
37         return -1;
38     }
39 
40     public static int findLineBreak(string s, int startIndex) {
41         int length = cast(int)(s.length);
42         for (int i = startIndex; i < length; i++) {
43             switch (s[i]) {
44                 case '\n':
45                 case '\r':
46                     return i;
47                 default:break;
48             }
49         }
50         return -1;
51     }
52 
53     public static bool isBlank(string s) {
54         return findNonSpace(s, 0) == -1;
55     }
56 
57     public static bool isLetter(string s, int index) {
58         // int codePoint = Char.codePointAt(s, index);
59         // return Char.isLetter(codePoint);
60         import std.ascii;
61         auto b = isAlpha(s.charAt(index));
62         return b;
63     }
64 
65     public static bool isSpaceOrTab(string s, int index) {
66         if (index < s.length) {
67             switch (s[index]) {
68                 case ' ':
69                 case '\t':
70                     return true;
71                 default: break;
72             }
73         }
74         return false;
75     }
76 
77     /**
78      * Prepares the input line replacing {@code \0}
79      */
80     public static string prepareLine(string line) {
81         // Avoid building a new string in the majority of cases (no \0)
82         StringBuilder sb = null;
83         int length = cast(int)(line.length);
84         for (int i = 0; i < length; i++) {
85             char c = line[i];
86             switch (c) {
87                 case '\0':
88                     if (sb is null) {
89                         sb = new StringBuilder(length);
90                         sb.append(line, 0, i);
91                     }
92                     sb.append("\uFFFD");
93                     break;
94                 default:
95                     if (sb !is null) {
96                         sb.append(c);
97                     }
98             }
99         }
100 
101         if (sb !is null) {
102             return sb.toString();
103         } else {
104             return line;
105         }
106     }
107 
108     public static int skip(char skip, string s, int startIndex, int endIndex) {
109         for (int i = startIndex; i < endIndex; i++) {
110             if (s[i] != skip) {
111                 return i;
112             }
113         }
114         return endIndex;
115     }
116 
117     public static int skipBackwards(char skip, string s, int startIndex, int lastIndex) {
118         for (int i = startIndex; i >= lastIndex; i--) {
119             if (s[i] != skip) {
120                 return i;
121             }
122         }
123         return lastIndex - 1;
124     }
125 
126     public static int skipSpaceTab(string s, int startIndex, int endIndex) {
127         for (int i = startIndex; i < endIndex; i++) {
128             switch (s[i]) {
129                 case ' ':
130                 case '\t':
131                     break;
132                 default:
133                     return i;
134             }
135         }
136         return endIndex;
137     }
138 
139     public static int skipSpaceTabBackwards(string s, int startIndex, int lastIndex) {
140         for (int i = startIndex; i >= lastIndex; i--) {
141             switch (s[i]) {
142                 case ' ':
143                 case '\t':
144                     break;
145                 default:
146                     return i;
147             }
148         }
149         return lastIndex - 1;
150     }
151 
152     private static int findNonSpace(string s, int startIndex) {
153         int length = cast(int)(s.length);
154         for (int i = startIndex; i < length; i++) {
155             switch (s[i]) {
156                 case ' ':
157                 case '\t':
158                 case '\n':
159                 case '\u000B':
160                 case '\f':
161                 case '\r':
162                     break;
163                 default:
164                     return i;
165             }
166         }
167         return -1;
168     }
169 }