1 module tcenal.parser_combinator.report_parsing;
2 
3 import compile_time_unittest : enableCompileTimeUnittest;
4 import tcenal.parser_combinator.memo : Memo, MemoEntry;
5 
6 import std.stdio : writeln;
7 import std.algorithm : sort, splitter, until;
8 import std.typecons : Tuple;
9 import std.range : repeat, retro;
10 import std.conv : to;
11 import std.array : array;
12 
13 mixin enableCompileTimeUnittest;
14 
15 
16 void reportParsing(string input, Memo memo)
17 {
18     size_t[] positions = memo.keys;
19     positions.sort();
20 
21     foreach (position; positions)
22     {
23         with (calcLineLineNumColumnNum(input, position))
24         {
25             writeln('='.repeat(32));
26             writeln(lineNum, ": ", line);
27             writeln(' '.repeat(lineNum.to!string().length + 2 + columnNum), "^");
28         }
29 
30         string[] ruleNames = memo[position].keys;
31         ruleNames.sort();
32 
33         foreach (ruleName; ruleNames)
34         {
35             MemoEntry memoEntry = memo[position][ruleName];
36 
37             if (memoEntry.parsingResult.success)
38             {
39                 writeln(memoEntry.parsingResult.node.toAsciiTree());
40             }
41             else
42             {
43                 writeln("+-", ruleName.retro().until('.').array().retro(), ": fail");
44             }
45         }
46     }
47 }
48 
49 
50 Tuple!(string, "line", size_t, "lineNum", size_t, "columnNum") calcLineLineNumColumnNum(string input, size_t position)
51 in
52 {
53     assert (position <= input.length);
54 }
55 body
56 {
57     size_t lineNum = 1;
58     size_t totalLength;
59 
60     foreach (line; input.splitter('\n'))
61     {
62         if (position < totalLength + line.length + 1)
63         {
64             return typeof(return)(line, lineNum, position - totalLength);
65         }
66 
67         totalLength += line.length + 1;
68         ++lineNum;
69     }
70 
71     return typeof(return)();
72 }
73 unittest
74 {
75     with (calcLineLineNumColumnNum("foo\nbar\nfoobar", 1))
76     {
77         assert (line == "foo");
78         assert (lineNum == 1);
79         assert (columnNum == 1);
80     }
81 
82     with (calcLineLineNumColumnNum("foo\nbar\nfoobar", 3))
83     {
84         assert (line == "foo");
85         assert (lineNum == 1);
86         assert (columnNum == 3);
87     }
88 
89     with (calcLineLineNumColumnNum("foo\nbar\nfoobar", 5))
90     {
91         assert (line == "bar");
92         assert (lineNum == 2);
93         assert (columnNum == 1);
94     }
95 }