Hide keyboard shortcuts

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. 

5 

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.""" 

19 

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'. 

30 

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 } 

85 

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 = [] 

115 

116 #@+node:ekr.20170530031729.1: *3* lua_i.get_new_dict 

117 #@@nobeautify 

118 

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 

123 

124 def add_key(d, pattern, data): 

125 key = pattern[0] 

126 aList = d.get(key,[]) 

127 aList.append(data) 

128 d[key] = aList 

129 

130 d: Dict[str, List[Any]] 

131 

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') 

192 

193 def starts_block(self, i, lines, new_state, prev_state): 

194 """True if the new state starts a block.""" 

195 

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 

201 

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.""" 

227 

228 def __init__(self, d=None): 

229 if d: 

230 prev = d.get('prev') 

231 self.context = prev.context 

232 else: 

233 self.context = '' 

234 

235 def __repr__(self): 

236 return "Lua_ScanState context: %r " % (self.context) 

237 __str__ = __repr__ 

238 

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 

249 

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 

258 

259#@-others 

260importer_dict = { 

261 'func': Lua_Importer.do_import(), 

262 'extensions': ['.lua',], 

263} 

264#@@language python 

265#@@tabwidth -4 

266 

267 

268#@-leo