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