1 | 1 | import re |
|
2 | 1 | from typing import Any, Dict |
|
3 | |||
4 | 1 | from antlr4.Token import CommonToken |
|
5 | 1 | from cleo.io.io import IO |
|
6 | |||
7 | 1 | from sdoc import sdoc2 |
|
8 | 1 | from sdoc.antlr.sdoc2Parser import sdoc2Parser |
|
9 | 1 | from sdoc.antlr.sdoc2ParserVisitor import sdoc2ParserVisitor |
|
10 | 1 | from sdoc.sdoc.SDocVisitor import SDocVisitor |
|
11 | 1 | from sdoc.sdoc2.Position import Position |
|
12 | |||
13 | |||
14 | 1 | View Code Duplication | class SDoc2Visitor(sdoc2ParserVisitor, SDocVisitor): |
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
15 | """ |
||
16 | Visitor for SDoc level 2. |
||
17 | """ |
||
18 | |||
19 | # ------------------------------------------------------------------------------------------------------------------ |
||
20 | 1 | def __init__(self, sdoc1_path: str, io: IO): |
|
21 | """ |
||
22 | Object constructor. |
||
23 | |||
24 | :param str sdoc1_path: The path to the original SDoc1 document. |
||
25 | :param cleo.styles.output_style.OutputStyle io: The IO object. |
||
26 | """ |
||
27 | 1 | SDocVisitor.__init__(self, io) |
|
28 | |||
29 | 1 | self._io: IO = io |
|
30 | """ |
||
31 | Styled output formatter. |
||
32 | """ |
||
33 | |||
34 | 1 | self._output: Any = None |
|
35 | """ |
||
36 | Object for streaming the generated output. This object MUST implement the write method. |
||
37 | """ |
||
38 | |||
39 | 1 | self._sdoc1_file_name: str = sdoc1_path |
|
40 | """ |
||
41 | The original file name at SDoc1 level. |
||
42 | """ |
||
43 | |||
44 | 1 | self._sdoc1_line: int = 0 |
|
45 | """ |
||
46 | The offset of for computing the current line at SDoc1 level. |
||
47 | """ |
||
48 | |||
49 | 1 | self._sdoc1_column: int = 0 |
|
50 | """ |
||
51 | The offset of for computing the current column at SDoc1 level. |
||
52 | """ |
||
53 | |||
54 | 1 | self._sdoc2_line: int = 0 |
|
55 | """ |
||
56 | The line position of the last position command. |
||
57 | """ |
||
58 | |||
59 | 1 | self._sdoc2_column: int = 0 |
|
60 | 1 | """ |
|
61 | The last column position of the last position command. |
||
62 | """ |
||
63 | |||
64 | # ------------------------------------------------------------------------------------------------------------------ |
||
65 | 1 | @staticmethod |
|
66 | 1 | def _get_options(ctx) -> Dict[str, str]: |
|
67 | """ |
||
68 | Returns the option of a command. |
||
69 | |||
70 | :param ParserRuleContext ctx: The parse tree with the options. |
||
71 | """ |
||
72 | 1 | options = {} |
|
73 | 1 | i = 0 |
|
74 | while True: |
||
75 | 1 | name_token = ctx.OPT_ARG_NAME(i) |
|
76 | 1 | value_token = ctx.OPT_ARG_VALUE(i) |
|
77 | 1 | if not name_token: |
|
78 | 1 | break |
|
79 | |||
80 | 1 | option_name = name_token.getText() |
|
81 | 1 | option_value = value_token.getText() |
|
82 | # Trim leading and trailing (double)quotes from string. (Is there a way to do this in ANTLR?) |
||
83 | 1 | if (option_value[0] == '"' and option_value[-1] == '"') or \ |
|
84 | (option_value[0] == "'" and option_value[-1] == "'"): |
||
85 | 1 | option_value = option_value[1:-1] |
|
86 | |||
87 | 1 | options[option_name] = option_value |
|
88 | 1 | i += 1 |
|
89 | |||
90 | 1 | return options |
|
91 | |||
92 | # ------------------------------------------------------------------------------------------------------------------ |
||
93 | 1 | def get_position(self, token: CommonToken) -> Position: |
|
94 | """ |
||
95 | Returns the position of the token in the original SDoc1 source file. |
||
96 | |||
97 | :param CommonToken token: |
||
98 | """ |
||
99 | 1 | line_number = token.line |
|
100 | 1 | column = token.column |
|
101 | |||
102 | 1 | if self._sdoc2_line == line_number: |
|
103 | 1 | column = self._sdoc1_column + (column - self._sdoc2_column) |
|
104 | |||
105 | 1 | line_number = self._sdoc1_line + (line_number - self._sdoc2_line) |
|
106 | |||
107 | 1 | return Position(self._sdoc1_file_name, line_number, column, -1, -1) |
|
108 | |||
109 | # ------------------------------------------------------------------------------------------------------------------ |
||
110 | 1 | def set_output(self, output: Any) -> None: |
|
111 | """ |
||
112 | Sets the object for streaming the generated output. |
||
113 | |||
114 | :param any output: This object MUST implement the write method. |
||
115 | """ |
||
116 | self._output = output |
||
117 | |||
118 | # ------------------------------------------------------------------------------------------------------------------ |
||
119 | 1 | def stream(self, snippet: str) -> None: |
|
120 | """ |
||
121 | Puts an output snippet on the output stream. |
||
122 | |||
123 | :param str snippet: The snippet to be appended to the output stream of this parser. |
||
124 | """ |
||
125 | if snippet is not None: |
||
126 | self._output.write(snippet) |
||
127 | |||
128 | # ------------------------------------------------------------------------------------------------------------------ |
||
129 | 1 | def visitCmd_begin(self, ctx: sdoc2Parser.Cmd_beginContext) -> None: |
|
130 | """ |
||
131 | Visit a parse tree produced by a begin command. |
||
132 | |||
133 | :param sdoc2Parser.Cmd_beginContext ctx: The parse tree. |
||
134 | """ |
||
135 | 1 | command = ctx.BLOCK_ARG_ARG().getText() |
|
136 | |||
137 | 1 | sdoc2.node_store.append_block_node(command, self._get_options(ctx), self.get_position(ctx.start)) |
|
138 | |||
139 | # ------------------------------------------------------------------------------------------------------------------ |
||
140 | 1 | def visitCmd_end(self, ctx: sdoc2Parser.Cmd_endContext) -> None: |
|
141 | """ |
||
142 | Visit a parse tree produced by an end command. |
||
143 | |||
144 | :param sdoc2Parser.Cmd_endContext ctx: The parse tree. |
||
145 | """ |
||
146 | 1 | command = ctx.BLOCK_ARG_ARG().getText().rstrip() |
|
147 | |||
148 | 1 | sdoc2.node_store.end_block_node(command) |
|
149 | |||
150 | # ------------------------------------------------------------------------------------------------------------------ |
||
151 | 1 | def visitCmd_position(self, ctx: sdoc2Parser.Cmd_positionContext) -> None: |
|
152 | """ |
||
153 | Visit a parse tree produced by a position command. |
||
154 | |||
155 | :param sdoc2Parser.Cmd_positionContext ctx: The parse tree. |
||
156 | """ |
||
157 | 1 | argument = ctx.INLINE_ARG_ARG() |
|
158 | 1 | parts = re.match(r'(.+):([0-9]+)\.([0-9]+)', str(argument)) |
|
159 | 1 | if not parts: |
|
160 | self._error('{0!s} is not a valid position'.format(argument)) |
||
161 | return |
||
162 | |||
163 | 1 | self._sdoc1_file_name = parts.group(1) |
|
164 | 1 | self._sdoc1_line = int(parts.group(2)) |
|
165 | 1 | self._sdoc1_column = int(parts.group(3)) |
|
166 | |||
167 | 1 | token = ctx.stop |
|
168 | 1 | self._sdoc2_line = token.line |
|
169 | 1 | self._sdoc2_column = token.column + len(token.text) |
|
170 | |||
171 | # ------------------------------------------------------------------------------------------------------------------ |
||
172 | 1 | def visitCmd_sdoc2(self, ctx: sdoc2Parser.Cmd_sdoc2Context) -> None: |
|
173 | """ |
||
174 | Visit a parse tree produced by a inline command. |
||
175 | |||
176 | :param sdoc2Parser.Cmd_sdoc2Context ctx: The parse tree. |
||
177 | """ |
||
178 | 1 | command = ctx.SDOC2_COMMAND().getText() |
|
179 | 1 | argument = ctx.INLINE_ARG_ARG() |
|
180 | |||
181 | 1 | sdoc2.node_store.append_inline_node(command[1:], |
|
182 | self._get_options(ctx), |
||
183 | argument.getText() if argument else '', |
||
184 | self.get_position(ctx.start)) |
||
185 | |||
186 | # ------------------------------------------------------------------------------------------------------------------ |
||
187 | 1 | def visitText(self, ctx: sdoc2Parser.TextContext) -> None: |
|
188 | """ |
||
189 | Visit a parse tree produced by TEXT. |
||
190 | |||
191 | :param sdoc2Parser.TextContext ctx: The parse tree. |
||
192 | """ |
||
193 | 1 | sdoc2.node_store.append_inline_node('TEXT', {}, ctx.TEXT().getText(), self.get_position(ctx.start)) |
|
194 | |||
195 | # ---------------------------------------------------------------------------------------------------------------------- |
||
196 |