<<
path:
root/public/track.git/html/src/parse.jai
blob: 0c3ab5ea6a137980eb907a61be66f14a0d6f05b1
[raw]
[clear marker]
2COMMENT_PREFIXES :: "(///?|/\\*{1,9}|#{1,9}|\\-\\-)";
3KEYWORDS :: "(TODO|NOTE|IDEA|CONTINUE)";
4AUTHOR :: "([@\\-_\\s\\d\\w]+)";
5PRIORITY :: "([-+]?\\d+)";
7RE_PATTERN :: #run -> string {
8 return tprint("%1\\s*%2:?(\\(%3[\\s]*,?[\\s]*%4?\\))?:?",
17Token_Identifier :: enum {
26 identifier: Token_Identifier;
41init_pattern :: (mode: Re.ParseFlags = .FoldCase | .LikePerl) {
42 re = Re.compile(RE_PATTERN, mode);
45matches_process :: (source: string, matches: [..]Match) -> [..]Token {
47 text_parse :: () -> string #expand {
48 terminator := terminator_get(`it.groups[0]);
49 text := consume_till(`source, terminator, `it.position + `it.length);
53 parse_int_try :: (s: *string) -> int {
54 v, success := parse_int(s);
55 if !success return 690000;
60 /** This is inefficient, but since the ordering from [..]Match
61 isn't always chronological, it would lead to a shitty program behavior.
63 Plus I don't expect that people have amalgamations in their projects.
65 lines_count :: (s: string, pos_till: int) -> int {
66 assert(pos_till < s.count-1, "Ooohhps");
70 if s[i] == "\n" then lines += 1;
76 count_non_empty_elements :: inline (groups: []string) -> count: int {
88 capture_count := count_non_empty_elements(it.groups);
89 if it.match.count == 0 then continue;
96 token.identifier = identifier_determine(it.groups[1]);
98 token.line = lines_count(source, it.position);
99 array_add(*tokens, token);
103 text := text_parse();
105 token.identifier = identifier_determine(it.groups[1]);
106 token.author = trim(it.groups[3]);
107 token.comment = text;
108 token.line = lines_count(source, it.position);
109 array_add(*tokens, token);
111 // With author and priority
113 text := text_parse();
115 token.identifier = identifier_determine(it.groups[1]);
116 token.priority = parse_int_try(*trim(it.groups[4]));
117 token.author = trim(it.groups[3]);
118 token.comment = text;
119 token.line = lines_count(source, it.position);
120 array_add(*tokens, token);
127matches_collect :: (source: string) -> (success: bool, matches: [..]Match) {
129 has_match := search_for(source, *matches);
130 return has_match, matches;
140is_at_end :: (n: int) -> bool #expand {
141 return n > `source.count-1;
144skip_space_return_index :: inline (s: string, start_from: int) -> int {
145 for i: start_from..s.count-1 {
155consume_till :: (source: string, terminator: string, pos: int) -> string {
158 init_string_builder(*buf);
160 // TODO(adam, 3): Quite a bad name, so the intent isn't really clear!
161 no_multiline_comment: bool = !(terminator == "*/");
167 no_multiline_comment,
171 s := builder_to_string(*buf);
175consume_backend :: inline (
176 buf: *String_Builder,
179 no_multiline_comment: bool,
182 /** Don't append tabs or whitespace, if it's an empty area like this:
184 # TODO: abc fooo baaar
187 more_than_one_space := false;
188 skip_next_iteration := false;
190 for i: pos..source.count-1 {
191 if skip_next_iteration {
192 skip_next_iteration = false;
199 case 0x0B; #through; // vertical tab
201 if !more_than_one_space {
202 more_than_one_space = true;
203 append(buf, source[i]);
206 if no_multiline_comment ^ is_terminator(source, terminator, i+1) {
209 if is_next_character(source, "\n", i+1) && no_multiline_comment {
213 if is_next_character(source, "/", i+1) {
217 more_than_one_space = false;
220 cond &= source[i] == terminator[0];
222 if terminator.count == 2 {
223 cond &= is_next_character(source, terminator[1], i+1);
226 skip_next_iteration = true;
230 if cond then continue;
231 append(buf, source[i]);
236is_terminator :: (source: string, terminator: string, idx: int) -> bool {
237 offset := skip_space_return_index(source, idx);
240 cond &= !is_at_end(offset) && source[offset] == terminator[0];
242 if terminator.count == 2 {
243 cond &= !is_at_end(offset + 1) && source[offset + 1] == terminator[1];
249is_next_character :: inline (source: string, terminator: u8, idx: int) -> bool {
250 offset := skip_space_return_index(source, idx);
251 return !is_at_end(offset) && source[offset] == terminator;
254terminator_get :: (starter: string) -> string {
255 if starts_with(starter, "/*") { // TS "fix" *//*
261identifier_determine :: (id: string) -> Token_Identifier {
262 if to_lower_copy(id) == {
263 case "todo"; return .TODO;
264 case "note"; return .NOTE;
265 case "idea"; return .IDEA;
266 case "continue"; return .CONTINUE;
282 if subject.count <= 0 break;
284 diff := s.count - subject.count;
286 matched, captures := Re.match(subject, re);
288 has_match |= matched;
292 offset := c.data - subject.data;
295 if offset < 0 || offset >= subject.count break;
297 advance := offset + count;
298 subject.data += advance;
299 subject.count -= advance;
303 match.position = offset + diff;
304 match.length = c.count;
306 array_ordered_remove_by_index(*captures, 0);
308 match.groups = NewArray(captures.count, string);
309 for captures match.groups[it_index] = it;
311 array_add(matches, match);