Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/pygments/lexers/tnt.py: 34%

206 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2023-07-01 06:54 +0000

1""" 

2 pygments.lexers.tnt 

3 ~~~~~~~~~~~~~~~~~~~ 

4 

5 Lexer for Typographic Number Theory. 

6 

7 :copyright: Copyright 2006-2023 by the Pygments team, see AUTHORS. 

8 :license: BSD, see LICENSE for details. 

9""" 

10 

11import re 

12 

13from pygments.lexer import Lexer 

14from pygments.token import Text, Comment, Operator, Keyword, Name, Number, \ 

15 Punctuation, Error 

16 

17__all__ = ['TNTLexer'] 

18 

19 

20class TNTLexer(Lexer): 

21 """ 

22 Lexer for Typographic Number Theory, as described in the book 

23 Gödel, Escher, Bach, by Douglas R. Hofstadter 

24 

25 .. versionadded:: 2.7 

26 """ 

27 

28 name = 'Typographic Number Theory' 

29 url = 'https://github.com/Kenny2github/language-tnt' 

30 aliases = ['tnt'] 

31 filenames = ['*.tnt'] 

32 

33 cur = [] 

34 

35 LOGIC = set('⊃→]&∧^|∨Vv') 

36 OPERATORS = set('+.⋅*') 

37 VARIABLES = set('abcde') 

38 PRIMES = set("'′") 

39 NEGATORS = set('~!') 

40 QUANTIFIERS = set('AE∀∃') 

41 NUMBERS = set('0123456789') 

42 WHITESPACE = set('\t \v\n') 

43 

44 RULES = re.compile('''(?xi) 

45 joining | separation | double-tilde | fantasy\\ rule 

46 | carry[- ]over(?:\\ of)?(?:\\ line)?\\ ([0-9]+) | detachment 

47 | contrapositive | De\\ Morgan | switcheroo 

48 | specification | generalization | interchange 

49 | existence | symmetry | transitivity 

50 | add\\ S | drop\\ S | induction 

51 | axiom\\ ([1-5]) | premise | push | pop 

52 ''') 

53 LINENOS = re.compile(r'(?:[0-9]+)(?:(?:, ?|,? and )(?:[0-9]+))*') 

54 COMMENT = re.compile(r'\[[^\n\]]+\]') 

55 

56 def __init__(self, *args, **kwargs): 

57 Lexer.__init__(self, *args, **kwargs) 

58 self.cur = [] 

59 

60 def whitespace(self, start, text, required=False): 

61 """Tokenize whitespace.""" 

62 end = start 

63 try: 

64 while text[end] in self.WHITESPACE: 

65 end += 1 

66 except IndexError: 

67 end = len(text) 

68 if required and end == start: 

69 raise AssertionError 

70 if end != start: 

71 self.cur.append((start, Text, text[start:end])) 

72 return end 

73 

74 def variable(self, start, text): 

75 """Tokenize a variable.""" 

76 if text[start] not in self.VARIABLES: 

77 raise AssertionError 

78 end = start+1 

79 while text[end] in self.PRIMES: 

80 end += 1 

81 self.cur.append((start, Name.Variable, text[start:end])) 

82 return end 

83 

84 def term(self, start, text): 

85 """Tokenize a term.""" 

86 if text[start] == 'S': # S...S(...) or S...0 

87 end = start+1 

88 while text[end] == 'S': 

89 end += 1 

90 self.cur.append((start, Number.Integer, text[start:end])) 

91 return self.term(end, text) 

92 if text[start] == '0': # the singleton 0 

93 self.cur.append((start, Number.Integer, text[start])) 

94 return start+1 

95 if text[start] in self.VARIABLES: # a''... 

96 return self.variable(start, text) 

97 if text[start] == '(': # (...+...) 

98 self.cur.append((start, Punctuation, text[start])) 

99 start = self.term(start+1, text) 

100 if text[start] not in self.OPERATORS: 

101 raise AssertionError 

102 self.cur.append((start, Operator, text[start])) 

103 start = self.term(start+1, text) 

104 if text[start] != ')': 

105 raise AssertionError 

106 self.cur.append((start, Punctuation, text[start])) 

107 return start+1 

108 raise AssertionError # no matches 

109 

110 def formula(self, start, text): 

111 """Tokenize a formula.""" 

112 if text[start] in self.NEGATORS: # ~<...> 

113 end = start+1 

114 while text[end] in self.NEGATORS: 

115 end += 1 

116 self.cur.append((start, Operator, text[start:end])) 

117 return self.formula(end, text) 

118 if text[start] in self.QUANTIFIERS: # Aa:<...> 

119 self.cur.append((start, Keyword.Declaration, text[start])) 

120 start = self.variable(start+1, text) 

121 if text[start] != ':': 

122 raise AssertionError 

123 self.cur.append((start, Punctuation, text[start])) 

124 return self.formula(start+1, text) 

125 if text[start] == '<': # <...&...> 

126 self.cur.append((start, Punctuation, text[start])) 

127 start = self.formula(start+1, text) 

128 if text[start] not in self.LOGIC: 

129 raise AssertionError 

130 self.cur.append((start, Operator, text[start])) 

131 start = self.formula(start+1, text) 

132 if text[start] != '>': 

133 raise AssertionError 

134 self.cur.append((start, Punctuation, text[start])) 

135 return start+1 

136 # ...=... 

137 start = self.term(start, text) 

138 if text[start] != '=': 

139 raise AssertionError 

140 self.cur.append((start, Operator, text[start])) 

141 start = self.term(start+1, text) 

142 return start 

143 

144 def rule(self, start, text): 

145 """Tokenize a rule.""" 

146 match = self.RULES.match(text, start) 

147 if match is None: 

148 raise AssertionError 

149 groups = sorted(match.regs[1:]) # exclude whole match 

150 for group in groups: 

151 if group[0] >= 0: # this group matched 

152 self.cur.append((start, Keyword, text[start:group[0]])) 

153 self.cur.append((group[0], Number.Integer, 

154 text[group[0]:group[1]])) 

155 if group[1] != match.end(): 

156 self.cur.append((group[1], Keyword, 

157 text[group[1]:match.end()])) 

158 break 

159 else: 

160 self.cur.append((start, Keyword, text[start:match.end()])) 

161 return match.end() 

162 

163 def lineno(self, start, text): 

164 """Tokenize a line referral.""" 

165 end = start 

166 while text[end] not in self.NUMBERS: 

167 end += 1 

168 self.cur.append((start, Punctuation, text[start])) 

169 self.cur.append((start+1, Text, text[start+1:end])) 

170 start = end 

171 match = self.LINENOS.match(text, start) 

172 if match is None: 

173 raise AssertionError 

174 if text[match.end()] != ')': 

175 raise AssertionError 

176 self.cur.append((match.start(), Number.Integer, match.group(0))) 

177 self.cur.append((match.end(), Punctuation, text[match.end()])) 

178 return match.end() + 1 

179 

180 def error_till_line_end(self, start, text): 

181 """Mark everything from ``start`` to the end of the line as Error.""" 

182 end = start 

183 try: 

184 while text[end] != '\n': # there's whitespace in rules 

185 end += 1 

186 except IndexError: 

187 end = len(text) 

188 if end != start: 

189 self.cur.append((start, Error, text[start:end])) 

190 end = self.whitespace(end, text) 

191 return end 

192 

193 def get_tokens_unprocessed(self, text): 

194 """Returns a list of TNT tokens.""" 

195 self.cur = [] 

196 start = end = self.whitespace(0, text) 

197 while start <= end < len(text): 

198 try: 

199 # try line number 

200 while text[end] in self.NUMBERS: 

201 end += 1 

202 if end != start: # actual number present 

203 self.cur.append((start, Number.Integer, text[start:end])) 

204 # whitespace is required after a line number 

205 orig = len(self.cur) 

206 try: 

207 start = end = self.whitespace(end, text, True) 

208 except AssertionError: 

209 del self.cur[orig:] 

210 start = end = self.error_till_line_end(end, text) 

211 continue 

212 # at this point it could be a comment 

213 match = self.COMMENT.match(text, start) 

214 if match is not None: 

215 self.cur.append((start, Comment, text[start:match.end()])) 

216 start = end = match.end() 

217 # anything after the closing bracket is invalid 

218 start = end = self.error_till_line_end(start, text) 

219 # do not attempt to process the rest 

220 continue 

221 del match 

222 if text[start] in '[]': # fantasy push or pop 

223 self.cur.append((start, Keyword, text[start])) 

224 start += 1 

225 end += 1 

226 else: 

227 # one formula, possibly containing subformulae 

228 orig = len(self.cur) 

229 try: 

230 start = end = self.formula(start, text) 

231 except (AssertionError, RecursionError): # not well-formed 

232 del self.cur[orig:] 

233 while text[end] not in self.WHITESPACE: 

234 end += 1 

235 self.cur.append((start, Error, text[start:end])) 

236 start = end 

237 # skip whitespace after formula 

238 orig = len(self.cur) 

239 try: 

240 start = end = self.whitespace(end, text, True) 

241 except AssertionError: 

242 del self.cur[orig:] 

243 start = end = self.error_till_line_end(start, text) 

244 continue 

245 # rule proving this formula a theorem 

246 orig = len(self.cur) 

247 try: 

248 start = end = self.rule(start, text) 

249 except AssertionError: 

250 del self.cur[orig:] 

251 start = end = self.error_till_line_end(start, text) 

252 continue 

253 # skip whitespace after rule 

254 start = end = self.whitespace(end, text) 

255 # line marker 

256 if text[start] == '(': 

257 orig = len(self.cur) 

258 try: 

259 start = end = self.lineno(start, text) 

260 except AssertionError: 

261 del self.cur[orig:] 

262 start = end = self.error_till_line_end(start, text) 

263 continue 

264 start = end = self.whitespace(start, text) 

265 except IndexError: 

266 try: 

267 del self.cur[orig:] 

268 except NameError: 

269 pass # if orig was never defined, fine 

270 self.error_till_line_end(start, text) 

271 return self.cur