From: Tim Vieira Date: Thu, 11 Jul 2013 15:38:25 +0000 (-0400) Subject: Use Dyna Boolean type instead of Python's bool. This requires redefining or X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=53af654ca989d90f309f3ce614eed0e0803cb6f4;p=dyna2 Use Dyna Boolean type instead of Python's bool. This requires redefining or wrapping Python's comparsion (<=, ==), and logical operators (and, or,qq not). move examples/lists into repl tests. updated examples/force to use dyna lists updated some other tests.. --- diff --git a/examples/expected/lists.py.out b/examples/expected/lists.py.out deleted file mode 100644 index c678512..0000000 --- a/examples/expected/lists.py.out +++ /dev/null @@ -1,32 +0,0 @@ - -Solution -======== -a = [1, 2]. -b = true. -c = true. -d = false. -things = [1, [2, 2], [3, 4]]. -thingsbag = [1, 1, 2, "three"]. -thingset = [1, 2, "three"]. - -d/1 -=== -d(2) = true. -d(4) = true. - -f/1 -=== -f([1, 2]). - -foo/1 -===== -foo("a") = true. - -goal/1 -====== -goal([2]) = true. - -goo/1 -===== -goo([1, 2]) = true. - diff --git a/examples/force.dyna b/examples/force.dyna index f1845bb..2c4abb8 100644 --- a/examples/force.dyna +++ b/examples/force.dyna @@ -21,7 +21,7 @@ forceY(V,T) += f(U,V,T) * (y(U,T) - y(V,T)). % Constants a := 0.15. -niter := 200. +niter := 20. edgelen := 4.0. % "the unit edge length" % should `a` be negative? @@ -35,8 +35,8 @@ edge(A,B) := edge(B,A). node(U) :- true for _ is edge(U,_). node(U) :- true for _ is edge(_,U). -% pack x and y into a tuple -pos(U,T) := tuple(x(U, T), y(U, T)). +% pack x and y into a list +pos(U,T) := [x(U, T), y(U, T)]. % visualization frame(T, &text(Name, pos(Name, T))) := true for node(Name). diff --git a/examples/lists.dyna b/examples/lists.dyna deleted file mode 100644 index 5e4dd94..0000000 --- a/examples/lists.dyna +++ /dev/null @@ -1,36 +0,0 @@ -f([1,2]). - -a := [1,2]. - -goal(X) := f([1|X]). - -% structured term, sould be true -b := &f("a") in [1,2,&f("a"),3]. - -% should be true -c := 2 in [1,2,3]. - -% should be false -d := 4 in [1,2,3]. - -% simpler iteration of a complex list -things set= X for X in [1,[2,2],[3,4]]. - -% unpack structure requiring a check -d(&X) := true for [X,X] in [1,[2,2],[3,4],[4,4]]. - -% these two examples should an interesting behavior -foo(A) := true for &f(A) in [1,2,&f("a"),3]. - -% this one checks if the value of f(A) is in the list, (note: 1 == True, in python). -goo(A) := true for f(A) in [1,2,&f("a"),3]. - -thingsbag bag= "three". -thingsbag bag= 1. -thingsbag bag= 1. -thingsbag bag= 2. - -thingset set= "three". -thingset set= 1. -thingset set= 1. -thingset set= 2. \ No newline at end of file diff --git a/src/Dyna/Backend/Python/Backend.hs b/src/Dyna/Backend/Python/Backend.hs index 74419af..7d19c29 100644 --- a/src/Dyna/Backend/Python/Backend.hs +++ b/src/Dyna/Backend/Python/Backend.hs @@ -154,13 +154,10 @@ constants = go where go ("-",1) = Just $ PDBS $ call "-" [] go ("^",2) = Just $ PDBS $ infixOp "^" - go ("|",2) = Just $ PDBS $ infixOp "|" go ("-",2) = Just $ PDBS $ infixOp "-" go ("/",2) = Just $ PDBS $ infixOp "/" go ("*",2) = Just $ PDBS $ infixOp "*" go ("**",2) = Just $ PDBS $ infixOp "**" - go ("&",2) = Just $ PDBS $ infixOp "&" - go ("%",2) = Just $ PDBS $ infixOp "%" go ("+",2) = Just $ PDBS $ infixOp "+" go ("mod",2) = Just $ PDBS $ infixOp "%" @@ -168,39 +165,36 @@ constants = go go ("log",_) = Just $ PDBS $ call "log" [] go ("exp",1) = Just $ PDBS $ call "exp" [] go ("sqrt",1) = Just $ PDBS $ call "sqrt" [] - go ("getattr",_) = Just $ PDBS $ call "getattr" [] + + go ("uniform",_) = Just $ PDBS $ call "uniform" [] + go ("pycall",_) = Just $ PDBS $ call "pycall" [] go ("split",_) = Just $ PDBS $ call "split" [] go ("float",_) = Just $ PDBS $ call "float" [] go ("int",_) = Just $ PDBS $ call "int" [] - go ("pycall",_) = Just $ PDBS $ call "pycall" [] - go ("range",_) = Just $ PDBS $ call "range" [] - go ("in",2) = Just $ PDBS $ call "in_list" [] - - go ("<=",2) = Just $ PDBS $ infixOp "<=" - go ("<",2) = Just $ PDBS $ infixOp "<" - go ("=",2) = Just $ PDBS $ call "equals" [] - go ("==",2) = Just $ PDBS $ call "equals" [] - go (">=",2) = Just $ PDBS $ infixOp ">=" - go (">",2) = Just $ PDBS $ infixOp ">" - go ("!=",2) = Just $ PDBS $ infixOp "!=" + go ("<=",2) = Just $ PDBS $ call "lte" [] + go ("<",2) = Just $ PDBS $ call "lt" [] + go ("=",2) = Just $ PDBS $ call "eq" [] + go ("==",2) = Just $ PDBS $ call "eq" [] + go (">=",2) = Just $ PDBS $ call "gte" [] + go (">",2) = Just $ PDBS $ call "gt" [] + go ("!=",2) = Just $ PDBS $ call "not_eq" [] - go ("and",2) = Just $ PDBS $ infixOp "and" - go ("or",2) = Just $ PDBS $ infixOp "or" + go ("|",2) = Just $ PDBS $ call "or_" [] + go ("&",2) = Just $ PDBS $ call "and_" [] + go ("!",1) = Just $ PDBS $ call "not_" [] - go ("true",0) = Just $ PDBS $ nullary "True" - go ("false",0) = Just $ PDBS $ nullary "False" - go ("null",0) = Just $ PDBS $ nullary "None" + go ("true",0) = Just $ PDBS $ nullary "true" + go ("false",0) = Just $ PDBS $ nullary "false" + go ("null",0) = Just $ PDBS $ nullary "null" - go ("!",1) = Just $ PDBS $ call "not" [] - go ("not",1) = Just $ PDBS $ call "not" [] - - go ("eval",1) = Just $ PDBS $ call "None;exec " [] - go ("tuple",_) = Just $ PDBS $ call "" [] + --go ("eval",1) = Just $ PDBS $ call "None;exec " [] + --go ("tuple",_) = Just $ PDBS $ call "" [] + go ("in",2) = Just $ PDBS $ call "in_list" [] go ("nil",0) = Just $ PDBS $ call "build" ["nil/0"] go ("cons",2) = Just $ PDBS $ call "build" ["cons/2"] @@ -252,8 +246,8 @@ piterate vs = if length vs == 0 then "_" pdope_ :: S.Set DFunctAr -> DOpAMine PyDopeBS -> State Int (Doc e) pdope_ _ (OPIndr _ _) = dynacSorry "indirect evaluation not implemented" pdope_ _ (OPAsgn v val) = return $ pretty v <+> equals <+> pretty val -pdope_ _ (OPCheq v val) = return $ "if not equals(" <> pretty v <> ", " <> pretty val <> "): continue" -pdope_ _ (OPCkne v val) = return $ "if equals(" <> pretty v <> ", " <> pretty val <> "): continue" +pdope_ _ (OPCheq v val) = return $ "if not eq(" <> pretty v <> ", " <> pretty val <> "): continue" +pdope_ _ (OPCkne v val) = return $ "if eq(" <> pretty v <> ", " <> pretty val <> "): continue" pdope_ _ (OPPeel vs i f _) = return $ "try:" `above` (indent 4 $ tupledOrUnderscore vs diff --git a/src/Dyna/Backend/Python/Selftest.hs b/src/Dyna/Backend/Python/Selftest.hs index abd4a3f..d2f65cb 100644 --- a/src/Dyna/Backend/Python/Selftest.hs +++ b/src/Dyna/Backend/Python/Selftest.hs @@ -77,7 +77,7 @@ mkExample name = test_End_To_End :: [Test] test_End_To_End = map mkExample [ "simple", "equalities", "fib-limit", "dijkstra", "papa2", "matrixops" - , "factorial-bc", "geom", "lists", "dijkstra-backpointers" ] + , "factorial-bc", "geom", "dijkstra-backpointers" ] --test_REPL :: [Test] --test_REPL = map (\n -> testProgramRuns n ("./test/repl/"++n) []) diff --git a/src/Dyna/Backend/Python/aggregator.py b/src/Dyna/Backend/Python/aggregator.py index ee99ab3..f393bd9 100644 --- a/src/Dyna/Backend/Python/aggregator.py +++ b/src/Dyna/Backend/Python/aggregator.py @@ -7,7 +7,7 @@ from __future__ import division import operator from collections import Counter -from utils import drepr, _repr, user_vars +from utils import drepr, _repr, user_vars, isbool, true, false from errors import AggregatorError @@ -142,10 +142,16 @@ class or_equals(BAggregator): s = [x for x, m in self.iteritems() if m > 0] if len(s): for val in s: - if val is not True and val is not False: + if not isbool(val): raise TypeError('%s is not Boolean.' % _repr(val)) - return reduce(lambda x,y: x or y, s) + # TODO: can short circuit as soon as we get a true... but above we + # check the types.. so we don't get the benefit. + for val in s: + if val is true: + return true + return false + class and_equals(BAggregator): @@ -153,10 +159,16 @@ class and_equals(BAggregator): s = [x for x, m in self.iteritems() if m > 0] if len(s): for val in s: - if val is not True and val is not False: + if not isbool(val): raise TypeError('%s is not Boolean.' % _repr(val)) - return reduce(lambda x,y: x and y, s) + # TODO: can short circuit as soon as we get a false. but above we + # check the types.. so we don't get the benfit + for val in s: + if val is false: + return false + return true + class set_equals(BAggregator): def fold(self): diff --git a/src/Dyna/Backend/Python/chart.py b/src/Dyna/Backend/Python/chart.py index 8e8df05..97f4c58 100644 --- a/src/Dyna/Backend/Python/chart.py +++ b/src/Dyna/Backend/Python/chart.py @@ -1,8 +1,8 @@ from collections import defaultdict from aggregator import aggregator from term import Term -from utils import _repr -from stdlib import equals +from utils import _repr, true + class Chart(object): @@ -24,10 +24,10 @@ class Chart(object): heading = [self.name, '='*len(self.name)] # special handing or-equals aggregators -- only list true facts (and errors) - if self.agg_name == ':-' or self.agg_name == '|=': + if self.agg_name == ':-': lines = [] for term in sorted(rows): - if term.value is True: + if term.value is true: lines.append('%s.' % _repr(term)) elif term.value: # e.g. $error lines.append('%s = %s.' % (_repr(term), _repr(term.value))) @@ -80,7 +80,7 @@ class Chart(object): yield term, term.args, term.value else: for term in candidates: - if equals(term.value, val): + if term.value == val: yield term, term.args, term.value def insert(self, args): # TODO: rename diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index ea2b06e..2a3da45 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -81,7 +81,8 @@ import load, post from term import Term, Cons, Nil, MapsTo from chart import Chart from utils import ip, red, green, blue, magenta, yellow, parse_attrs, \ - ddict, dynac, read_anf, strip_comments, _repr, hide_ugly_filename + ddict, dynac, read_anf, strip_comments, _repr, hide_ugly_filename, \ + true, false from prioritydict import prioritydict from config import dotdynadir @@ -265,16 +266,15 @@ class Interpreter(object): def build(self, fn, *args): # TODO: codegen should handle true/0 is True and false/0 is False if fn == 'true/0': - return True + return true if fn == 'false/0': - return False + return false if fn == 'cons/2': return Cons(*args) if fn == 'nil/0': return Nil if fn == '->/2': return MapsTo(*args) - if fn == '$key/1': self.new_fn(fn, '=') @@ -295,7 +295,7 @@ class Interpreter(object): # remove $rule if hasattr(rule, 'item'): - self.delete_emit(rule.item, True, ruleix=None, variables=None) + self.delete_emit(rule.item, true, ruleix=None, variables=None) if rule.init is not None: # remove update handlers @@ -387,6 +387,9 @@ class Interpreter(object): # Thus, we can skip the delete-updates. self.update_dispatcher(item, was, delete=True) + + assert now is not True or now is not False # invalid dyna types. + item.value = now if now is not None: @@ -449,6 +452,7 @@ class Interpreter(object): h(*args, emit=_emit) head.value = head.aggregator.fold() + return head.value def new_query(self, fn, ruleix, handler): @@ -548,7 +552,7 @@ class Interpreter(object): if interp.agg_name[fn] is None: interp.new_fn(fn, ':=') item = interp.build(fn, ix, *a) - interp.emit(item, True, ruleix=None, variables=None, delete=False) + interp.emit(item, true, ruleix=None, variables=None, delete=False) return item for i in new_rules: r = self.rules[i] @@ -594,10 +598,10 @@ def peel(fn, item): and constants (possibly an empty tuple). """ if fn == "true/0": - assert item is True + assert item is true return if fn == "false/0": - assert item is False + assert item is false return assert isinstance(item, Term) assert item.fn == fn diff --git a/src/Dyna/Backend/Python/load/tsv.py b/src/Dyna/Backend/Python/load/tsv.py index e94d450..fd68ad3 100644 --- a/src/Dyna/Backend/Python/load/tsv.py +++ b/src/Dyna/Backend/Python/load/tsv.py @@ -4,6 +4,7 @@ TODO: option for strict number of columns. """ import re +from utils import true class tsv(object): """ @@ -37,10 +38,10 @@ class tsv(object): fn = '%s/%s' % (name, len(a)) if interp.agg_name[fn] is None: - interp.new_fn(fn, ':=') + interp.new_fn(fn, ':-') interp.emit(interp.build(fn, *a), - True, + true, ruleix=None, variables=None, delete=False) diff --git a/src/Dyna/Backend/Python/stdlib.py b/src/Dyna/Backend/Python/stdlib.py index 8327652..1b740cd 100644 --- a/src/Dyna/Backend/Python/stdlib.py +++ b/src/Dyna/Backend/Python/stdlib.py @@ -1,42 +1,76 @@ import re from term import Term, Cons, Nil, MapsTo from collections import Counter -from utils import pretty, pretty_print - - +from utils import pretty, pretty_print, true, false, null, isbool from math import log, exp, sqrt from random import random as _random + def uniform(a=0, b=1): return _random() * (b - a) + a -def equals(x,y): +def or_(x, y): + if not (isbool(x) and isbool(y)): + raise TypeError('') + return todyna(x or y) + +def and_(x, y): + if not (isbool(x) and isbool(y)): + raise TypeError('') + return todyna(x and y) + +def not_(x): + if not isbool(x): + raise TypeError('') + if x: + return false + else: + return true + +def gt(x, y): + return todyna(x > y) + +def gte(x, y): + return todyna(x >= y) + +def lt(x, y): + return todyna(x < y) + +def lte(x, y): + return todyna(x <= y) + +def eq(x,y): """ My work around for discrepency in bool equality True==1 and False==0. - >>> equals(True, 1) - False + >>> eq(true, 1) + false - >>> equals(1, 1.0) - True + >>> eq(1, 1.0) + true """ - if isinstance(x, bool) or isinstance(y, bool): - return type(x) == type(y) and x == y - else: - return x == y + return todyna(x == y) +def not_eq(x, y): + if x != y: + return true + else: + return false _range = range def range(*x): return todyna(_range(*x)) + def split(s, delim='\s+'): - return todynalist(re.split(delim, s)) + return todyna(re.split(delim, s)) + def crash(): class Crasher(Exception): pass raise Crasher('Hey, you asked for it!') + def pycall(name, *args): """ Temporary foreign function interface - call Python functions from dyna! @@ -47,22 +81,14 @@ def pycall(name, *args): def topython(x): - #if isinstance(x, AList) or x is Nil: - # return {topython(k): topython(v) for k,v in x.aslist} - if isinstance(x, Cons) or x is Nil: - return [topython(y) for y in x.aslist] - return x - -def todynalist(x): # TODO: get rid of this. - return todyna(x) + if islist(x): + return [topython(y) for y in x.aslist] -def getkey(m, k): - return m[k] + elif isinstance(x, MapsTo): + return tuple(x.args) -def setkey(m, k, v): - m[k] = v - return m + return x def todyna(x): @@ -72,11 +98,13 @@ def todyna(x): x.sort() return todyna(x) + elif x is True: + return true + + elif x is False: + return false + elif isinstance(x, dict): - #c = Nil - #for k,v in x.items(): - # c = AList(todyna([k,v]), c) - #return c return todyna([MapsTo(k,v) for k,v in x.items()]) elif isinstance(x, (list, tuple)): @@ -85,22 +113,39 @@ def todyna(x): c = Cons(todyna(y), c) return c else: + return x def get(x, i): return x[i] + +def getkey(m, k): + return m[k] + + +def setkey(m, k, v): + m[k] = v + return m + + +def islist(x): + return isinstance(x, Cons) or x is Nil + + def iter_cons(x): - if not (isinstance(x, Cons) or x is Nil): - raise TypeError("Attemping to iterate something which isn't a list.") + if not islist(x): + raise TypeError("Attemping to iterate something which isn't a list. %r" % (x,)) return x.like_chart() + def in_list(x, a): - if not (isinstance(a, Cons) or a is Nil): - raise TypeError("Attemping to iterate something which isn't a list.") + if not islist(a): + raise TypeError("Attemping to iterate something which isn't a list. %r" % (a,)) return x in a.aslist + # should probably be done with memoized backchaining... def read_lines(filename): with file(filename) as f: diff --git a/src/Dyna/Backend/Python/term.py b/src/Dyna/Backend/Python/term.py index f8b1e39..14a5cf1 100644 --- a/src/Dyna/Backend/Python/term.py +++ b/src/Dyna/Backend/Python/term.py @@ -1,5 +1,5 @@ from errors import notimplemented -from utils import _repr +from utils import _repr, true, false from aggregator import NoAggregator @@ -70,7 +70,10 @@ class Cons(Term): return '[%s]' % (', '.join(map(_repr, self.aslist))) def __contains__(self, x): - return x in self.aslist + if x in self.aslist: + return true + else: + return false def like_chart(self): for a in self.aslist: @@ -109,7 +112,7 @@ class _Nil(Term): return '[]' def __contains__(self, x): - return False + return false def like_chart(self): return iter([]) diff --git a/src/Dyna/Backend/Python/utils.py b/src/Dyna/Backend/Python/utils.py index affd0bc..b2e8130 100644 --- a/src/Dyna/Backend/Python/utils.py +++ b/src/Dyna/Backend/Python/utils.py @@ -7,11 +7,38 @@ from collections import namedtuple from cStringIO import StringIO + +class _true(object): + def __nonzero__(self): + return True + def __repr__(self): + return 'true' + +class _false(object): + def __nonzero__(self): + return False + def __repr__(self): + return 'false' + +true = _true() +false = _false() +null = None + +def isbool(x): + return x is true or x is false + + + + def _repr(x): + +# TODO: this assertion should eventually hold. +# assert x is not True and x is not False, x if x is True: return 'true' elif x is False: return 'false' + elif x is None: return 'null' elif isinstance(x, basestring): diff --git a/test/repl/list.dynadoc b/test/repl/list.dynadoc index ebdcc22..16b2b88 100644 --- a/test/repl/list.dynadoc +++ b/test/repl/list.dynadoc @@ -83,24 +83,32 @@ Changes d(2) = true. d(4) = true. -% quote is important! or else we enumerate everything! -> foo(A) := true for &bar(A) in [1,2,&bar("a"),3]. -| -| bar("int") = 1. + +% let's look at a the difference between Booleans and integers +> bar("int") = 1. | bar("bool") = true. -| bar("spaz") = 2. -| -| goo(A) := true for bar(A) in [true,2,&bar("a"),3]. +| bar("two") = 2. Changes ======= bar("bool") = true. bar("int") = 1. -bar("spaz") = 2. +bar("two") = 2. + +% quote is important! or else we enumerate everything! +> foo(A) := true for &bar(A) in [1,2,&bar("a"),3]. + +Changes +======= foo("a") = true. + +% if you don't quote bar(A) drives the querym checking membership in the list. +> goo(A) := true for bar(A) in [true,2,&bar("a")]. + +Changes +======= goo("bool") = true. -goo("int") = true. % TODO: this should't appear in the results (XREF:bool) -goo("spaz") = true. +goo("two") = true. % unfortunately 1 == true and 0 == false in python so the following is true @@ -108,7 +116,7 @@ goo("spaz") = true. Changes ======= -testbool = true. % TODO: XREF:bool +testbool = false. % fun with set= at bag= @@ -126,5 +134,5 @@ testbool = true. % TODO: XREF:bool Changes ======= -thingsbag = [1, 1, 1, 2, "three"]. -thingset = [1, 2, "three"]. +thingsbag = [1, 1, 2, "three", true]. +thingset = [1, 2, true, "three"].