markion.py (2770B) - raw
1 #!/usr/bin/env python3 2 # Markion 3 # Copyright (C) 2019-2020 Oscar Benedito <oscar@oscarbenedito.com> 4 # 5 # This program is free software: you can redistribute it and/or modify 6 # it under the terms of the GNU Affero General Public License as 7 # published by the Free Software Foundation, either version 3 of the 8 # License, or (at your option) any later version. 9 # 10 # This program is distributed in the hope that it will be useful, 11 # but WITHOUT ANY WARRANTY; without even the implied warranty of 12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 # GNU Affero General Public License for more details. 14 # 15 # You should have received a copy of the GNU Affero General Public License 16 # along with this program. If not, see <https://www.gnu.org/licenses/>. 17 import os, sys, re, argparse 18 __version__ = "1.0.0" 19 parser = argparse.ArgumentParser(prog='Markion', description='Markion is a simple scripts that retrieves tangled code from Markdown.') 20 parser.add_argument('file', metavar='file', type=str, nargs=1, help='Input file.') 21 parser.add_argument('-d', '--output-directory', dest='out_dir', type=str, default=os.getcwd(), help='Change the output directory.') 22 parser.add_argument('-D', '--auto-directory', dest='auto_dir', action='store_true', help='Auto detect output directory.') 23 parser.add_argument('-v', '--version', action='version', version='%(prog)s ' + __version__) 24 args = parser.parse_args() 25 with open(args.file[0], 'r') as f: 26 inp = f.read() 27 r_block = '```[\w\-.]*\s+block\s+([\w.-]+).*?\n(.*?)\n```\s*?\n' 28 r_file = '```[\w\-.]*\s+file\s+([\w.-]+).*?\n(.*?\n)```\s*?\n' 29 blocks = re.findall(r_block, inp, flags = re.DOTALL) 30 files = re.findall(r_file, inp, flags = re.DOTALL) 31 r_include = re.compile('([ \t]*)\[\[\s*include\s+([\w\-.]+)\s*\]\]', flags = re.DOTALL) 32 def resolve(content, blocks): 33 it = r_include.finditer(content) 34 for include in it: 35 block_name = include[2] 36 if blocks[block_name][0]: 37 raise Exception('Circular dependency in block ' + block_name) 38 blocks[block_name][0] = True 39 s = resolve(blocks[block_name][1], blocks) 40 blocks[block_name][0] = False 41 blocks[block_name][1] = s 42 s = include[1] + s.replace('\n', '\n' + include[1]) 43 content = r_include.sub(repr(s)[1:-1], content, count = 1) 44 return content 45 block_content = { b[0] : [False, b[1]] for b in blocks } 46 file_content = dict() 47 for f in files: 48 if f[0] not in file_content: 49 file_content[f[0]] = '' 50 file_content[f[0]] += resolve(f[1], block_content) 51 if args.auto_dir: 52 args.out_dir = os.path.dirname(args.file[0]) 53 if not os.path.exists(args.out_dir): 54 os.mkdirs(args.out_dir) 55 for fn, fc in file_content.items(): 56 with open(os.path.join(args.out_dir, fn), 'w') as f: 57 f.write(fc)