#!/usr/bin/env python3 # Copyright (c) 2021 OK Ojisan(Takuya OKAHISA) # Released under the MIT license # http://opensource.org/licenses/mit-license.php # hpp version 1.2 import os import sys import re import html def hpp(stream, n = 0, flags = None, defs = None): cmdname = os.path.basename(sys.argv[0]) if n > 10: err = "{0}: 再帰の段数が 10 を超えました。".format(cmdname) print(err, file=sys.stderr) sys.exit(1) target = stream.name ls = os.linesep pairs = [x.split("=", 1) for x in sys.argv[1:]] if flags is None: flags = [x[0] for x in pairs if len(x) >= 1 and x[0] != ""] if defs is None: defs = [x for x in pairs if len(x) == 2 and x[0] != ""] do_print = True in_use = False esc = False stack = [] useindent = "" retval = "" macro = "" param = "" value = "" content = "" lineno = 0 for row in stream: lineno += 1 strip = row.strip() indent = re.match("^(\s*)", row).group(1) if m := re.match("^$", strip): if do_print: if m.group(1) == "|": indent = "" filename = m.group(2) text = "" with rdopen(filename) as f: text = hpp(f, n + 1, flags, defs) for line in text.splitlines(): output = indent + line + ls if in_use: value += line + ls elif n == 0: print(output, end="") else: retval += output elif m := re.match("^$", strip): stack.append(do_print) flag = m.group(1) do_print = do_print and any([f == flag for f in flags]) elif m := re.match("^$", strip): stack.append(do_print) flag = m.group(1) do_print = do_print and not any([f == flag for f in flags]) elif strip == "" or \ re.match("^$", strip): do_print = stack.pop() elif strip == "" or \ re.match("^$", strip): do_print = not do_print elif m := re.match("^$", strip): if do_print and in_use: err1 = "{0}: {1}:{2}行目: ".format(cmdname, target, lineno) err2 = "use または def の中で use は使えません。" print(err1, file=sys.stderr, end="") print(err2, file=sys.stderr) sys.exit(1) if do_print: useindent = "" if m.group(1) == "|" else indent param = "" value = "" source = m.group(2) content = get_content(n, source, flags, defs) in_use = True esc = False elif m := re.match("^$", strip): if do_print and param != "": value = trimls(value) content = content.replace(param, value) if do_print: param = m.group(1) value = "" in_use = True elif re.match("^$", strip): if do_print and param != "": value = trimls(value) content = content.replace(param, value) if do_print: if esc: content = html.escape(content) for line in content.splitlines(): output = useindent + line + ls if n == 0: print(output, end="") else: retval += output param = "" value = "" content = "" in_use = False esc = False elif m := re.match("^$", strip): if do_print: if m.group(1) == "|": indent = "" sep1 = m.group(2) sep2 = m.group(3) subs = m.group(4).split(sep2) source = subs.pop(0) text = get_content(n, source, flags, defs) text = subst(text, subs, sep1, defs) for line in text.splitlines(): output = indent + line + ls if in_use: value += line + ls elif n == 0: print(output, end="") else: retval += output elif m := re.match("^$", strip): if do_print and in_use: err1 = "{0}: {1}:{2}行目: ".format(cmdname, target, lineno) err2 = "use または def の中で def/use は使えません。" print(err1, file=sys.stderr, end="") print(err2, file=sys.stderr) sys.exit(1) if do_print: useindent = indent macro = m.group(1) param = "" value = "" source = m.group(2) content = get_content(n, source, flags, defs) in_use = True esc = False elif m := re.match("^$", strip): if do_print: name = m.group(1) sep1 = m.group(2) sep2 = m.group(3) subs = m.group(4).split(sep2) source = subs.pop(0) text = get_content(n, source, flags, defs) text = subst(text, subs, sep1, defs) defs.append([name, text]) flags.append(name) elif m := re.match("^$", strip): if do_print and in_use: err1 = "{0}: {1}:{2}行目: ".format(cmdname, target, lineno) err2 = "use または def の中で def は使えません。" print(err1, file=sys.stderr, end="") print(err2, file=sys.stderr) sys.exit(1) if do_print: useindent = indent macro = m.group(1) param = "" value = "" content = "" in_use = True esc = False elif re.match("^$", strip): if do_print and in_use: value = trimls(value) if param != "": content = content.replace(param, value) if content != "": value = content if esc: value = html.escape(value) defs.append([macro, value]) flags.append(macro) macro = "" param = "" value = "" content = "" in_use = False esc = False elif re.match("^$", strip): if do_print and in_use: value = trimls(value) if param != "": content = content.replace(param, value) if content != "": value = content value = value.replace("", "-->") value = html.escape(value) defs.append([macro, value]) flags.append(macro) macro = "" param = "" value = "" content = "" in_use = False esc = False elif m := re.match("^$", strip): if do_print: sep1 = m.group(1) sep2 = m.group(2) subs = m.group(3).split(sep2) for sub in subs: x = sub.split(sep1, 1) if len(x) >= 1 and x[0] != "": flags.append(x[0]) if len(x) == 2 and x[0] != "": name = x[0] val = replace_macro(x[1], defs) defs.append([name, val]) elif m := re.match("^$", strip): if do_print: sep = m.group(1) names = m.group(2).split(sep) for flag in names: if flag == "": continue while flag in flags: flags.remove(flag) for i in range(len(defs))[::-1]: if defs[i][0] == flag: defs.pop(i) elif re.match("^$", strip): if do_print: esc = True elif m := re.match("^$", strip): if do_print: filename = m.group(1) with rdopen(filename) as f: hpp(f, n + 1, flags, defs) elif re.match("^$", strip): pass elif do_print: row = replace_macro(row, defs) if in_use: n1 = len(indent) n2 = len(useindent) if strip == "": value += ls elif n1 < n2: value += row else: value += row[n2:] elif n == 0: print(row, end="") else: retval += row return retval def get_content(n, name, flags, defs): cmdname = os.path.basename(sys.argv[0]) if (name[0].isalnum()): with rdopen(name) as f: return trimls(hpp(f, n + 1, flags, defs)) else: for x in defs: if x[0] == name: return x[1] err = "{0}: {1} は定義されていません。".format(cmdname, name) print(err, file=sys.stderr) sys.exit(1) def trimls(text): if len(text) >= 1 and text[-1] == "\n": text = text[0:-1] if len(text) >= 1 and text[-1] == "\r": text = text[0:-1] return text def subst(text, subs, sep, defs): for sub in subs: x = sub.split(sep, 1) if len(x) == 2 and x[0] != "": par = x[0] val = replace_macro(x[1], defs) text = text.replace(par, val) return text def replace_macro(text, defs): for x in defs: text = text.replace(x[0], x[1]) return text def rdopen(filename): return open(filename, encoding = "utf-8") def show_macro(macro, defs): for x in defs: if x[0] == macro: print(x[1], file=sys.stderr) hpp(sys.stdin)