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.20140723122936.17926: * @file ../plugins/importers/c.py 

3"""The @auto importer for the C language and other related languages.""" 

4import re 

5from leo.core import leoGlobals as g 

6from leo.plugins.importers import linescanner 

7assert g 

8Importer = linescanner.Importer 

9Target = linescanner.Target 

10#@+others 

11#@+node:ekr.20140723122936.17928: ** class C_Importer 

12class C_Importer(Importer): 

13 

14 #@+others 

15 #@+node:ekr.20200819144754.1: *3* c_i.ctor 

16 def __init__(self, importCommands, **kwargs): 

17 """C_Importer.__init__""" 

18 # Init the base class. 

19 super().__init__( 

20 importCommands, 

21 language='c', 

22 state_class=C_ScanState, 

23 ) 

24 self.headline = None 

25 # Fix #545 by supporting @data c_import_typedefs. 

26 self.type_keywords = [ 

27 'auto', 'bool', 'char', 'const', 'double', 

28 'extern', 'float', 'int', 'register', 

29 'signed', 'short', 'static', 'typedef', 

30 'union', 'unsigned', 'void', 'volatile', 

31 ] 

32 aSet = set( 

33 self.type_keywords + 

34 (self.c.config.getData('c_import_typedefs') or []) 

35 ) 

36 self.c_type_names = '(%s)' % '|'.join(list(aSet)) 

37 self.c_types_pattern = re.compile(self.c_type_names) 

38 self.c_class_pattern = re.compile(r'\s*(%s\s*)*\s*class\s+(\w+)' % (self.c_type_names)) 

39 self.c_func_pattern = re.compile(r'\s*(%s\s*)+\s*([\w:]+)' % (self.c_type_names)) 

40 self.c_keywords = '(%s)' % '|'.join([ 

41 'break', 'case', 'continue', 'default', 'do', 'else', 'enum', 

42 'for', 'goto', 'if', 'return', 'sizeof', 'struct', 'switch', 'while', 

43 ]) 

44 self.c_keywords_pattern = re.compile(self.c_keywords) 

45 #@+node:ekr.20200819073508.1: *3* c_i.clean_headline 

46 def clean_headline(self, s, p=None): 

47 """ 

48 Adjust headline for templates. 

49 """ 

50 if not p: 

51 return s.strip() 

52 lines = self.get_lines(p) 

53 if s.startswith('template') and len(lines) > 1: 

54 line = lines[1] 

55 # Filter out all keywords and cruft. 

56 # This isn't perfect, but it's a good start. 

57 for z in self.type_keywords: 

58 line = re.sub(fr"\b{z}\b", '', line) 

59 for ch in '()[]{}=': 

60 line = line.replace(ch, '') 

61 return line.strip() 

62 return s.strip() 

63 #@+node:ekr.20161204173153.1: *3* c_i.match_name_patterns 

64 c_name_pattern = re.compile(r'\s*([\w:]+)') 

65 

66 def match_name_patterns(self, line): 

67 """Set self.headline if the line defines a typedef name.""" 

68 m = self.c_name_pattern.match(line) 

69 if m: 

70 word = m.group(1) 

71 if not self.c_types_pattern.match(word): 

72 self.headline = word 

73 #@+node:ekr.20161204165700.1: *3* c_i.match_start_patterns 

74 # Define patterns that can start a block 

75 c_extern_pattern = re.compile(r'\s*extern\s+(\"\w+\")') 

76 c_template_pattern = re.compile(r'\s*template\s*<(.*?)>\s*$') 

77 c_typedef_pattern = re.compile(r'\s*(\w+)\s*\*\s*$') 

78 

79 def match_start_patterns(self, line): 

80 """ 

81 True if line matches any block-starting pattern. 

82 If true, set self.headline. 

83 """ 

84 m = self.c_extern_pattern.match(line) 

85 if m: 

86 self.headline = line.strip() 

87 return True 

88 # #1626 

89 m = self.c_template_pattern.match(line) 

90 if m: 

91 self.headline = line.strip() 

92 return True 

93 m = self.c_class_pattern.match(line) 

94 if m: 

95 prefix = m.group(1).strip() if m.group(1) else '' 

96 self.headline = '%sclass %s' % (prefix, m.group(3)) 

97 self.headline = self.headline.strip() 

98 return True 

99 m = self.c_func_pattern.match(line) 

100 if m: 

101 if self.c_types_pattern.match(m.group(3)): 

102 return True 

103 prefix = m.group(1).strip() if m.group(1) else '' 

104 self.headline = '%s %s' % (prefix, m.group(3)) 

105 self.headline = self.headline.strip() 

106 return True 

107 m = self.c_typedef_pattern.match(line) 

108 if m: 

109 # Does not set self.headline. 

110 return True 

111 m = self.c_types_pattern.match(line) 

112 return bool(m) 

113 #@+node:ekr.20161204072326.1: *3* c_i.start_new_block 

114 def start_new_block(self, i, lines, new_state, prev_state, stack): 

115 """Create a child node and update the stack.""" 

116 line = lines[i] 

117 target = stack[-1] 

118 # Insert the reference in *this* node. 

119 h = self.gen_ref(line, target.p, target) 

120 # Create a new child and associated target. 

121 if self.headline: 

122 h = self.headline 

123 if new_state.level() > prev_state.level(): 

124 child = self.create_child_node(target.p, line, h) 

125 else: 

126 # We may not have seen the { yet, so adjust. 

127 # Without this, the new block becomes a child of the preceding. 

128 new_state = C_ScanState() 

129 new_state.curlies = prev_state.curlies + 1 

130 child = self.create_child_node(target.p, line, h) 

131 stack.append(Target(child, new_state)) 

132 # Add all additional lines of the signature. 

133 skip = self.skip # Don't change the ivar! 

134 while skip > 0: 

135 skip -= 1 

136 i += 1 

137 assert i < len(lines), (i, len(lines)) 

138 line = lines[i] 

139 if not self.headline: 

140 self.match_name_patterns(line) 

141 if self.headline: 

142 child.h = '%s %s' % (child.h.strip(), self.headline) 

143 self.add_line(child, lines[i]) 

144 #@+node:ekr.20161204155335.1: *3* c_i.starts_block 

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

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

147 self.headline = None 

148 line = lines[i] 

149 if prev_state.context: 

150 return False 

151 if self.c_keywords_pattern.match(line): 

152 return False 

153 if not self.match_start_patterns(line): 

154 return False 

155 # Must not be a complete statement. 

156 if line.find(';') > -1: 

157 return False 

158 # Scan ahead until an open { is seen. the skip count. 

159 self.skip = 0 

160 while self.skip < 10: 

161 if new_state.level() > prev_state.level(): 

162 return True 

163 self.skip += 1 

164 i += 1 

165 if i < len(lines): 

166 line = lines[i] 

167 prev_state = new_state 

168 new_state = self.scan_line(line, prev_state) 

169 else: 

170 break 

171 return False 

172 #@-others 

173#@+node:ekr.20161108223159.1: ** class C_ScanState 

174class C_ScanState: 

175 """A class representing the state of the C line-oriented scan.""" 

176 

177 def __init__(self, d=None): 

178 """C_ScanSate ctor""" 

179 if d: 

180 prev = d.get('prev') 

181 self.context = prev.context 

182 self.curlies = prev.curlies 

183 else: 

184 self.context = '' 

185 self.curlies = 0 

186 

187 def __repr__(self): 

188 """C_ScanState.__repr__""" 

189 return 'C_ScanState context: %r curlies: %s' % (self.context, self.curlies) 

190 

191 __str__ = __repr__ 

192 

193 #@+others 

194 #@+node:ekr.20161119115315.1: *3* c_state.level 

195 def level(self): 

196 """C_ScanState.level.""" 

197 return self.curlies 

198 #@+node:ekr.20161118051111.1: *3* c_state.update 

199 def update(self, data): 

200 """ 

201 Update the state using the 6-tuple returned by i.scan_line. 

202 Return i = data[1] 

203 """ 

204 context, i, delta_c, delta_p, delta_s, bs_nl = data 

205 # self.bs_nl = bs_nl 

206 self.context = context 

207 self.curlies += delta_c 

208 return i 

209 

210 #@-others 

211 

212#@-others 

213importer_dict = { 

214 'func': C_Importer.do_import(), 

215 'extensions': ['.c', '.cc', '.c++', '.cpp', '.cxx', '.h', '.h++',], 

216} 

217#@@language python 

218#@@tabwidth -4 

219#@-leo