├── src ├── __init__.py ├── main.py ├── tools.py ├── gen.py ├── term.py ├── trans.py ├── parse.py ├── raddsl_rewrite.py └── raddsl_parse.py ├── .gitignore ├── README.md ├── examples ├── fact.c └── fib.c ├── pigletc.py └── LICENSE /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.pyc 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/true-grue/PigletC/HEAD/README.md -------------------------------------------------------------------------------- /examples/fact.c: -------------------------------------------------------------------------------- 1 | int r; 2 | int n; 3 | 4 | void main() { 5 | n = 5; 6 | r = 1; 7 | while (n > 1) { 8 | r = r * n; 9 | n = n - 1; 10 | } 11 | print(r); 12 | } 13 | -------------------------------------------------------------------------------- /examples/fib.c: -------------------------------------------------------------------------------- 1 | int n; 2 | int a; 3 | int b; 4 | int i; 5 | int t; 6 | 7 | void main() { 8 | a = 0; 9 | b = 1; 10 | i = 0; 11 | n = 20; 12 | while (i < n) { 13 | t = a; 14 | a = b; 15 | b = t + b; 16 | i = i + 1; 17 | } 18 | print(a); 19 | } 20 | -------------------------------------------------------------------------------- /pigletc.py: -------------------------------------------------------------------------------- 1 | # PigletC: a tiny C-like language compiler for PigletVM, 2 | # see https://github.com/vkazanov/bytecode-interpreters-post 3 | # Author: Peter Sovietov 4 | 5 | import os 6 | import sys 7 | from src.main import compile 8 | 9 | if len(sys.argv) == 2 and os.path.isfile(sys.argv[1]): 10 | path = sys.argv[1] 11 | with open(path) as f: 12 | src = f.read() 13 | c = compile(path, src) 14 | with open("%s.pvm" % path, "w") as f: 15 | f.write(c.asm) 16 | else: 17 | print("usage: pigletc.py file.c") 18 | -------------------------------------------------------------------------------- /src/main.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | from .parse import parse 4 | from .trans import trans 5 | from .gen import gen 6 | from .tools import error, perform 7 | 8 | 9 | class Compiler: 10 | def __init__(self, path, text): 11 | self.path = path 12 | self.text = text 13 | self.table = {} 14 | self.data_cnt = 0 15 | self.label_cnt = 0 16 | self.ir = [] 17 | self.asm = "" 18 | 19 | 20 | def compile(path, text): 21 | c = Compiler(path, text) 22 | ast = parse(text, lambda p: error(c, "syntax error", p)) 23 | perform(trans, ast, c=c) 24 | c.asm = "\n".join(perform(gen, c.ir, c=c) + ["DONE", ""]) 25 | return c 26 | -------------------------------------------------------------------------------- /src/tools.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | import sys 4 | from .raddsl_rewrite import Tree, apply 5 | from .term import is_term, is_list 6 | 7 | 8 | def error(c, msg, pos=None): 9 | if pos is None: 10 | print(msg) 11 | else: 12 | line = c.text.count("\n", 0, pos) 13 | col = pos - (c.text.rfind("\n", 0, pos) + 1) 14 | print("%s:%d:%d: %s" % (c.path, line + 1, col + 1, msg)) 15 | sys.exit(1) 16 | 17 | 18 | def perform(rules, ast, **attrs): 19 | t = Tree(ast) 20 | for a in attrs: 21 | setattr(t, a, attrs[a]) 22 | if not apply(t, rules): 23 | print("apply error") 24 | sys.exit(1) 25 | return t.out 26 | 27 | 28 | def flatten(ast): 29 | if is_term(ast): 30 | map(flatten, ast) 31 | elif is_list(ast): 32 | lst = [] 33 | for x in ast: 34 | y = flatten(x) 35 | lst.extend(y if is_list(x) else [y]) 36 | ast[:] = lst 37 | return ast 38 | -------------------------------------------------------------------------------- /src/gen.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | from .raddsl_rewrite import * 4 | from .term import * 5 | 6 | X, Y = let(X=any), let(Y=any) 7 | 8 | rules = rule(alt( 9 | seq(Push(Int(X)), to(lambda e: "PUSHI %d" % e.X)), 10 | seq(Load(), to(lambda e: "LOAD")), 11 | seq(Store(), to(lambda e: "STORE")), 12 | seq(Call(), to(lambda e: "CALL")), 13 | seq(Bop("+"), to(lambda e: "ADD")), 14 | seq(Bop("-"), to(lambda e: "SUB")), 15 | seq(Bop("*"), to(lambda e: "MUL")), 16 | seq(Bop("/"), to(lambda e: "DIV")), 17 | seq(Bop("<"), to(lambda e: "LESS")), 18 | seq(Bop(">"), to(lambda e: "GREATER")), 19 | seq(Bop("<="), to(lambda e: "LESS_OR_EQUAL")), 20 | seq(Bop(">="), to(lambda e: "GREATER_OR_EQUAL")), 21 | seq(Bop("=="), to(lambda e: "EQUAL")), 22 | seq(Bop("!="), to(lambda e: "EQUAL\nPUSHI 0\nEQUAL")), 23 | seq(Label(X), to(lambda e: "L%d:" % e.X)), 24 | seq(Jump(X), to(lambda e: "JUMP L%d" % e.X)), 25 | seq(JumpIf0(X), to(lambda e: "JUMP_IF_FALSE L%d" % e.X)), 26 | seq(Asm(X), to(lambda e: e.X)) 27 | )) 28 | 29 | gen = many(rules) 30 | -------------------------------------------------------------------------------- /src/term.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | 4 | class Head(dict): 5 | def __eq__(self, right): 6 | return self["tag"] == right 7 | 8 | def __ne__(self, right): 9 | return not self.__eq__(right) 10 | 11 | def __repr__(self): 12 | return self["tag"] 13 | 14 | 15 | def make_term(tag): 16 | return lambda *args, **attrs: (Head(tag=tag, **attrs),) + args 17 | 18 | 19 | def is_term(x): 20 | return isinstance(x, tuple) 21 | 22 | 23 | def is_list(x): 24 | return isinstance(x, list) 25 | 26 | 27 | def attr(term, name): 28 | return term[0][name] 29 | 30 | 31 | Id = make_term("Id") 32 | Int = make_term("Int") 33 | Op = make_term("Op") 34 | Bop = make_term("Bop") 35 | Assign = make_term("Assign") 36 | Call = make_term("Call") 37 | If = make_term("If") 38 | While = make_term("While") 39 | Var = make_term("Var") 40 | Func = make_term("Func") 41 | Push = make_term("Push") 42 | Load = make_term("Load") 43 | Store = make_term("Store") 44 | Label = make_term("Label") 45 | Jump = make_term("Jump") 46 | JumpIf0 = make_term("JumpIf0") 47 | Asm = make_term("Asm") 48 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 Peter Sovietov. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/trans.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | from .raddsl_rewrite import * 4 | from .term import * 5 | from .tools import flatten, error 6 | 7 | 8 | def add_var(X): 9 | def walk(t): 10 | t.c.table[X] = dict(ast=t.out, offs=t.c.data_cnt) 11 | t.c.data_cnt += 1 12 | return True 13 | return walk 14 | 15 | 16 | def add_func(X, Y): 17 | def walk(t): 18 | t.c.table[X] = dict(ast=t.out, offs=len(t.c.ir)) 19 | t.c.ir.extend(flatten(Y)) 20 | return True 21 | return walk 22 | 23 | 24 | def label(t): 25 | t.out = t.c.label_cnt 26 | t.c.label_cnt += 1 27 | return True 28 | 29 | 30 | def offs(tag): 31 | def walk(t): 32 | entry = t.c.table.get(t.out[1]) 33 | if not entry or attr(entry["ast"], "tag") != tag: 34 | error(t.c, "unknown name '%s'" % t.out[1], attr(t.out, "pos")) 35 | t.out = Int(entry["offs"]) 36 | return True 37 | return walk 38 | 39 | 40 | expr = delay(lambda: expr) 41 | block = delay(lambda: block) 42 | 43 | expr = alt( 44 | rule(let(X=seq(Id(any), offs("Var"))), to(lambda e: [Push(e.X), Load()])), 45 | rule(let(X=Int(any)), to(lambda e: [Push(e.X)])), 46 | rule(Bop(let(O=any), let(X=expr), let(Y=expr)), 47 | to(lambda e: [e.X, e.Y, Bop(e.O)])) 48 | ) 49 | 50 | assign_st = rule( 51 | Assign(let(X=expr), let(Y=expr)), to(lambda e: [e.X[:-1], e.Y, Store()]) 52 | ) 53 | 54 | if_st = rule( 55 | If(let(X=expr), let(Y=block)), let(L1=label), 56 | to(lambda e: [e.X, JumpIf0(e.L1), e.Y, Label(e.L1)]) 57 | ) 58 | 59 | while_st = rule( 60 | While(let(X=expr), let(Y=block)), 61 | let(L1=label), let(L2=label), to(lambda e: [ 62 | Label(e.L1), 63 | e.X, 64 | JumpIf0(e.L2), 65 | e.Y, 66 | Jump(e.L1), 67 | Label(e.L2) 68 | ]) 69 | ) 70 | 71 | call_st = alt( 72 | rule(Call(Id("print"), [let(X=expr)]), to(lambda e: [e.X, Asm("PRINT")])), 73 | rule(Call(let(X=seq(Id(any), offs("Func"))), []), 74 | to(lambda e: [Call(e.X)])) 75 | ) 76 | 77 | stmt = alt( 78 | assign_st, 79 | if_st, 80 | while_st, 81 | call_st, 82 | block 83 | ) 84 | 85 | block = many(stmt) 86 | 87 | trans = many(alt( 88 | rule(Var(Id(let(X=any))), env(lambda e: add_var(e.X))), 89 | rule(Func(Id(let(X=any)), let(Y=block)), env(lambda e: add_func(e.X, e.Y))) 90 | )) 91 | -------------------------------------------------------------------------------- /src/parse.py: -------------------------------------------------------------------------------- 1 | # PigletC 2 | 3 | from .raddsl_parse import * 4 | from .term import * 5 | 6 | 7 | def mark(s): 8 | s.out.append(s.pos) 9 | return True 10 | 11 | 12 | def ast_ident(kw): 13 | return to(2, 14 | lambda p, x: Op(x, pos=p) if x in kw else Id(x, pos=p)) 15 | 16 | 17 | ast_integer = to(2, lambda p, x: Int(int(x), pos=p)) 18 | ast_op = to(2, lambda p, x: Op(x, pos=p)) 19 | ast_bop = to(3, lambda x, o, y: Bop(o[1], x, y, pos=attr(o, "pos"))) 20 | ast_assign = to(2, lambda x, y: Assign(x, y, pos=attr(x, "pos"))) 21 | ast_call = to(2, lambda x, y: Call(x, y, pos=attr(x, "pos"))) 22 | ast_if = to(2, lambda x, y: If(x, y, pos=attr(x, "pos"))) 23 | ast_while = to(2, lambda x, y: While(x, y, pos=attr(x, "pos"))) 24 | ast_var = to(1, lambda x: Var(x, pos=attr(x, "pos"))) 25 | ast_func = to(2, lambda x, y: Func(x, y, pos=attr(x, "pos"))) 26 | 27 | OPERATORS = "; ( ) { } + - * / = != < <= > >= ==".split() 28 | KEYWORDS = "if while int void".split() 29 | single_comment = seq(a("//"), many(non(a("\n")))) 30 | multi_comment = seq(a("/*"), many(non(a("*/"))), a("*/")) 31 | comment = alt(single_comment, multi_comment) 32 | ws = many(alt(space, comment)) 33 | name = seq(cite(letter, many(alt(letter, digit))), ast_ident(KEYWORDS)) 34 | integer = seq(cite(some(digit)), ast_integer) 35 | operator = seq(cite(match(OPERATORS)), ast_op) 36 | token = memo(seq(ws, mark, alt(operator, name, integer))) 37 | ident = seq(token, guard(lambda x: x[0] == "Id")) 38 | 39 | 40 | def op(o): return seq(token, guard(lambda x: x == ("Op", o)), drop) 41 | 42 | 43 | def left(p): return seq(tab.expr(p + 1), ast_bop) 44 | 45 | 46 | def block(x): return block(x) 47 | 48 | 49 | tab = Prec(token, lambda x: x[1] if x[0] == "Op" else attr(x, "tag")) 50 | tab.prefix["Id"] = empty 51 | tab.prefix["Int"] = empty 52 | tab.prefix["("] = seq(drop, tab.expr(0), op(")")) 53 | tab.infix["!="] = left, 1 54 | tab.infix["=="] = left, 1 55 | tab.infix["<="] = left, 2 56 | tab.infix[">="] = left, 2 57 | tab.infix["<"] = left, 2 58 | tab.infix[">"] = left, 2 59 | tab.infix["+"] = left, 3 60 | tab.infix["-"] = left, 3 61 | tab.infix["*"] = left, 4 62 | tab.infix["/"] = left, 4 63 | 64 | assign = seq(ident, op("="), tab.expr(0), ast_assign) 65 | args = group(opt(list_of(tab.expr(0), op(",")))) 66 | call = seq(ident, op("("), args, op(")"), ast_call) 67 | if_st = seq(op("if"), tab.expr(0), block, ast_if) 68 | while_st = seq(op("while"), tab.expr(0), block, ast_while) 69 | stmt = alt(seq(alt(assign, call), op(";")), if_st, while_st) 70 | block = seq(op("{"), group(many(stmt)), op("}")) 71 | var_def = seq(op("int"), ident, op(";"), ast_var) 72 | func_def = seq(op("void"), ident, op("("), op(")"), block, ast_func) 73 | main = seq(many(alt(var_def, func_def)), ws, end) 74 | 75 | 76 | def parse(text, error): 77 | s = Stream(text) 78 | return s.out if main(s) else error(s.epos) 79 | -------------------------------------------------------------------------------- /src/raddsl_rewrite.py: -------------------------------------------------------------------------------- 1 | # raddsl 16112019 2 | # Author: Peter Sovietov 3 | 4 | 5 | SEQ_TYPES = tuple, list 6 | 7 | 8 | class Tree: 9 | def __init__(self, out=None, **attrs): 10 | self.__dict__.update(attrs) 11 | self.env = Env() 12 | self.out = out 13 | 14 | 15 | class Env(dict): 16 | def __getattr__(self, name): 17 | return self[name] 18 | 19 | 20 | def is_list(x): return type(x) in SEQ_TYPES 21 | 22 | 23 | def match_seq(tree, pat, x): 24 | i = 0 25 | for p in pat: 26 | if not match(tree, p, x[i]): 27 | return False 28 | i += 1 29 | return True 30 | 31 | 32 | def match(tree, pat, x): 33 | if callable(pat): 34 | x, tree.out = tree.out, x 35 | b, tree.out = pat(tree), x 36 | return b 37 | if not is_list(pat): 38 | return pat == x 39 | if is_list(x) and len(x) == len(pat): 40 | return match_seq(tree, pat, x) 41 | return False 42 | 43 | 44 | def apply(tree, pat): 45 | return pat(tree) if callable(pat) else match(tree, pat, tree.out) 46 | 47 | 48 | def non(pat): 49 | def walk(tree): 50 | return not apply(tree, pat) 51 | return walk 52 | 53 | 54 | def alt(*args): 55 | def walk(tree): 56 | old = tree.env 57 | for pat in args: 58 | if apply(tree, pat): 59 | return True 60 | tree.env = old 61 | return False 62 | return walk 63 | 64 | 65 | def seq(*args): 66 | def walk(tree): 67 | old = tree.out 68 | for pat in args: 69 | if not apply(tree, pat): 70 | tree.out = old 71 | return False 72 | return True 73 | return walk 74 | 75 | 76 | def let(**kwargs): 77 | name, pat = list(kwargs.items())[0] 78 | 79 | def walk(tree): 80 | if apply(tree, pat): 81 | if name in tree.env: 82 | return match(tree, tree.env[name], tree.out) 83 | tree.env = Env(tree.env) 84 | tree.env[name] = tree.out 85 | return True 86 | return False 87 | return walk 88 | 89 | 90 | def rule(*args): 91 | pat = seq(*args) 92 | 93 | def walk(tree): 94 | env = tree.env 95 | tree.env = Env() 96 | b = pat(tree) 97 | tree.env = env 98 | return b 99 | return walk 100 | 101 | 102 | def to(f): 103 | def walk(tree): 104 | tree.out = f(tree.env) 105 | return True 106 | return walk 107 | 108 | 109 | def where(*args): 110 | pat = seq(*args) 111 | 112 | def walk(tree): 113 | old = tree.out 114 | b = pat(tree) 115 | tree.out = old 116 | return b 117 | return walk 118 | 119 | 120 | def build(f): 121 | def walk(tree): 122 | tree.out = f(tree) 123 | return True 124 | return walk 125 | 126 | 127 | def cons(first, rest): 128 | def walk(tree): 129 | if is_list(tree.out) and tree.out: 130 | b = match(tree, first, tree.out[:len(first)]) 131 | return b and match(tree, rest, tree.out[len(first):]) 132 | return False 133 | return walk 134 | 135 | 136 | def rewrite_seq(tree, pat, x): 137 | old = x 138 | if type(x) is list: 139 | x = tuple(x) 140 | i = 0 141 | for p in pat: 142 | if not rewrite_rec(tree, p, x[i]): 143 | return False 144 | x = x[:i] + (tree.out,) + x[i + 1:] 145 | i += 1 146 | tree.out = list(x) if type(old) is list else x 147 | return True 148 | 149 | 150 | def rewrite_rec(tree, pat, x): 151 | tree.out = x 152 | if callable(pat): 153 | return pat(tree) 154 | if not is_list(pat): 155 | return pat == x 156 | if is_list(x) and len(x) == len(pat): 157 | return rewrite_seq(tree, pat, x) 158 | return False 159 | 160 | 161 | def rewrite(pat): 162 | def walk(tree): 163 | old = tree.out 164 | if not rewrite_rec(tree, pat, tree.out): 165 | tree.out = old 166 | return False 167 | return True 168 | return walk 169 | 170 | 171 | def many(pat): 172 | if not callable(pat): 173 | pat = seq(pat) 174 | 175 | def walk(tree): 176 | x = tree.out 177 | old = x 178 | if type(x) is list: 179 | x = tuple(x) 180 | i = 0 181 | for tree.out in old: 182 | if is_list(tree.out): 183 | if not pat(tree): 184 | tree.out = old 185 | return False 186 | x = x[:i] + (tree.out,) + x[i + 1:] 187 | i += 1 188 | tree.out = list(x) if type(old) is list else x 189 | return True 190 | return walk 191 | 192 | 193 | def repeat(pat): 194 | def walk(tree): 195 | while True: 196 | old = tree.out 197 | if not apply(tree, pat): 198 | tree.out = old 199 | return True 200 | return walk 201 | 202 | 203 | def any(t): return True 204 | 205 | 206 | def env(f): return lambda t: f(t.env)(t) 207 | 208 | 209 | def opt(*args): return alt(seq(*args), any) 210 | 211 | 212 | def delay(f): return lambda t: f()(t) 213 | 214 | 215 | def guard(f): return lambda t: f(t.env) 216 | 217 | 218 | def topdown(x): 219 | f = seq(x, many(delay(lambda: f))) 220 | return f 221 | 222 | 223 | def bottomup(x): 224 | f = seq(many(delay(lambda: f)), x) 225 | return f 226 | 227 | 228 | def innermost(x): 229 | f = bottomup(opt(seq(x, delay(lambda: f)))) 230 | return f 231 | 232 | 233 | def bottomup_except(stop, x): 234 | f = alt(stop, seq(many(delay(lambda: f)), x)) 235 | return f 236 | -------------------------------------------------------------------------------- /src/raddsl_parse.py: -------------------------------------------------------------------------------- 1 | # raddsl 16112019 2 | # Author: Peter Sovietov 3 | 4 | 5 | class Stream: 6 | def __init__(self, buf, **attrs): 7 | self.__dict__.update(attrs) 8 | self.buf = buf 9 | self.pos = 0 10 | self.epos = 0 11 | self.out = [] 12 | self.memo = {} 13 | 14 | 15 | def back(s, i, j, err=True): 16 | if err and s.pos > s.epos: 17 | s.epos = s.pos 18 | s.pos = i 19 | del s.out[j:] 20 | return False 21 | 22 | 23 | def peek(f): 24 | def parse(s): 25 | i, j = s.pos, len(s.out) 26 | t = f(s) 27 | s.pos = i 28 | del s.out[j:] 29 | return t 30 | return parse 31 | 32 | 33 | def npeek(f): 34 | def parse(s): 35 | i, j = s.pos, len(s.out) 36 | t = f(s) 37 | s.pos = i 38 | del s.out[j:] 39 | return not t 40 | return parse 41 | 42 | 43 | def alt(*args): 44 | def parse(s): 45 | for f in args: 46 | if f(s): 47 | return True 48 | return False 49 | return parse 50 | 51 | 52 | def many(f): 53 | def parse(s): 54 | while f(s): 55 | pass 56 | return True 57 | return parse 58 | 59 | 60 | def some(f): 61 | def parse(s): 62 | if not f(s): 63 | return False 64 | while f(s): 65 | pass 66 | return True 67 | return parse 68 | 69 | 70 | def seq(*args): 71 | def parse(s): 72 | i, j = s.pos, len(s.out) 73 | for f in args: 74 | if not f(s): 75 | return back(s, i, j) 76 | return True 77 | return parse 78 | 79 | 80 | def repeat(f, n): 81 | def parse(s): 82 | i, j = s.pos, len(s.out) 83 | for i in range(n): 84 | if not f(s): 85 | return back(s, i, j) 86 | return True 87 | return parse 88 | 89 | 90 | def cite(*args): 91 | f = seq(*args) 92 | 93 | def parse(s): 94 | i = s.pos 95 | if f(s): 96 | s.out.append(s.buf[i:s.pos]) 97 | return True 98 | return False 99 | return parse 100 | 101 | 102 | def push(f): 103 | def parse(s): 104 | i = s.pos 105 | if f(s): 106 | s.out.append(s.buf[i]) 107 | return True 108 | return False 109 | return parse 110 | 111 | 112 | def unpack(f): 113 | def parse(s): 114 | f(s) 115 | s.out += s.out.pop() 116 | return True 117 | return parse 118 | 119 | 120 | def guard(f): 121 | def parse(s): 122 | return f(s.out[-1]) 123 | return parse 124 | 125 | 126 | def to(n, f): 127 | def parse(s): 128 | i = len(s.out) - n 129 | s.out[i:] = [f(*s.out[i:])] 130 | return True 131 | return parse 132 | 133 | 134 | def group(*args): 135 | f = seq(*args) 136 | 137 | def parse(s): 138 | i = len(s.out) 139 | if f(s): 140 | s.out[i:] = [s.out[i:]] 141 | return True 142 | return False 143 | return parse 144 | 145 | 146 | def eat(f): 147 | def parse(s): 148 | if s.pos < len(s.buf) and f(s.buf[s.pos]): 149 | s.pos += 1 150 | return True 151 | return False 152 | return parse 153 | 154 | 155 | def a(val): 156 | def parse(s): 157 | i = s.pos + len(val) 158 | if s.buf[s.pos:i] == val: 159 | s.pos = i 160 | return True 161 | return False 162 | return parse 163 | 164 | 165 | def match(words): 166 | d = {} 167 | for w in words: 168 | d[len(w)] = d.get(len(w), set()) | set([w]) 169 | sets = sorted(d.items(), reverse=True) 170 | 171 | def parse(s): 172 | for x in sets: 173 | if s.buf[s.pos:s.pos + x[0]] in x[1]: 174 | s.pos += x[0] 175 | return True 176 | return False 177 | return parse 178 | 179 | 180 | def empty(s): return True 181 | 182 | 183 | def opt(f): return alt(f, empty) 184 | 185 | 186 | def non(f): return seq(npeek(f), any) 187 | 188 | 189 | def maybe(f, x): return alt(f, to(0, lambda: x)) 190 | 191 | 192 | def one_of(chars): return eat(lambda c: c in chars) 193 | 194 | 195 | def range_of(a, b): return eat(lambda x: a <= x <= b) 196 | 197 | 198 | def list_of(f, delim): return seq(f, many(seq(delim, f))) 199 | 200 | 201 | any = eat(lambda x: True) 202 | drop = unpack(to(1, lambda x: [])) 203 | end = npeek(any) 204 | digit = eat(lambda x: x.isdigit()) 205 | letter = eat(lambda x: x.isalpha()) 206 | lower = eat(lambda x: x.islower()) 207 | upper = eat(lambda x: x.isupper()) 208 | alnum = eat(lambda x: x.isalnum()) 209 | space = eat(lambda x: x.isspace()) 210 | 211 | 212 | def memo(f): 213 | def parse(s): 214 | key = f, s.pos 215 | if key in s.memo: 216 | o, s.pos = s.memo[key] 217 | s.out.append(o) 218 | return True 219 | if not f(s): 220 | return False 221 | s.memo[key] = s.out[-1], s.pos 222 | return True 223 | return parse 224 | 225 | 226 | class Prec: 227 | def __init__(self, token, tag): 228 | self.token = token 229 | self.tag = tag 230 | self.prefix = {} 231 | self.infix = {} 232 | 233 | def prefix_expr(self, s): 234 | if not self.token(s): 235 | return False 236 | e = self.prefix.get(self.tag(s.out[-1])) 237 | return e and e(s) 238 | 239 | def infix_expr(self, s, p): 240 | i, j = s.pos, len(s.out) 241 | if not self.token(s): 242 | return False 243 | e = self.infix.get(self.tag(s.out[-1])) 244 | if e and e[1] >= p: 245 | s.out.append(e) 246 | return True 247 | return back(s, i, j, False) 248 | 249 | def parse_expr(self, s, min_p): 250 | i, j = s.pos, len(s.out) 251 | if not self.prefix_expr(s): 252 | return back(s, i, j, False) 253 | while self.infix_expr(s, min_p): 254 | f, p = s.out.pop() 255 | if not f(p)(s): 256 | return back(s, i, j) 257 | return True 258 | 259 | def expr(self, p): return lambda s: self.parse_expr(s, p) 260 | --------------------------------------------------------------------------------