From: Tim Vieira Date: Tue, 9 Jul 2013 19:55:22 +0000 (-0400) Subject: small improvements to handling booleans X-Git-Url: https://hydra-www.ietfng.org/gitweb/?a=commitdiff_plain;h=c11d61ca35a8e405a7057e25a01a859fa500e993;p=dyna2 small improvements to handling booleans clean up aggregators crash handler hook (write repl lines) hide ugly file names in error messages more test cases added coverage target to makefile added a few more test cases. --- diff --git a/Makefile b/Makefile index 0b1cd38..4048471 100644 --- a/Makefile +++ b/Makefile @@ -31,12 +31,12 @@ fcomp: .PHONY: clean veryclean clean: rm -rf examples/*.dyna.*.plan \ - examples/*.dyna.*.planc \ - examples/*.dyna.plan.py \ - examples/*.dyna.plan.pyc \ - examples/*.dyna.*.out \ - examples/*.dyna.d \ - examples/*.hist + examples/*.dyna.*.planc \ + examples/*.dyna.plan.py \ + examples/*.dyna.plan.pyc \ + examples/*.dyna.*.out \ + examples/*.dyna.d \ + examples/*.hist rm -rf test/*/*.out rm -f tags TAGS veryclean: clean @@ -80,7 +80,6 @@ ghcbuild: -o dist/build/dyna/dyna \ -outputdir dist/build/dyna/dyna-tmp \ -main-is Dyna.Main.Driver Dyna.Main.Driver - mkdir -p dist/build/dyna-selftests mkdir -p dist/build/dyna-selftests/dyna-selftests-tmp ghc --make -isrc \ @@ -105,3 +104,8 @@ profbuild: .PHONY: tags TAGS tags TAGS: hasktags -b src + +coverage: + (coverage run run-doctests.py \ + ; coverage html --include 'src/*' -d coverage-report \ + ; gnome-open coverage-report/index.html) diff --git a/src/Dyna/Backend/Python/Backend.hs b/src/Dyna/Backend/Python/Backend.hs index d6423eb..9bef418 100644 --- a/src/Dyna/Backend/Python/Backend.hs +++ b/src/Dyna/Backend/Python/Backend.hs @@ -51,7 +51,7 @@ aggrs :: S.Set String aggrs = S.fromList [ "max=" , "min=" , "+=" , "*=" - , "and=" , "or=" , "&=" , "|=" + , "&=" , "|=" , ":-" , "=" , "majority=" , "mean=" @@ -182,8 +182,8 @@ constants = go go ("<=",2) = Just $ PDBS $ infixOp "<=" go ("<",2) = Just $ PDBS $ infixOp "<" - 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 "!=" @@ -252,10 +252,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" <+> pretty v <+> "!=" - <+> pretty val <> ": continue" -pdope_ _ (OPCkne v val) = return $ "if" <+> pretty v <+> "==" - <+> pretty val <> ": continue" +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_ _ (OPPeel vs i f _) = return $ "try:" `above` (indent 4 $ tupledOrUnderscore vs diff --git a/src/Dyna/Backend/Python/aggregator.py b/src/Dyna/Backend/Python/aggregator.py index 3a5ca30..fd3f76b 100644 --- a/src/Dyna/Backend/Python/aggregator.py +++ b/src/Dyna/Backend/Python/aggregator.py @@ -148,25 +148,25 @@ class min_equals(BAggregator): if len(s): return min(s) -class maxwithkey_equals(max_equals): - def fold(self): - m = max_equals.fold(self) - self.key = None - if m is not None: - if not hasattr(m, 'aslist') or len(m.aslist) != 2: - raise AggregatorError("argmax expects a pair of values") - self.key = m.aslist[1] - return m.aslist[0] - -class minwithkey_equals(min_equals): - def fold(self): - m = min_equals.fold(self) - self.key = None - if m is not None: - if not hasattr(m, 'aslist') or len(m.aslist) != 2: - raise AggregatorError("argmin expects a pair of values") - self.key = m.aslist[1] - return m.aslist[0] +#class maxwithkey_equals(max_equals): +# def fold(self): +# m = max_equals.fold(self) +# self.key = None +# if m is not None: +# if not hasattr(m, 'aslist') or len(m.aslist) != 2: +# raise AggregatorError("argmax expects a pair of values") +# self.key = m.aslist[1] +# return m.aslist[0] + +#class minwithkey_equals(min_equals): +# def fold(self): +# m = min_equals.fold(self) +# self.key = None +# if m is not None: +# if not hasattr(m, 'aslist') or len(m.aslist) != 2: +# raise AggregatorError("argmin expects a pair of values") +# self.key = m.aslist[1] +# return m.aslist[0] class plus_equals(BAggregator): @@ -181,18 +181,17 @@ class times_equals(BAggregator): if len(s): return reduce(operator.mul, s) -class and_equals(BAggregator): - def fold(self): - s = [k for k, m in self.iteritems() if m > 0] - if len(s): - return reduce(lambda x,y: x and y, s) - -class or_equals(BAggregator): - def fold(self): - s = [k for k, m in self.iteritems() if m > 0] - if len(s): - return reduce(lambda x,y: x or y, s) +#class and_equals(BAggregator): +# def fold(self): +# s = [k for k, m in self.iteritems() if m > 0] +# if len(s): +# return reduce(lambda x,y: x and y, s) +#class or_equals(BAggregator): +# def fold(self): +# s = [k for k, m in self.iteritems() if m > 0] +# if len(s): +# return reduce(lambda x,y: x or y, s) class boolean_or_equals(BAggregator): def fold(self): @@ -232,8 +231,6 @@ defs = { 'min=': min_equals, '+=': plus_equals, '*=': times_equals, - 'and=': and_equals, - 'or=': or_equals, '&=': boolean_and_equals, '|=': boolean_or_equals, ':-': boolean_or_equals, diff --git a/src/Dyna/Backend/Python/chart.py b/src/Dyna/Backend/Python/chart.py index a2b9c2b..8e8df05 100644 --- a/src/Dyna/Backend/Python/chart.py +++ b/src/Dyna/Backend/Python/chart.py @@ -2,7 +2,7 @@ from collections import defaultdict from aggregator import aggregator from term import Term from utils import _repr - +from stdlib import equals class Chart(object): @@ -80,7 +80,7 @@ class Chart(object): yield term, term.args, term.value else: for term in candidates: - if term.value == val: + if equals(term.value, val): yield term, term.args, term.value def insert(self, args): # TODO: rename diff --git a/src/Dyna/Backend/Python/errors.py b/src/Dyna/Backend/Python/errors.py index 7d31fb4..b775d1b 100644 --- a/src/Dyna/Backend/Python/errors.py +++ b/src/Dyna/Backend/Python/errors.py @@ -5,7 +5,9 @@ from config import dotdynadir class DynaCompilerError(Exception): - pass + def __init__(self, msg, filename): + self.filename = filename + super(DynaCompilerError, self).__init__(msg) class AggregatorError(Exception): @@ -45,8 +47,8 @@ def exception_handler(etype, evalue, tb): # chart -- because it might be too big to email); input to repl. # This should all go into a tarball. - if crash_handler.interp is not None: - crash_handler.interp() + for hook in crash_handler.hooks: + hook() print 'FATAL ERROR (%s): %s' % (etype.__name__, evalue) print 'Crash log available %s' % crashreport.name @@ -58,8 +60,8 @@ def crash_handler(): """ sys.excepthook = exception_handler -# XXX: global state... -crash_handler.interp = None + +crash_handler.hooks = [] def show_traceback(einfo=None): diff --git a/src/Dyna/Backend/Python/interpreter.py b/src/Dyna/Backend/Python/interpreter.py index e14fe4d..7f65a2c 100644 --- a/src/Dyna/Backend/Python/interpreter.py +++ b/src/Dyna/Backend/Python/interpreter.py @@ -116,7 +116,7 @@ import load, post from term import Term, Cons, Nil from chart import Chart from utils import ip, red, green, blue, magenta, yellow, parse_attrs, \ - ddict, dynac, read_anf, strip_comments, _repr + ddict, dynac, read_anf, strip_comments, _repr, hide_ugly_filename from prioritydict import prioritydict from config import dotdynadir @@ -132,7 +132,8 @@ class Rule(object): self.query = None @property def span(self): - return parse_attrs(self.init or self.query)['Span'] + span = parse_attrs(self.init or self.query)['Span'] + return hide_ugly_filename(span) @property def src(self): return strip_comments(parse_attrs(self.init or self.query)['rule']) @@ -254,7 +255,7 @@ class Interpreter(object): E[h.rule][type(e)].append((e, item, val)) # aggregation errors - for r in I: + for r in sorted(I, key=lambda r: r.index): print >> out, 'Error(s) aggregating %s:' % r for etype in I[r]: print >> out, ' %s:' % etype.__name__ @@ -266,7 +267,7 @@ class Interpreter(object): print >> out # errors pertaining to rules - for r in E: + for r in sorted(E, key=lambda r: r.index): print >> out, 'Error(s) in rule:', r.span print >> out for line in r.src.split('\n'): @@ -505,10 +506,6 @@ class Interpreter(object): item.aggregator.inc(val, ruleix, variables) self.agenda[item] = time() # FIFO - def repl(self): - import repl - repl.REPL(self).cmdloop() - def do(self, filename, initialize=True): """ Compile, load, and execute new dyna rules. @@ -595,12 +592,10 @@ class Interpreter(object): def dynac(self, filename): filename = path(filename) self.files.append(filename) - out = self.tmp / filename.read_hexhash('sha1') + '.plan.py' # out = filename + '.plan.py' - - dynac(filename, out) self.files.append(out) + dynac(filename, out) return out def dynac_code(self, code): @@ -675,9 +670,6 @@ def main(): args.source.copy(plan) else: - #plan = args.source + '.plan.py' - #interp.dynac(args.source, plan) - try: plan = interp.dynac(args.source) except DynaCompilerError as e: @@ -718,7 +710,18 @@ def main(): interp.dump_charts(args.output) # should be a post-processor if args.interactive or not args.source: - interp.repl() + from repl import REPL + repl = REPL(interp) + + def repl_crash(): + # all files the interpreter generated + with file(dotdynadir / 'crash-repl.log', 'wb') as f: + for line in repl.lines: + print >> f, line + + crash_handler.hooks.append(repl_crash) + + repl.cmdloop() if __name__ == '__main__': diff --git a/src/Dyna/Backend/Python/load/sexpr.py b/src/Dyna/Backend/Python/load/sexpr.py index 358e9b8..c02222b 100644 --- a/src/Dyna/Backend/Python/load/sexpr.py +++ b/src/Dyna/Backend/Python/load/sexpr.py @@ -45,31 +45,3 @@ class sexpr(object): variables=None, delete=False) - -# TODO: maybe really big terms should have a pretty printer -def pretty(t, initialindent=0): - "Pretty print tree as a tabbified s-expression." - f = StringIO() - out = f.write - def pp(t, indent=initialindent, indentme=True): - if indentme: - out(' '*indent) - if isinstance(t, basestring): # base case - return out('"%s"' % t) - if len(t) == 1: - if t[0]: - pp('"%s"' % t[0], indent, indentme) - return - label, children = t[0], t[1:] - label = '"%s"' % label - assert isinstance(label, basestring) - out('&t(%s, ' % label) - n = len(children) - for i, child in enumerate(children): - pp(child, indent + len(label) + 5, i != 0) # first child already indented - if i != n-1: # no newline after last child - out(',\n') - out(')') - pp(t) - out('\n') - return f.getvalue() diff --git a/src/Dyna/Backend/Python/repl.py b/src/Dyna/Backend/Python/repl.py index 6133c45..c3c19df 100644 --- a/src/Dyna/Backend/Python/repl.py +++ b/src/Dyna/Backend/Python/repl.py @@ -8,7 +8,7 @@ to help. """ import os, cmd, readline -from utils import dynac, ip, lexer, subst, drepr, _repr, get_module +from utils import ip, lexer, subst, drepr, _repr, get_module from stdlib import topython, todyna from errors import DynaCompilerError, DynaInitializerException from config import dotdynadir @@ -29,6 +29,7 @@ class REPL(cmd.Cmd, object): f.write('') readline.read_history_file(hist) self.lineno = 0 + self.lines = [] # create help routines based on doc string. for x, v in REPL.__dict__.iteritems(): @@ -111,6 +112,7 @@ class REPL(cmd.Cmd, object): been interpreted. If you want to modify the input line before execution (for example, variable substitution) do it here. """ + self.lines.append(line) return line def postcmd(self, stop, line): @@ -127,11 +129,11 @@ class REPL(cmd.Cmd, object): """Do nothing on empty input line""" pass -# def do_ip(self, _): -# """ -# Development tool. Jump into an interactive python shell. -# """ -# ip() + def do_ip(self, _): + """ + Development tool. Jump into an interactive python shell. + """ + ip() def do_debug(self, line): """ @@ -142,19 +144,15 @@ class REPL(cmd.Cmd, object): f.write(line) debug.main(f.name) -# def do_run(self, filename): -# """ -# Load dyna rules from `filename`. -# -# > run examples/papa.dyna -# -# """ -# try: -# changed = self.interp.do(self.interp.dynac(filename)) -# except DynaCompilerError as e: -# print e -# else: -# self._changed(changed) + def do_dynac(self, line): + try: + src = self.interp.dynac_code(line) # might raise DynaCompilerError + except DynaCompilerError as e: + src = e.filename + print e + finally: + print 'opening file %s' % src + os.system('emacs -nw %s' % src) def _query(self, q): @@ -261,7 +259,7 @@ class REPL(cmd.Cmd, object): print "ERROR: Line doesn't end with period." return try: - src = self.interp.dynac_code(line) # might raise DynaCompilerError + src = self.interp.dynac_code(line + ' %% repl line %s' % self.lineno) changed = self.interp.do(src) except (DynaInitializerException, DynaCompilerError) as e: @@ -289,22 +287,6 @@ class REPL(cmd.Cmd, object): print '%s = %s.' % (x, _repr(x.value)) print -# def _changed_subscriptions(self, changed): -# -# # TODO: this doesn't show changes - it redumps everything. -# -# if not changed: -# return -# for x, _ in sorted(changed.items()): -# if x.fn == '$subscribed/2': -# [i, q] = x.args -# if x.value: -# print '%s: %s' % (i, q) -# for result in x.value: -# print ' ', _repr(result.value), 'where', drepr(dict(result.variables)) -# print -# self.interp.dump_errors() - def cmdloop(self, _=None): try: super(REPL, self).cmdloop() @@ -353,6 +335,22 @@ class REPL(cmd.Cmd, object): # print ' ', _repr(result.value), 'where', drepr(dict(result.variables)) # print +# def _changed_subscriptions(self, changed): +# +# # TODO: this doesn't show changes - it redumps everything. +# +# if not changed: +# return +# for x, _ in sorted(changed.items()): +# if x.fn == '$subscribed/2': +# [i, q] = x.args +# if x.value: +# print '%s: %s' % (i, q) +# for result in x.value: +# print ' ', _repr(result.value), 'where', drepr(dict(result.variables)) +# print +# self.interp.dump_errors() + def do_help(self, line): mod = line.split() if len(mod) <= 1: diff --git a/src/Dyna/Backend/Python/stdlib.py b/src/Dyna/Backend/Python/stdlib.py index 5e8e922..921d043 100644 --- a/src/Dyna/Backend/Python/stdlib.py +++ b/src/Dyna/Backend/Python/stdlib.py @@ -1,6 +1,7 @@ import re from term import Term, Cons, Nil from collections import Counter +from utils import pretty, pretty_print try: from numpy import log, exp, sqrt @@ -12,6 +13,22 @@ except ImportError: # XXX: should probably issue a warning return _random() * (b - a) + a +def equals(x,y): + """ + My work around for discrepency in bool equality True==1 and False==0. + + >>> equals(True, 1) + False + + >>> equals(1, 1.0) + True + """ + if isinstance(x, bool) or isinstance(y, bool): + return type(x) == type(y) and x == y + else: + return x == y + + _range = range def range(*x): return todyna(_range(*x)) @@ -20,8 +37,7 @@ def split(s, delim='\s+'): return todynalist(re.split(delim, s)) def crash(): - class Crasher(Exception): - pass + class Crasher(Exception): pass raise Crasher('Hey, you asked for it!') def pycall(name, *args): diff --git a/src/Dyna/Backend/Python/term.py b/src/Dyna/Backend/Python/term.py index 0b95982..47c0df2 100644 --- a/src/Dyna/Backend/Python/term.py +++ b/src/Dyna/Backend/Python/term.py @@ -35,23 +35,23 @@ class Term(object): return fn return '%s(%s)' % (fn, ','.join(map(_repr, self.args))) - def __getstate__(self): - return (self.fn, self.args, self.value, self.aggregator) +# def __getstate__(self): +# return (self.fn, self.args, self.value, self.aggregator) - def __setstate__(self, state): - (self.fn, self.args, self.value, self.aggregator) = state +# def __setstate__(self, state): +# (self.fn, self.args, self.value, self.aggregator) = state - def __add__(self, _): - raise TypeError("Can't subtract terms.") +# def __add__(self, _): +# raise TypeError("Can't subtract terms.") - def __sub__(self, _): - raise TypeError("Can't add terms.") +# def __sub__(self, _): +# raise TypeError("Can't add terms.") - def __mul__(self, _): - raise TypeError("Can't multiply terms.") +# def __mul__(self, _): +# raise TypeError("Can't multiply terms.") - def __div__(self, _): - raise TypeError("Can't divide terms.") +# def __div__(self, _): +# raise TypeError("Can't divide terms.") class Cons(Term): @@ -90,11 +90,11 @@ class Cons(Term): def __hash__(self): return hash(tuple(self.aslist)) - def __cmp__(self, other): - try: - return cmp(self.aslist, other.aslist) - except AttributeError: - return 1 +# def __cmp__(self, other): +# try: +# return cmp(self.aslist, other.aslist) +# except AttributeError: +# return class _Nil(Term): @@ -121,6 +121,12 @@ class _Nil(Term): except AttributeError: return False +# def __cmp__(self, other): +# try: +# return cmp(self.aslist, other.aslist) +# except AttributeError: +# return 1 + Nil = _Nil() diff --git a/src/Dyna/Backend/Python/utils.py b/src/Dyna/Backend/Python/utils.py index 1cef4f7..affd0bc 100644 --- a/src/Dyna/Backend/Python/utils.py +++ b/src/Dyna/Backend/Python/utils.py @@ -4,6 +4,7 @@ from path import path from subprocess import Popen, PIPE from config import dynahome, dotdynadir from collections import namedtuple +from cStringIO import StringIO def _repr(x): @@ -80,10 +81,13 @@ def dynac(f, out, anf=None, compiler_args=()): stdout, stderr = p.communicate() if p.returncode: assert not stdout.strip(), [stdout, stderr] - # hide our temporary file's ugly sha1 file names from users. - ugly_file_name = dotdynadir + '[a-z0-9/.]+\.dyna\S*' - stderr = re.sub(ugly_file_name, '', stderr) - raise DynaCompilerError(stderr) + stderr = hide_ugly_filename(stderr) + raise DynaCompilerError(stderr, f) + + +def hide_ugly_filename(x, replacement=''): + p = dotdynadir + '[a-z0-9/.]+\.dyna\S*' + return re.sub(p, replacement, x) def lexer(term): @@ -175,6 +179,37 @@ def parse_sexpr(e): return es +def pretty_print(t): + print pretty(t) + +def pretty(t, initialindent=0): + "Pretty print tree as a tabbified s-expression." + f = StringIO() + out = f.write + def pp(t, indent=initialindent, indentme=True): + if indentme: + out(' '*indent) + if isinstance(t, basestring): # base case + return out('%s' % t) + if len(t) == 1: + if t[0]: + pp('%s' % t[0], indent, indentme) + return + label, children = t[0], t[1:] + label = '%s' % label + assert isinstance(label, basestring) + out('(%s ' % label) + n = len(children) + for i, child in enumerate(children): + pp(child, indent + len(label) + 2, i != 0) # first child already indented + if i != n-1: # no newline after last child + out('\n') + out(')') + pp(t) + out('\n') + return f.getvalue() + + class ANF(namedtuple('ANF', 'lines ruleix agg head evals unifs result')): pass diff --git a/test/app/ptb.dynadoc b/test/app/ptb.dynadoc index 30cd48c..40d17a3 100644 --- a/test/app/ptb.dynadoc +++ b/test/app/ptb.dynadoc @@ -152,3 +152,5 @@ ntrees = 23. > query errors(S) errors(12) = ["(ROOT (S (NP Laura) (VP (VP (V (V say) -s) (SBAR that (S (NP George) (VP (Modal might) (VP (V sleep)))))) (PP (P on) (NP (Det the) (N floor))))) !)", "(ROOT (S (NP Laura) (VP (V (V say) -s) (SBAR that (S (NP George) (VP (VP (Modal might) (VP (V sleep))) (PP (P on) (NP (Det the) (N floor)))))))) !)"]. errors(21) = ["(ROOT (S (NP (NP (Det the) (N (Adj (Adj fine) (@Adj and (Adj blue))) (N woman))) (@NP and (NP (Det every) (N man)))) (VP (VP (Modal must) (VP (V have) (VP (V (V eat) -ed) (NP (Det two) (N (N sandwich) -s))))) (@VP and (VP (VP (V (V sleep) -ed)) (PP (P on) (NP (Det the) (N floor))))))) .)", "(ROOT (S (NP (NP (Det the) (N (Adj (Adj fine) (@Adj and (Adj blue))) (N woman))) (@NP and (NP (Det every) (N man)))) (VP (VP (Modal must) (VP (V have) (VP (VP (V (V eat) -ed) (NP (Det two) (N (N sandwich) -s))) (@VP and (VP (V (V sleep) -ed)))))) (PP (P on) (NP (Det the) (N floor))))) .)"]. + +%> *resume* \ No newline at end of file diff --git a/test/repl/boolean-aggregators.dynadoc b/test/repl/boolean-aggregators.dynadoc index 4c98d5f..fbfc66c 100644 --- a/test/repl/boolean-aggregators.dynadoc +++ b/test/repl/boolean-aggregators.dynadoc @@ -72,6 +72,12 @@ Changes b = true. c = true. + + +> f(1,2). f(2,2). + +*ignore* + > sol Solution @@ -79,3 +85,8 @@ Solution a. b = true. c = true. + +f/2 +=== +f(1,2). +f(2,2). \ No newline at end of file diff --git a/test/repl/equals-errors.dynadoc b/test/repl/equals-errors.dynadoc index adaa7e8..3da43e5 100644 --- a/test/repl/equals-errors.dynadoc +++ b/test/repl/equals-errors.dynadoc @@ -1,36 +1,41 @@ -:- a = 2. -============= -a := 2 +> a = 2. +Changes +======= +a = 2. % It's ok to assign 2 again. - -:- a = 2. - +> a = 2. % but you can't set it to a different value, such as 1. +> a = 1. -:- a = 1. -============= -a := $error +Changes +======= +a = $error. -:- sol +> sol Solution ======== -a => $error. - +a = $error. Errors ====== -because a is "failed to aggregate item `a` because `=` got conflicting values [1, 2]": +Error(s) aggregating a/0: + AggregatorError: + `a`: `=` got conflicting values [1, 2] + +> retract_rule 2 -:- retract_rule 2 +Changes +======= +a = 2. -:- sol +> sol Solution ======== -a => 2. +a = 2. diff --git a/test/repl/list.dynadoc b/test/repl/list.dynadoc new file mode 100644 index 0000000..fe8b061 --- /dev/null +++ b/test/repl/list.dynadoc @@ -0,0 +1,124 @@ +> x = cons(1, 2). + +DynaInitializerException: +TypeError('Malformed list',) in ininitializer for rule + x = cons(1, 2). +new rule(s) were not added to program. + + +> s set= Y for Y in [3,2,1,[2,1],&f(1)]. + +Changes +======= +s = [1, 2, 3, [2, 1], f(1)]. + +> foo = 1 in s. + +Changes +======= +foo = true. + +% check comparison (sort order) of list and non-list. +> f(s). f(1). f([]). + +Changes +======= +f(1) = true. +f([1, 2, 3, [2, 1], f(1)]) = true. +f([]) = true. + +% test for empty list +> query 1 in nil + +1 in nil = false. + +> nothing set= X for X in []. + + + + +> g([1,2]). + +Changes +======= +g([1, 2]) = true. + +> a := [1,2]. + +Changes +======= +a = [1, 2]. + +> goal(X) := g([1|X]). + +Changes +======= +goal([2]) = true. + + +% list contains +> b := &f("a") in [1,2,&f("a"),3]. +| c := 2 in [1,2,3]. +| d := 4 in [1,2,3]. + +Changes +======= +b = true. +c = true. +d = false. + + +% iteration of a complex list +> things set= X for X in [1,[2,2],[3,4]]. + +Changes +======= +things = [1, [2, 2], [3, 4]]. + +% unpack structure requiring a check +> d(&X) := true for [X,X] in [1,[2,2],[3,4],[4,4]]. + +Changes +======= +d(2) = true. +d(4) = true. + +% quote is important! or else we enumerate everything! +> 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]. + + +Changes +======= +foo("a") = true. +goo(1) = true. +goo([1, 2, 3, [2, 1], f(1)]) = true. +goo([]) = true. + + +% unfortunately 1 == true and 0 == false in python so the following is true +> testbool := true in [1,2]. + +Changes +======= +testbool = true. + + +% fun with set= at bag= +> thingsbag bag= "three". +| thingsbag bag= 1. +| thingsbag bag= 1. +| thingsbag bag= 2. +| +| thingset set= "three". +| thingset set= 1. +| thingset set= 1. +| thingset set= 2. + +Changes +======= +thingsbag = [1, 1, 2, "three"]. +thingset = [1, 2, "three"]. + diff --git a/test/repl/trace.dynadoc b/test/repl/trace.dynadoc index 4444362..d203024 100644 --- a/test/repl/trace.dynadoc +++ b/test/repl/trace.dynadoc @@ -97,3 +97,17 @@ bar(10,10) = 220 └─ foo(10) = 11 | └─ continue as before (shared structure) + + +> x = [1,2,3]. + +Changes +======= +x = [1, 2, 3]. + +> trace x + +x = [1, 2, 3] +| +└─ = [1, 2, 3] + x = [1, 2, 3]. \ No newline at end of file