Skip to content

Commit 724ebcd

Browse files
committed
merge with master
2 parents 92d51f1 + 5ee4189 commit 724ebcd

5 files changed

Lines changed: 148 additions & 2 deletions

File tree

pixie/stdlib.lisp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@
115115
(extend -hash Integer hash-int)
116116

117117
(extend -eq Integer -num-eq)
118+
(extend -eq Float -num-eq)
119+
(extend -eq Ratio -num-eq)
118120

119121
(def ordered-hash-reducing-fn
120122
(fn ordered-hash-reducing-fn
@@ -437,7 +439,6 @@
437439
(fn [x & args]
438440
(apply f (if (nil? x) else x) args)))
439441

440-
441442
(defmacro foreach [binding & body]
442443
(assert (= 2 (count binding)) "binding and collection required")
443444
`(reduce

pixie/vm/compiler.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,9 @@ def compile_form(form, ctx):
313313
if isinstance(form, numbers.Float):
314314
ctx.push_const(form)
315315
return
316+
if isinstance(form, numbers.Ratio):
317+
ctx.push_const(form)
318+
return
316319

317320
if isinstance(form, symbol.Symbol):
318321
name = form._str

pixie/vm/numbers.py

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,23 @@ def float_val(self):
4545
def type(self):
4646
return Float._type
4747

48+
class Ratio(Number):
49+
_type = object.Type(u"pixie.stdlib.Ratio")
50+
_immutable_fields_ = ["_numerator", "_denominator"]
51+
52+
def __init__(self, numerator, denominator):
53+
self._numerator = numerator
54+
self._denominator = denominator
55+
56+
def numerator(self):
57+
return self._numerator
58+
59+
def denominator(self):
60+
return self._denominator
61+
62+
def type(self):
63+
return Ratio._type
64+
4865
IMath = as_var("IMath")(Protocol(u"IMath"))
4966
_add = as_var("-add")(DoublePolymorphicFn(u"-add", IMath))
5067
_sub = as_var("-sub")(DoublePolymorphicFn(u"-sub", IMath))
@@ -72,13 +89,117 @@ def define_num_ops():
7289
for (c1, conv1) in num_classes:
7390
for (c2, conv2) in num_classes:
7491
for (op, sym) in [("_add", "+"), ("_sub", "-"), ("_mul", "*"), ("_div", "/")]:
92+
if op == "_div" and c1 == Integer and c2 == Integer:
93+
continue
7594
extend_num_op(op, c1, c2, conv1, sym, conv2)
7695
extend_num_op("_num_eq", c1, c2, conv1, "==", conv2,
7796
wrap_start = "true if ", wrap_end = " else false")
7897

7998
define_num_ops()
8099

81100

101+
def gcd(u, v):
102+
while v != 0:
103+
r = u % v
104+
u = v
105+
v = r
106+
return u
107+
108+
@extend(_div, Integer._type, Integer._type)
109+
def _div(n, d):
110+
assert isinstance(n, Integer) and isinstance(d, Integer)
111+
nv = n.int_val()
112+
dv = d.int_val()
113+
object.affirm(dv != 0, u"Divide by zero")
114+
g = gcd(nv, dv)
115+
if g == 0:
116+
return rt.wrap(0)
117+
nv = nv / g
118+
dv = dv / g
119+
if dv == 1:
120+
return rt.wrap(nv)
121+
elif dv == -1:
122+
return rt.wrap(-1 * nv)
123+
else:
124+
if dv < 0:
125+
nv = nv * -1
126+
dv = dv * -1
127+
return Ratio(nv, dv)
128+
129+
@extend(_add, Ratio._type, Ratio._type)
130+
def _add(a, b):
131+
assert isinstance(a, Ratio) and isinstance(b, Ratio)
132+
return rt._div(rt._add(rt.wrap(b.numerator() * a.denominator()),
133+
rt.wrap(a.numerator() * b.denominator())),
134+
rt.wrap(a.denominator() * b.denominator()))
135+
136+
@extend(_sub, Ratio._type, Ratio._type)
137+
def _sub(a, b):
138+
assert isinstance(a, Ratio) and isinstance(b, Ratio)
139+
return rt._div(rt._add(rt.wrap(-1 * b.numerator() * a.denominator()),
140+
rt.wrap(a.numerator() * b.denominator())),
141+
rt.wrap(a.denominator() * b.denominator()))
142+
143+
@extend(_mul, Ratio._type, Ratio._type)
144+
def _mul(a, b):
145+
assert isinstance(a, Ratio) and isinstance(b, Ratio)
146+
return rt._div(rt.wrap(b.numerator() * a.numerator()),
147+
rt.wrap(b.denominator() * a.denominator()))
148+
149+
@extend(_div, Ratio._type, Ratio._type)
150+
def _div(a, b):
151+
assert isinstance(a, Ratio) and isinstance(b, Ratio)
152+
return rt._div(rt.wrap(b.denominator() * a.numerator()),
153+
rt.wrap(b.numerator() * a.denominator()))
154+
155+
@extend(_num_eq, Ratio._type, Ratio._type)
156+
def _num_eq(a, b):
157+
assert isinstance(a, Ratio) and isinstance(b, Ratio)
158+
return true if a.numerator() == b.numerator() and a.denominator() == b.denominator() else false
159+
160+
ratio_op_tmpl = """@extend({pfn}, {ty1}._type, {ty2}._type)
161+
def {pfn}_{ty1}_{ty2}(a, b):
162+
assert isinstance(a, {ty1}) and isinstance(b, {ty2})
163+
return rt.{pfn}({conv1}(a), {conv2}(b))
164+
"""
165+
166+
def to_ratio(x):
167+
if isinstance(x, Ratio):
168+
return x
169+
else:
170+
return Ratio(x.int_val(), 1)
171+
172+
def to_ratio_conv(c):
173+
if c == Ratio:
174+
return ""
175+
else:
176+
return "to_ratio"
177+
178+
def to_float(x):
179+
if isinstance(x, Float):
180+
return x
181+
else:
182+
return rt.wrap(x.numerator() / float(x.denominator()))
183+
184+
def to_float_conv(c):
185+
if c == Float:
186+
return ""
187+
else:
188+
return "to_float"
189+
190+
def define_ratio_ops():
191+
for (c1, c2) in [(Integer, Ratio), (Ratio, Integer)]:
192+
for op in ["_add", "_sub", "_mul", "_div", "_num_eq"]:
193+
code = ratio_op_tmpl.format(pfn=op, ty1=c1.__name__, ty2=c2.__name__, conv1=to_ratio_conv(c1), conv2=to_ratio_conv(c2))
194+
exec code
195+
196+
for (c1, c2) in [(Float, Ratio), (Ratio, Float)]:
197+
for op in ["_add", "_sub", "_mul", "_div", "_num_eq"]:
198+
code = ratio_op_tmpl.format(pfn=op, ty1=c1.__name__, ty2=c2.__name__, conv1=to_float_conv(c1), conv2=to_float_conv(c2))
199+
exec code
200+
201+
define_ratio_ops()
202+
82203
# def add(a, b):
83204
# if isinstance(a, Integer):
84205
# if isinstance(b, Integer):
@@ -113,3 +234,11 @@ def _str(f):
113234
@extend(proto._repr, Float._type)
114235
def _repr(f):
115236
return rt.wrap(unicode(str(f.float_val())))
237+
238+
@extend(proto._repr, Ratio._type)
239+
def _repr(r):
240+
return rt.wrap(unicode(str(r.numerator()) + "/" + str(r.denominator())))
241+
242+
@extend(proto._str, Ratio._type)
243+
def _str(r):
244+
return rt.wrap(unicode(str(r.numerator()) + "/" + str(r.denominator())))

pixie/vm/reader.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,7 @@ def invoke(self, rdr, ch):
400400
int_matcher = re.compile(u'^([-+]?)(?:(0[xX])([0-9a-fA-F]+)|0([0-7]+)|([1-9][0-9]?)[rR]([0-9a-zA-Z]+)|([0-9]*))$')
401401

402402
float_matcher = re.compile(u'^([-+]?[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$')
403+
ratio_matcher = re.compile(u'^([-+]?[0-9]+)/([0-9]+)$')
403404

404405
def parse_int(m):
405406
sign = 1
@@ -427,6 +428,11 @@ def parse_int(m):
427428
def parse_float(m):
428429
return rt.wrap(float(str(m.group(0))))
429430

431+
def parse_ratio(m):
432+
n = rt.wrap(int(str(m.group(1))))
433+
d = rt.wrap(int(str(m.group(2))))
434+
return rt._div(n, d)
435+
430436
def parse_number(s):
431437
m = int_matcher.match(s)
432438
if m:
@@ -436,7 +442,11 @@ def parse_number(s):
436442
if m:
437443
return parse_float(m)
438444
else:
439-
return None
445+
m = ratio_matcher.match(s)
446+
if m:
447+
return parse_ratio(m)
448+
else:
449+
return None
440450

441451
def read_number(rdr, ch):
442452
acc = [ch]

pixie/vm/rt.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,16 @@ def wrapper(*args):
6666
import pixie.vm.libs.ffi
6767
import pixie.vm.symbol
6868
import pixie.vm.libs.path
69+
import pixie.vm.libs.string
6970

7071

7172

7273
numbers.init()
7374

7475
@specialize.argtype(0)
7576
def wrap(x):
77+
if isinstance(x, bool):
78+
return true if x else false
7679
if isinstance(x, int):
7780
return numbers.Integer(x)
7881
if isinstance(x, float):

0 commit comments

Comments
 (0)