-
Notifications
You must be signed in to change notification settings - Fork 34
/
alectryon_custom_driver.py
executable file
·97 lines (75 loc) · 3.35 KB
/
alectryon_custom_driver.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
#!/usr/bin/env python3
"""
This is an example of a custom driver: it exposes the same interface as
Alectryon's usual CLI, but it sets the internal parameter pp_margin of SerAPI
to a different value, and it sets up all sorts of custom highlighting.
See ``custom_highlighting.v`` for examples of what it can do. The following
command is just a sanity test::
$ python alectryon_custom_driver.py --version | grep -o Alectryon > alectryon_custom_driver.out
# Custom driver; produces ‘alectryon_custom_driver.out’
"""
import sys
from os.path import join, dirname
## Set up the Python path (not needed if installing from Pip)
root = join(dirname(__file__), "..")
sys.path.insert(0, root)
## Tweak a setting that is not exposed on the command line.
from alectryon.serapi import SerAPI
SerAPI.DEFAULT_PP_ARGS['pp_margin'] = 55
## Teach the Coq lexer to highlight embedded C code
# Create a lexer that highlights C code within ``C:{{ … }}`` blocks, Coq
# comments as Markdown, and a custom mini-language inside ``calc:()`` blocks
# using a custom lexer.
# Step 1: imports
import re
from pygments import token
from pygments.lexer import RegexLexer, bygroups, using, regex_opt
from pygments.lexers.c_cpp import CLexer
from pygments.lexers.markup import MarkdownLexer
# Step 2 (for Markdown in comments): make a custom lexer that changes ``Text``
# tokens into ``Comment``.
class MarkdownInComments(MarkdownLexer):
def get_tokens_unprocessed(self, text, **kwargs): # pylint: disable=arguments-differ
for idx, t, v in super().get_tokens_unprocessed(text, **kwargs):
t = token.Comment if t in (token.Text, token.Whitespace) else t
yield idx, t, v
# Step 3: Define a lexer that highlights Markdown, C, and a custom mini language
# within certain bounds and tags everything else as ``Other``.
class EmbeddedLexer(RegexLexer):
name = 'EmbeddedC'
aliases, filenames = [], []
flags = re.DOTALL
tokens = {
'root': [
('(C)(:[{][{])(.+?)([}][}])',
bygroups(token.Keyword, token.Operator, using(CLexer), token.Operator)),
('([(][*])(.+?)([*][)])', # nested comments left as an exercise
bygroups(token.Comment, using(MarkdownInComments), token.Comment)),
('(calc)(:[(])',
bygroups(token.Keyword, token.Operator), ('#push', 'minilang')),
('.', token.Other), # ← This indicates the "host" language
],
'minilang': [
("[(]", token.Operator, '#push'),
("[)]", token.Operator, '#pop'),
(regex_opt("add sub div mul".split()), token.Keyword),
("[0-9]+", token.Number),
("[a-zA-Z0-9_]+", token.Name.Variable),
(".", token.Text),
]
}
# Step 4: Embed all these domain-specific lexers into a host Coq lexer.
from pygments.lexer import DelegatingLexer
from alectryon.pygments_lexer import CoqLexer
class CustomLexer(DelegatingLexer, CoqLexer):
name = 'Coq'
def __init__(self, **options):
super().__init__(CoqLexer, EmbeddedLexer, **options)
# Step 5: Replace Alectryon's usual Coq lexer with our custom one. Note how the
# replacement inherits from CoqLexer to not confuse Alectryon.
import alectryon.pygments_lexer
alectryon.pygments_lexer.CoqLexer = CustomLexer
## Run Alectryon's usual CLI
from alectryon.cli import main
if __name__ == "__main__":
main()