Commit | Line | Data |
---|---|---|
63ff1ecb BA |
1 | import sys, os, re, logging |
2 | ||
3 | # Languages mapping as used by markdown/pandoc | |
4 | shortname2language = dict( | |
5 | c='C', | |
6 | cpp='Cpp', | |
7 | f='Fortran', | |
8 | html='HTML', | |
9 | js='JavaScript', | |
10 | r='R', | |
11 | rb='Ruby', | |
12 | pl='Perl', | |
13 | py='Python', | |
14 | sh='Bash', | |
15 | tex='Tex', | |
16 | ) | |
17 | ||
18 | def read(text, argv=sys.argv[2:]): | |
19 | lines = text.splitlines() | |
20 | # First read all include statements | |
21 | for i in range(len(lines)): | |
22 | if lines[i].startswith('#include "'): | |
23 | filename = lines[i].split('"')[1] | |
24 | with open(filename, 'r') as f: | |
25 | include_text = f.read() | |
26 | lines[i] = include_text | |
27 | text = '\n'.join(lines) | |
28 | logging.info('******* text after include:\n{}'.format(text)) | |
29 | ||
30 | # Run Mako | |
31 | mako_kwargs = {} | |
32 | for arg in argv: | |
33 | key, value = arg.split('=') | |
34 | mako_kwargs[key] = value | |
35 | ||
36 | try: | |
37 | import mako | |
38 | has_mako = True | |
39 | except ImportError: | |
40 | print('Cannot import mako - mako is not run') | |
41 | has_mako = False | |
42 | ||
43 | if has_mako: | |
44 | from mako.template import Template | |
45 | from mako.lookup import TemplateLookup | |
46 | lookup = TemplateLookup(directories=[os.curdir]) | |
47 | text = text.encode('utf-8') | |
48 | temp = Template(text=text, lookup=lookup, strict_undefined=True) | |
49 | logging.info('******* mako_kwargs: {}'.format(str(mako_kwargs))) | |
50 | text = temp.render(**mako_kwargs) | |
51 | ||
52 | logging.info('******* text after mako:\n{}'.format(text)) | |
53 | ||
54 | # Parse the cells | |
55 | lines = text.splitlines() | |
56 | cells = [] | |
57 | inside = None # indicates which type of cell we are inside | |
58 | fullname = None # full language name in code cells | |
59 | for line in lines: | |
60 | if line.startswith('-----'): | |
61 | # New cell, what type? | |
62 | m = re.search(r'-----([a-z0-9-]+)?', line) | |
63 | if m: | |
64 | shortname = m.group(1) | |
65 | if shortname: | |
66 | # Check if code is to be typeset as static | |
67 | # Markdown code (e.g., shortname=py-t) | |
68 | logging.info('******* found shortname {}' | |
69 | .format(shortname)) | |
70 | astext = shortname[-2:] == '-t' | |
71 | logging.info('******* cell: astext={} shortname={}' | |
72 | .format(astext, shortname)) | |
73 | if astext: | |
74 | # Markdown | |
75 | shortname = shortname[:-2] | |
76 | inside = 'markdown' | |
77 | cells.append(['markdown', 'code', ['\n']]) | |
78 | cells[-1][2].append('```%s\n' % fullname) | |
79 | else: | |
80 | # Code cell | |
81 | if shortname in shortname2language: | |
82 | fullname = shortname2language[shortname] | |
83 | inside = 'codecell' | |
84 | cells.append(['codecell', fullname, []]) | |
85 | else: | |
86 | logging.info('******* cell: markdown') | |
87 | # Markdown cell | |
88 | inside = 'markdown' | |
89 | cells.append(['markdown', 'text', ['\n']]) | |
90 | else: | |
91 | raise SyntaxError('Wrong syntax of cell delimiter:\n{}' | |
92 | .format(repr(line))) | |
93 | else: | |
94 | # Ordinary line in a cell | |
95 | if inside in ('markdown', 'codecell'): | |
96 | cells[-1][2].append(line) | |
97 | else: | |
98 | raise SyntaxError('line\n {}\nhas no beginning cell delimiter' | |
99 | .format(line)) | |
100 | # Merge the lines in each cell to a string | |
101 | for i in range(len(cells)): | |
102 | if cells[i][0] == 'markdown' and cells[i][1] == 'code': | |
103 | # Add an ending ``` of code | |
104 | cells[i][2].append('```\n') | |
105 | cells[i][2] = '\n'.join(cells[i][2]) | |
106 | # TODO: optional logging | |
107 | import pprint | |
108 | logging.info('******* cell data structure:\b%s' % pprint.pformat(cells)) | |
109 | return cells | |
110 | ||
111 | def write(cells): | |
112 | """Turn cells list into valid IPython notebook code.""" | |
113 | # Use Jupyter nbformat functionality for writing the notebook | |
114 | ||
115 | from nbformat.v4 import ( | |
116 | new_code_cell, new_markdown_cell, new_notebook, writes) | |
117 | nb_cells = [] | |
118 | ||
119 | for cell_tp, language, block in cells: | |
120 | if cell_tp == 'markdown': | |
121 | nb_cells.append( | |
122 | new_markdown_cell(source=block)) | |
123 | elif cell_tp == 'codecell': | |
124 | nb_cells.append(new_code_cell(source=block)) | |
125 | ||
126 | nb = new_notebook(cells=nb_cells) | |
127 | filestr = writes(nb) | |
128 | return filestr | |
129 | ||
130 | def driver(): | |
131 | """Compile a document and its variables.""" | |
132 | try: | |
133 | filename = sys.argv[1] | |
134 | with open(filename, 'r') as f: | |
135 | text = f.read() | |
136 | except (IndexError, IOError) as e: | |
137 | print('Usage: %s filename' % (sys.argv[0])) | |
138 | print(e) | |
139 | sys.exit(1) | |
140 | cells = read(text, argv=sys.argv[2:]) | |
141 | filestr = write(cells) | |
142 | # Assuming file extension .gj (generate Jupyter); TODO: less strict | |
143 | filename = filename[:-3] + '.ipynb' | |
144 | with open(filename, 'w') as f: | |
145 | f.write(filestr) | |
146 | ||
147 | if __name__ == '__main__': | |
148 | logfile = 'tmp.log' | |
149 | if os.path.isfile: | |
150 | os.remove(logfile) | |
151 | logging.basicConfig(format='%(message)s', level=logging.DEBUG, | |
152 | filename=logfile) | |
153 | driver() |