From 926c269f4764745d54522dbedd7f62079cde4536 Mon Sep 17 00:00:00 2001 From: Tim Vieira Date: Mon, 1 Jul 2013 17:53:00 -0400 Subject: [PATCH] added infix 'in' operator for "iterating" the elements of a list (i.e. `a += x for X in [1,2,3].` with `X` free and the list bound.) and check if the item exists in the list. --- examples/expected/lists.py.out | 17 +++++++++++++++++ examples/lists.dyna | 21 +++++++++++++++++++++ src/Dyna/Backend/Python/Backend.hs | 18 ++++++++---------- src/Dyna/Backend/Python/interpreter.py | 24 +++++++++++++----------- src/Dyna/Backend/Python/term.py | 15 +++++++++------ src/Dyna/Term/SurfaceSyntax.hs | 2 ++ 6 files changed, 70 insertions(+), 27 deletions(-) diff --git a/examples/expected/lists.py.out b/examples/expected/lists.py.out index a11297d..98ad10a 100644 --- a/examples/expected/lists.py.out +++ b/examples/expected/lists.py.out @@ -2,12 +2,29 @@ Solution ======== a = [1, 2]. +b = true. +c = true. +d = false. +things = [1, [2, 2], [3, 4]]. + +d/1 +=== +d(2) = true. +d(4) = true. f/1 === f([1, 2]) = true. +foo/1 +===== +foo("a") = true. + goal/1 ====== goal([2]) = true. +goo/1 +===== +goo([1, 2]) = true. + diff --git a/examples/lists.dyna b/examples/lists.dyna index c1bb6e3..9080c63 100644 --- a/examples/lists.dyna +++ b/examples/lists.dyna @@ -3,3 +3,24 @@ 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]. diff --git a/src/Dyna/Backend/Python/Backend.hs b/src/Dyna/Backend/Python/Backend.hs index d5e2f9b..dd0be56 100644 --- a/src/Dyna/Backend/Python/Backend.hs +++ b/src/Dyna/Backend/Python/Backend.hs @@ -120,7 +120,6 @@ builtins (f,is,o) = case () of [(x^.mv_var, nuniv)] _ -> Left True - {- -- XXX Argh, same as above. _ | f == "in" -> case is of @@ -140,7 +139,6 @@ builtins (f,is,o) = case () of cmod else Left True _ -> Left True - -} _ | MA.isJust (constants (f,length is)) -> Left True _ -> Left False @@ -169,16 +167,16 @@ constants = go go ("abs",1) = Just $ PDBS $ call "abs" [] 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 ("sqrt",1) = Just $ PDBS $ call "sqrt" [] + go ("getattr",_) = Just $ PDBS $ call "getattr" [] + go ("uniform",_) = Just $ PDBS $ call "uniform" [] - 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 ("split",_) = Just $ PDBS $ call "split" [] + go ("float",_) = Just $ PDBS $ call "float" [] + go ("int",_) = Just $ PDBS $ call "int" [] + go ("pycall",_) = Just $ PDBS $ call "pycall" [] --- go ("in",2) = Just $ PDBS $ call " in " [] + go ("in",2) = Just $ PDBS $ infixOp " in " go ("<=",2) = Just $ PDBS $ infixOp "<=" go ("<",2) = Just $ PDBS $ infixOp "<" diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index d9af1dd..e079195 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -7,8 +7,6 @@ TODO - More info in crash handler. (stack trace, repl transcript, cmd-line args, version control info, and dyna source is enough) - - hooks to call a crash script - - dyna syntax which just gets passed to the backend: - running repl commands, loaders, post-procesors @@ -88,7 +86,7 @@ STRONGER (robustness) USERS ===== - - user-defined priorities (blocked: back-chaining) + - user-defined priorities - Catch typos! Warn the user if they write a predicate that is not defined on the LHS of a rule and it's not quoted (i.e. not some new piece of structure). @@ -266,7 +264,7 @@ class Interpreter(object): print >> out, 'because %r is %s:' % (item, _repr(val)) for e, h in es: if h is not None: - r = h.dyna_rule + r = h.rule print >> out, ' %s\n in rule %s\n %s' % (e, r.span, r.src) print >> out @@ -287,19 +285,14 @@ class Interpreter(object): return True if fn == 'false/0': return False - if fn == 'cons/2': return Cons(*args) if fn == 'nil/0': return Nil - - - # FIXME: if fn not in self.agg_name: # item has no aggregator (e.g purely structural stuff) -- what # happens if we add one later? self.new_fn(fn, None) - return self.chart[fn].insert(args) # def retract_item(self, item): @@ -342,14 +335,17 @@ class Interpreter(object): agenda = self.agenda error = self.error while agenda: + item = agenda.pop_smallest() was = item.value + try: now = item.aggregator.fold() + except AggregatorError as e: error[item] = ('failed to aggregate item `%r` because %s' % (item, e), [(e, None)]) - now = self.build('$error/0') + now = self.build('$error/0') # XXX: should go an agenda or run delete? changed[item] = now item.value = now continue @@ -357,27 +353,33 @@ class Interpreter(object): except (ZeroDivisionError, TypeError, KeyboardInterrupt, NotImplementedError) as e: error[item] = ('failed to aggregate %r' % item.aggregator, [(e, None)]) - now = self.build('$error/0') + now = self.build('$error/0') # XXX: should go an agenda or run delete? changed[item] = now item.value = now continue if was == now: continue + was_error = False if item in error: # clear error was_error = True del error[item] + # TODO: handle `was` and `now` at the same time to avoid the two passes. # TODO: will need to propagate was=None when we have question mark if was is not None and not was_error: # if `was` is marked as an error we know it didn't propagate. # Thus, we can skip the delete-updates. self.update_dispatcher(item, was, delete=True) + item.value = now + if now is not None: self.update_dispatcher(item, now, delete=False) + changed[item] = now + return changed def update_dispatcher(self, item, val, delete): diff --git a/src/Dyna/Backend/Python/term.py b/src/Dyna/Backend/Python/term.py index 9d093dc..60f54e4 100644 --- a/src/Dyna/Backend/Python/term.py +++ b/src/Dyna/Backend/Python/term.py @@ -61,12 +61,15 @@ class Cons(Term): def __repr__(self): return '[%s]' % (', '.join(map(_repr, self.aslist))) -# def __iter__(self): -# for a in self.aslist: -# if not isinstance(a, Term): -# yield a, (None,), a -# else: -# yield a, (None,), a + def __contains__(self, x): + return x in self.aslist + + def __iter__(self): + for a in self.aslist: + if not isinstance(a, Term): + yield a, (None,), a + else: + yield a, (None,), a def __eq__(self, other): try: diff --git a/src/Dyna/Term/SurfaceSyntax.hs b/src/Dyna/Term/SurfaceSyntax.hs index be4155a..1d5a3b2 100644 --- a/src/Dyna/Term/SurfaceSyntax.hs +++ b/src/Dyna/Term/SurfaceSyntax.hs @@ -77,6 +77,8 @@ defOperSpec = foldr (\(k,v) -> mapInOrCons k v) def more , ("%" ,[(7,PFIn AssocLeft ) ]) , ("+" ,[(6,PFIn AssocLeft ) ]) + , ("in" ,[(4,PFIn AssocNone ) ]) + , ("<=" ,[(4,PFIn AssocNone ) ]) , ("<" ,[(4,PFIn AssocNone ) ]) , ("=" ,[(4,PFIn AssocNone ) ]) -- 2.50.1