Coverage for C:\leo.repo\leo-editor\leo\plugins\importers\lua.py : 18%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1#@+leo-ver=5-thin
2#@+node:ekr.20170530024520.2: * @file ../plugins/importers/lua.py
3"""
4The @auto importer for the lua language.
6Created 2017/05/30 by the `importer;;` abbreviation.
7"""
8import re
9from typing import Any, Dict, List
10from leo.core import leoGlobals as g
11from leo.plugins.importers import linescanner
12Importer = linescanner.Importer
13Target = linescanner.Target
14delete_blank_lines = True
15#@+others
16#@+node:ekr.20170530024520.3: ** class Lua_Importer
17class Lua_Importer(Importer):
18 """The importer for the lua lanuage."""
20 def __init__(self, importCommands, **kwargs):
21 """Lua_Importer.__init__"""
22 super().__init__(
23 importCommands,
24 language='lua',
25 state_class=Lua_ScanState,
26 strict=False,
27 )
28 self.start_stack = []
29 # Contains entries for all constructs that end with 'end'.
31 # Define necessary overrides.
32 #@+others
33 #@+node:ekr.20170530024520.5: *3* lua_i.clean_headline
34 def clean_headline(self, s, p=None):
35 """Return a cleaned up headline s."""
36 s = s.strip()
37 for tag in ('local', 'function'):
38 if s.startswith(tag):
39 s = s[len(tag) :]
40 i = s.find('(')
41 if i > -1:
42 s = s[:i]
43 return s.strip()
44 #@+node:ekr.20170530085347.1: *3* lua_i.cut_stack
45 def cut_stack(self, new_state, stack):
46 """Cut back the stack until stack[-1] matches new_state."""
47 assert len(stack) > 1 # Fail on entry.
48 # function/end's are strictly nested, so this suffices.
49 stack.pop()
50 # Restore the guard entry if necessary.
51 if len(stack) == 1:
52 stack.append(stack[-1])
53 assert len(stack) > 1 # Fail on exit.
54 #@+node:ekr.20170530040554.1: *3* lua_i.ends_block
55 def ends_block(self, i, lines, new_state, prev_state, stack):
56 """True if line ends the block."""
57 # pylint: disable=arguments-differ
58 if prev_state.context:
59 return False
60 line = lines[i]
61 # if line.strip().startswith('end'):
62 if g.match_word(line.strip(), 0, 'end'):
63 if self.start_stack:
64 top = self.start_stack.pop()
65 return top == 'function'
66 g.trace('unmatched "end" statement at line', i)
67 return False
68 #@+node:ekr.20170531052028.1: *3* lua_i.gen_lines
69 def gen_lines(self, lines, parent):
70 """
71 Non-recursively parse all lines of s into parent, creating descendant
72 nodes as needed.
73 """
74 tail_p = None
75 self.tail_lines = []
76 prev_state = self.state_class()
77 target = Target(parent, prev_state)
78 stack = [target, target]
79 self.vnode_info = {
80 # Keys are vnodes, values are inner dicts.
81 parent.v: {
82 'lines': [],
83 }
84 }
86 self.skip = 0
87 for i, line in enumerate(lines):
88 new_state = self.scan_line(line, prev_state)
89 top = stack[-1]
90 if self.skip > 0:
91 self.skip -= 1
92 elif line.isspace() and delete_blank_lines and not prev_state.context:
93 # Delete blank lines, but not inside strings and --[[ comments.
94 pass
95 elif self.is_ws_line(line):
96 if tail_p:
97 self.tail_lines.append(line)
98 else:
99 self.add_line(top.p, line)
100 elif self.starts_block(i, lines, new_state, prev_state):
101 tail_p = None
102 self.start_new_block(i, lines, new_state, prev_state, stack)
103 elif self.ends_block(i, lines, new_state, prev_state, stack):
104 tail_p = self.end_block(line, new_state, stack)
105 else:
106 if tail_p:
107 self.tail_lines.append(line)
108 else:
109 self.add_line(top.p, line)
110 prev_state = new_state
111 if self.tail_lines:
112 target = stack[-1]
113 self.extend_lines(target.p, self.tail_lines)
114 self.tail_lines = []
116 #@+node:ekr.20170530031729.1: *3* lua_i.get_new_dict
117 #@@nobeautify
119 def get_new_dict(self, context):
120 """The scan dict for the lua language."""
121 comment, block1, block2 = self.single_comment, self.block1, self.block2
122 assert comment
124 def add_key(d, pattern, data):
125 key = pattern[0]
126 aList = d.get(key,[])
127 aList.append(data)
128 d[key] = aList
130 d: Dict[str, List[Any]]
132 if context:
133 d = {
134 # key kind pattern ends?
135 '\\': [('len+1', '\\', None),],
136 '"': [('len', '"', context == '"'),],
137 "'": [('len', "'", context == "'"),],
138 }
139 # End Lua long brackets.
140 for i in range(10):
141 open_pattern = '--[%s[' % ('='*i)
142 # Both --]] and ]]-- end the long bracket.
143 pattern = ']%s]--' % ('='*i)
144 add_key(d, pattern, ('len', pattern, context==open_pattern))
145 pattern = '--]%s]' % ('='*i)
146 add_key(d, pattern, ('len', pattern, context==open_pattern))
147 if block1 and block2:
148 add_key(d, block2, ('len', block2, True))
149 else:
150 # Not in any context.
151 d = {
152 # key kind pattern new-ctx deltas
153 '\\':[('len+1', '\\', context, None),],
154 '"': [('len', '"', '"', None),],
155 "'": [('len', "'", "'", None),],
156 '{': [('len', '{', context, (1,0,0)),],
157 '}': [('len', '}', context, (-1,0,0)),],
158 '(': [('len', '(', context, (0,1,0)),],
159 ')': [('len', ')', context, (0,-1,0)),],
160 '[': [('len', '[', context, (0,0,1)),],
161 ']': [('len', ']', context, (0,0,-1)),],
162 }
163 # Start Lua long brackets.
164 for i in range(10):
165 pattern = '--[%s[' % ('='*i)
166 add_key(d, pattern, ('len', pattern, pattern, None))
167 if comment:
168 add_key(d, comment, ('all', comment, '', None))
169 if block1 and block2:
170 add_key(d, block1, ('len', block1, block1, None))
171 return d
172 #@+node:ekr.20170531052302.1: *3* lua_i.start_new_block
173 def start_new_block(self, i, lines, new_state, prev_state, stack):
174 """Create a child node and update the stack."""
175 if hasattr(new_state, 'in_context'):
176 assert not new_state.in_context(), ('start_new_block', new_state)
177 line = lines[i]
178 target = stack[-1]
179 # Insert the reference in *this* node.
180 h = self.gen_ref(line, target.p, target)
181 # Create a new child and associated target.
182 child = self.create_child_node(target.p, line, h)
183 if self.tail_lines:
184 self.prepend_lines(child, self.tail_lines)
185 self.tail_lines = []
186 stack.append(Target(child, new_state))
187 #@+node:ekr.20170530035601.1: *3* lua_i.starts_block
188 # Buggy: this could appear in a string or comment.
189 # The function must be an "outer" function, without indentation.
190 function_pattern = re.compile(r'^(local\s+)?function')
191 function_pattern2 = re.compile(r'(local\s+)?function')
193 def starts_block(self, i, lines, new_state, prev_state):
194 """True if the new state starts a block."""
196 def end(line):
197 # Buggy: 'end' could appear in a string or comment.
198 # However, this code is much better than before.
199 i = line.find('end')
200 return i if i > -1 and g.match_word(line, i, 'end') else -1
202 if prev_state.context:
203 return False
204 line = lines[i]
205 m = self.function_pattern.match(line)
206 if m and end(line) < m.start():
207 self.start_stack.append('function')
208 return True
209 # Don't create separate nodes for assigned functions,
210 # but *do* push 'function2' on the start_stack for the later 'end' statement.
211 m = self.function_pattern2.search(line)
212 if m and end(line) < m.start():
213 self.start_stack.append('function2')
214 return False
215 # Not a function. Handle constructs ending with 'end'.
216 line = line.strip()
217 if end(line) == -1:
218 for z in ('do', 'for', 'if', 'while',):
219 if g.match_word(line, 0, z):
220 self.start_stack.append(z)
221 break
222 return False
223 #@-others
224#@+node:ekr.20170530024520.7: ** class Lua_ScanState
225class Lua_ScanState:
226 """A class representing the state of the lua line-oriented scan."""
228 def __init__(self, d=None):
229 if d:
230 prev = d.get('prev')
231 self.context = prev.context
232 else:
233 self.context = ''
235 def __repr__(self):
236 return "Lua_ScanState context: %r " % (self.context)
237 __str__ = __repr__
239 #@+others
240 #@+node:ekr.20170530024520.8: *3* lua_state.level
241 def level(self):
242 """Lua_ScanState.level."""
243 return 0
244 # Never used.
245 #@+node:ekr.20170530024520.9: *3* lua_state.update
246 def update(self, data):
247 """
248 Lua_ScanState.update
250 Update the state using the 6-tuple returned by v2_scan_line.
251 Return i = data[1]
252 """
253 context, i, delta_c, delta_p, delta_s, bs_nl = data
254 # All ScanState classes must have a context ivar.
255 self.context = context
256 return i
257 #@-others
259#@-others
260importer_dict = {
261 'func': Lua_Importer.do_import(),
262 'extensions': ['.lua',],
263}
264#@@language python
265#@@tabwidth -4
268#@-leo