• R/O
  • HTTP
  • SSH
  • HTTPS

提交

标签
No Tags

Frequently used words (click to add to your profile)

javac++androidlinuxc#windowsobjective-ccocoa誰得qtpythonphprubygameguibathyscaphec計画中(planning stage)翻訳omegatframeworktwitterdomtestvb.netdirectxゲームエンジンbtronarduinopreviewer

A categorical programming language


Commit MetaInfo

修订版a493d594f55f546bed94bbed9f5a08547ff4ba02 (tree)
时间2023-01-03 09:20:01
作者Corbin <cds@corb...>
CommiterCorbin

Log Message

Really get hacking with graph reduction in RPython.

更改概述

差异

--- a/cammy-rpy/builder.nix
+++ b/cammy-rpy/builder.nix
@@ -2,17 +2,15 @@ entrypoint: exe: enableJIT:
22 { nixpkgs ? import <nixpkgs> {}, jelly ? null, movelist ? null }:
33 let
44 inherit (nixpkgs.pkgs)
5- fetchFromGitLab stdenv
5+ fetchhg stdenv
66 pkg-config libffi stb libcaca
77 python2 python2Packages;
88 # https://foss.heptapod.net/pypy/pypy/
9- pypySrc = fetchFromGitLab {
10- domain = "foss.heptapod.net";
11- owner = "pypy";
12- repo = "pypy";
13- # release candidate from branch release-pypy3.8-v7.x
14- rev = "90fd9ed34d52181de59cbfff863719472b05418e";
15- sha256 = "03cshgvh8qcsyac4q4vf0sbvcm1m2ikgwycwip4cc7sw9pzpw6a3";
9+ pypySrc = fetchhg {
10+ url = "https://foss.heptapod.net/pypy/pypy";
11+ # tag release-pypy3.8-v7.3.11
12+ rev = "release-pypy3.8-v7.3.11";
13+ sha256 = "sha256-yHIY4cuCpfB0Nl1Odutc9w2QUPhWK+rxaSGfbBV+ZUg=";
1614 };
1715 in
1816 let
--- /dev/null
+++ b/cammy-rpy/cammylib/graph.py
@@ -0,0 +1,111 @@
1+# Basic graph reduction.
2+# Heavily inspired by https://theory.stanford.edu/~blynn/lambda/cl.html
3+# and https://www.youtube.com/watch?v=GawiQQCn3bk
4+
5+from cammylib.parser import makeParser
6+
7+class InvalidGraph(Exception): pass
8+
9+class Node(object):
10+ _immutable_ = True
11+ def asStr(self): return self.symbol
12+
13+class Unit(Node):
14+ _immutable_ = True
15+ def asStr(self): return "*"
16+unit = Unit()
17+
18+class App(Node):
19+ _immutable_ = True
20+
21+ def __init__(self, f, x):
22+ self.f = f
23+ self.x = x
24+
25+ def asStr(self): return "(@ %s %s)" % (self.f.asStr(), self.x.asStr())
26+
27+class WordBox(Node):
28+ _immutable_ = True
29+ def __init__(self, w): self.w = w
30+ def asStr(self): return "(w# %d)" % self.w
31+
32+lits = {
33+ "Z": WordBox(0),
34+ "U": unit,
35+}
36+prims = {name: type(name, (Node,), {"_immutable_": True, "symbol": name})() for name in (
37+ "I", "B", "C", "K", "S", "N", "1+", "FZ", "FO",
38+)}
39+
40+def buildAtom(symbol):
41+ if symbol in lits: return lits[symbol]
42+ elif symbol in prims: return prims[symbol]
43+ else: raise InvalidGraph("can only build lits and prims")
44+def buildApp(head, args):
45+ if head != "@": raise InvalidGraph("can only build apps")
46+ if len(args) != 2: raise InvalidGraph("can only do binary app")
47+ return App(args[0], args[1])
48+GraphParser = makeParser("GraphParser", buildAtom, buildApp)
49+
50+def app(f, x):
51+ if f is prims["I"]: return x
52+ elif isinstance(f, WordBox): return f
53+ else: return App(f, x)
54+def comp(g, f):
55+ if f is prims["I"]: return g
56+ elif g is prims["I"]: return f
57+ return app(app(prims["B"], g), f)
58+
59+def reduce(g):
60+ # Heap pointer, value stack
61+ hp = g
62+ s = []
63+
64+ while isinstance(hp, App):
65+ print "---"
66+ print "Current hp:", hp.asStr()
67+ print "Current stack:"
68+ for v in s: print v.asStr()
69+ if hp.f is prims["I"] and s: hp = hp.x
70+ elif hp.f is prims["B"] and len(s) >= 2:
71+ g = hp.x
72+ f = s.pop()
73+ x = s.pop()
74+ hp = app(g, app(f, x))
75+ elif hp.f is prims["1+"]:
76+ n = reduce(hp.x)
77+ if isinstance(n, WordBox): hp = WordBox(n.w + 1)
78+ else: hp = app(hp.f, n)
79+ elif hp.f is prims["N"] and len(s) >= 2:
80+ x = hp.x
81+ f = s.pop()
82+ n = reduce(s.pop())
83+ if isinstance(n, WordBox):
84+ # Exponentiate by squaring
85+ i = n.w
86+ v = prims["I"]
87+ while i:
88+ if i & 1: v = comp(v, f)
89+ f = comp(f, f)
90+ i >>= 1
91+ hp = app(v, x)
92+ else:
93+ # not fully evaluated
94+ s.append(n)
95+ s.append(f)
96+ else: s.append(hp.x); hp = hp.f
97+
98+ # Rebuild spine if not fully applied
99+ while s: hp = app(hp, s.pop())
100+ return hp
101+
102+def doGraph(path):
103+ print "Reading graph from", path
104+ with open(path, "rb") as handle: parser = GraphParser(handle.read())
105+ g = parser.takeExpression()
106+ print "Got graph", g.asStr()
107+ applied = app(g, unit)
108+ print "Applying graph to unit to get", applied.asStr()
109+ reduced = reduce(applied)
110+ print "Reduced to", reduced.asStr()
111+ return 0
--- a/cammy-rpy/cammylib/parser.py
+++ b/cammy-rpy/cammylib/parser.py
@@ -5,59 +5,70 @@ class ParseError(Exception):
55 def __init__(self, message):
66 self.message = message
77
8-class CammyParser(object):
9- def __init__(self, s):
10- self._i = 0
11- self._s = s
12-
13- def position(self):
14- return self._i
15-
16- def hasMore(self):
17- return self._i < len(self._s)
18-
19- def eatWhitespace(self):
20- while self.hasMore() and self._s[self._i] in (" ", "\n"):
21- self._i += 1
22-
23- def canAndDoesEat(self, c):
24- if self._s[self._i] == c:
25- self._i += 1
26- return True
27- else:
28- return False
29-
30- def takeName(self):
31- start = self._i
32- while self.hasMore() and self._s[self._i] not in (")", "(", " ", "\n"):
33- self._i += 1
34- stop = self._i
35- return self._s[start:stop]
36-
37- def takeExpression(self):
38- self.eatWhitespace()
39- if self.canAndDoesEat('('):
40- return self.startFunctor()
41- else:
42- symbol = self.takeName()
43- if symbol.startswith("@"):
44- try:
45- return sexp.Hole(int(symbol[1:]))
46- except ValueError:
47- return sexp.Atom(symbol)
48- return sexp.Atom(symbol)
8+def makeParser(parserName, buildAtom, buildList):
9+ class Parser(object):
10+ def __init__(self, s):
11+ self._i = 0
12+ self._s = s
13+
14+ def position(self):
15+ return self._i
16+
17+ def hasMore(self):
18+ return self._i < len(self._s)
19+
20+ def eatWhitespace(self):
21+ while self.hasMore() and self._s[self._i] in (" ", "\n"):
22+ self._i += 1
4923
50- def startFunctor(self):
51- start = self._i
52- head = self.takeName()
53- self.eatWhitespace()
54- args = []
55- while not self.canAndDoesEat(')'):
56- args.append(self.takeExpression())
24+ def canAndDoesEat(self, c):
25+ if self._s[self._i] == c:
26+ self._i += 1
27+ return True
28+ else:
29+ return False
30+
31+ def takeName(self):
32+ start = self._i
33+ while self.hasMore() and self._s[self._i] not in (")", "(", " ", "\n"):
34+ self._i += 1
35+ stop = self._i
36+ return self._s[start:stop]
37+
38+ def takeExpression(self):
5739 self.eatWhitespace()
58- if not self.hasMore():
59- raise ParseError("Unterminated functor starting at %d" % start)
60- return sexp.Functor(head, args)
40+ if self.canAndDoesEat('('):
41+ return self.startFunctor()
42+ else:
43+ symbol = self.takeName()
44+ return buildAtom(symbol)
45+
46+ def startFunctor(self):
47+ start = self._i
48+ head = self.takeName()
49+ self.eatWhitespace()
50+ args = []
51+ while not self.canAndDoesEat(')'):
52+ args.append(self.takeExpression())
53+ self.eatWhitespace()
54+ if not self.hasMore():
55+ raise ParseError("Unterminated functor starting at %d" % start)
56+ return buildList(head, args)
57+
58+ Parser.__name__ = parserName
59+ return Parser
60+
61+def buildCammyAtom(symbol):
62+ if symbol.startswith("@"):
63+ try:
64+ return sexp.Hole(int(symbol[1:]))
65+ except ValueError:
66+ return sexp.Atom(symbol)
67+ return sexp.Atom(symbol)
68+
69+def buildCammyTemplate(head, args): return sexp.Functor(head, args)
70+
71+CammyParser = makeParser("CammyParser", buildCammyAtom, buildCammyTemplate)
6172
6273
6374 def parse(s):
--- a/cammy-rpy/main.py
+++ b/cammy-rpy/main.py
@@ -5,6 +5,7 @@ from cammylib.hax import patch_ctypes_for_ffi
55
66 from animate import doAnimate
77 from draw import doDraw
8+from cammylib.graph import doGraph
89 from repl import doREPL
910
1011 def help(exe, problem):
@@ -13,6 +14,7 @@ def help(exe, problem):
1314 print "Usage:"
1415 print " animate PROG WIDTH HEIGHT Animate in the terminal"
1516 print " draw PROG WIDTH HEIGHT OUT Draw a picture"
17+ print " graph PROG Uuuuhhhh"
1618 print " repl Start an interactive session"
1719 print "Set the hive path by exporting CAMMY_HIVE"
1820
@@ -21,19 +23,26 @@ def main(argv):
2123 if len(argv) < 2:
2224 help(exe, "no command specified")
2325 return 1
24- hivepath = os.environ["CAMMY_HIVE"]
2526 command = argv[1]
2627 # XXX unrolling_iterable
2728 if command == "animate":
2829 if len(argv) != 5:
2930 help(exe, "animate takes three arguments")
3031 return 1
32+ hivepath = os.environ["CAMMY_HIVE"]
3133 return doAnimate(hivepath, argv[2], int(argv[3]), int(argv[4]))
3234 elif command == "draw":
3335 if len(argv) != 6:
3436 help(exe, "draw takes four arguments")
37+ return 1
3538 return doDraw(argv[2], argv[3], argv[4], argv[5])
39+ elif command == "graph":
40+ if len(argv) != 3:
41+ help(exe, "graph takes one argument")
42+ return 1
43+ return doGraph(argv[2])
3644 elif command == "repl":
45+ hivepath = os.environ["CAMMY_HIVE"]
3746 return doREPL(hivepath)
3847 else:
3948 help(exe, "unknown command: " + command)
--- a/movelist/cammy-graph.scm
+++ b/movelist/cammy-graph.scm
@@ -20,6 +20,7 @@
2020 ((== expr 'id) (== tree 'I))
2121 ((== expr `(comp ,f ,g)) (== tree `(@ (@ B ,tg) ,tf))
2222 (combinators° f tf) (combinators° g tg))
23+ ((== expr 'ignore) (== tree 'U))
2324 ; \x l -> l (f x) (g x)
2425 ((== expr `(pair ,f ,g)) (== tree `(@ (@ S (@ (@ B C) (@ (@ B (@ C I)) ,tf))) ,tg))
2526 (combinators° f tf) (combinators° g tg))