def add_handler(f): arg_names = inspect.getargspec(f).args[self.undispatched_args+unextracted_args:] #+1 for the asker self.handlers[filters][names].append((f, arg_names, extractions)) if sum(extractions) == 1: extracted = [name for name, extract in zip(names, extractions) if extract] return term.simple(extracted[0], *arg_names) else: def throw(*args, **kwargs): raise Exception("Dispatcher(f) only returns a function if there " "is exactly one extracted argument.") return throw
return result def set_repr(self, v, new_repr): self.update(updates.trivial(), v, repr_change=updates.become(new_repr)) def reply(self, *args, **kwargs): reply = super(ContextUpdater, self).reply(*args, **kwargs) if self.Q is None: return reply responses = [] for internal in self.internal.values(): if self.changed[internal]: update = self.updates[internal] source = self.source[internal] responses.append(context_update( source, update, representations.quote(self.current[internal]) )) return reply.add(properties.combine(responses)) class UntaggedUpdateError(ValueError): pass context_update = term.simple( "the value of [source] at the referenced question should be updated by applying [update], " "and the result should be represented as [repr]", "source", "update", "repr" ) in_question = term.simple("the object referred to as [s] in the referenced question", "s")
from frozendict import frozendict import functions import lists import termtypes from termtypes import is_type from ipdb import set_trace as debug cons = T.dict_cons empty = T.empty_dict dict_type = termtypes.new_type("the type of dictionaries") termtypes.set_types(dict_type, cons, empty) image = term.simple("the function that maps a dictionary to the image of [k] in that dictionary", "k") @getter(image.head, cons.head) def cons_image(asker, k, key, value, other): if booleans.ask_firmly(asker, builtins.equal(k, key)): return asker.reply(answer=value) else: return asker.ask_tail( fields.get_field(image(k), other), handler=asker.pass_through(not_found.head) ) @getter(image.head, empty.head) def image_empty(asker, k): return asker.reply(value=not_found())
from ipdb import set_trace as debug import term from term import as_head, Term as T import database import askers from dispatch import Dispatcher import handlers import properties from builtins import builtin import fields import relayer import updates import context state = term.simple("a state of the interpeter where [history] maps " "strings to the user's historical response to those strings, " "and where [computation] is invoked to handle computations", 'history', 'computation') history = fields.named_binding( "the function that maps an interpreter state to the history of commands that " "have been entered in that state", state.head, 'history' ) handler = fields.named_binding( "the function that maps an interpreter state to the computation invoked " "to handle questions in that state", state.head, 'computation' )
import term from memoization import memoize import properties import fields from frozendict import frozendict import askers import termtypes #Representing terms-------------------------------- #TODO I could probably make Representation a function, #then make a decorator that turns any such function into a "held" #version... quoted_term = term.simple("a term with head [head] head and bindings [bindings]", 'head', 'bindings') term_type = termtypes.new_type("the type of terms") termtypes.set_type(term_type, quoted_term) head = fields.named_binding( "the function that maps a term to its head", quoted_term.head, 'head' ) bindings = fields.named_binding( "the function that maps a term to its bindings", quoted_term.head, 'bindings' )
def __init__(self, question, other=None): if other is None: other = properties.trivial() self.other = other self.question = question def add(self, property): return Query(question=self.question, other=properties.both(property, self.other)) def quote(self): return T("a query with question [question] and requested properties [properties]", question=self.question, properties=self.other) #Replies---------------------- answer_is = term.simple("the requested answer is [A]", "A") @term.as_head("answering question [Q]") def answering(Q): return term.MetaTerm(answering.head, Q=Q) class Reply(): def __init__(self, value=None, answer=None, question=None): self.question = question if value is None: value = properties.trivial() if answer is not None: value = properties.both(answer_is(answer), value) self.value = value
def new_type(head, *args): result = term.simple(head, *args) set_type(meta_type, result) known_types.add(result) return result
def is_type(t): def set_by_head(f): set_type(t, f) return f return set_by_head #NOTE using t() so that I can have parametrized types later #but for this function it would be more natural to just pass in the constructed type #I think this may bite me at some point def set_type(t, head): if hasattr(head, 'head'): head = head.head @typesetter(head) def return_type(asker): return asker.reply(answer=t()) return head def set_types(t, *heads): return [set_type(t, head) for head in heads] known_types = set() def new_type(head, *args): result = term.simple(head, *args) set_type(meta_type, result) known_types.add(result) return result meta_type = term.simple("the type of types") new_type(meta_type.head)
#object: the object on which we are getting the field setter = Dispatcher("setter", ("field", "object", "new_value")) #object: the object on which we are setting the field #new_value: the new value to which we are setting the field #TODO think about properties affecting field retrieval... #(e.g. we might be counting accesses, or have normative properties true of children...) #TODO I should probably implement properties as boolean-valued fields #FIXME if an object is in a reducible form, like an update or held question, #then an access to its modifiers may not return the "real" modifiers. #It seems worth thinking more about avoiding errors that that could cause. field_not_found = term.simple("the referenced field wasn't found on the referenced object") #TODO should call .explain more often... #FIXME should cache the values of fields probably? Either as properties or by id. #FIXME it seems like their should really just be a "cached" decorator? #that operates at the meta level... @builtin("what is the value of [field] of [object]?") def get_field(asker, field, object): reply = getter.dispatch(asker, field, object) if reply is None: reply = asker.reply() if not reply.has_answer(): reply.add(field_not_found()) return reply def get(asker, field, object):
if convert.check_hard(asker, lists.is_empty(), visible_children): return asker.reply(answer=updates.update(is_pointer_now(), object)) else: result = updates.update(has_pointer_now(), object) result = updates.update( updates.apply_to( fields.compose(visible_children(), lists.last_element()), add_pointer_to_bottom() ), result ) return asker.reply(answer=result) #Adding children------------------------ children_on_expanded = term.simple("when expanded, should be given children from [bindings]", "bindings") @updates.translator(children_on_expanded.head, toggle_expanded.head) def add_children_on_expanded(asker, old, new, bindings): children = [] for p in lists.iterator(asker, lists.from_dict(bindings)): k = asker.ask(fields.get_field(first(), p)).firm_answer v = asker.ask(fields.get_field(second(), p)).firm_answer prefix = strings.string_concat(k, T.from_str(": ")) new_node = node_from_term(asker, v) new_node = updates.update( updates.apply_to(headline(), strings.prepend_str(prefix)), new_node ) children.append(new_node) return asker.reply(answer=updates.update(
from dispatch import Dispatcher import term from term import Term as T from ipdb import set_trace as debug builtin = Dispatcher("builtin", ("question",)) #FIXME this should probably get taken over by dictionaries... @builtin("what is a value bound to the key [key] in the bindings [bindings]?") def lookup(asker,bindings,key): return searcher.dispatch(asker,bindings,key) searcher = Dispatcher("searcher", ("bindings", "key")) @searcher(T.dict_cons.head) def simple_lookup(asker, search_key, key, value, other): are_equal = asker.ask(equal(key, search_key)).answer #FIXME should avoid these cyclic imports import convert if are_equal is not None and convert.to_bool(asker, are_equal): return asker.reply(answer=value) else: return asker.ask_tail(lookup(other, search_key)) equal = term.simple("are [a] and [b] equal?", "a", "b")
import askers import term from term import Term as T import properties class Counter(askers.BaseAsker): def __init__(self, *args, **kwargs): super(Counter, self).__init__(*args, **kwargs) self.cost = 1 def reply(self, *args, **kwargs): result = super(Counter, self).reply(*args, **kwargs) return result.add(queries(T.from_int(self.cost))) def process_response(self, response, *args, **kwargs): if response.head == queries.head: #TODO could easily do this inside the system self.cost += response['k'].to_int() return properties.trivial() else: return super(Counter, self).process_response(response, *args, **kwargs) queries = term.simple("[k] queries were posed while answering", 'k')
return asker.reply(answer=representations.quote(k)) @interpreter(parsing.make_str_expr.head) def interpret_str(asker, view, s): return asker.reply(answer=representations.quote(s)) @interpreter(parsing.make_list_expr.head) def interpret_list(asker, view, l): #TODO if I had better mapping, this would be fine... #for now I have to do it in this terrible way... result = representations.make(T.empty_list.head) for x in reversed(list(lists.iterator(asker, l))): result = representations.make(T.cons.head, head=x, tail=result) return asker.reply(answer=result) should_print = term.simple("the referent of [s] should be printed in the referenced view", "s") should_dispatch = term.simple("the head of [x] should be printed and its bindings promoted " "in the referenced view", "x") should_return = term.simple("[x] should be returned " "if the user interacts with the referenced view", "x") should_assign = term.simple("[s] should refer to [x] in future interactions with the " "referenced view", "s", "x") #TODO this is a duplicate of run_arg, I should generalize them def interpret_arg(name): def make_interpreted_arg(f): arg_names = inspect.getargspec(f).args run_index = arg_names.index(name)
T.simple_string.head, 'list' ) @is_type(string_type) @literalizer("the string formed by concatenating [a] with [b]") def concat(asker, a, b): return to_str(asker, a) + to_str(asker, b) #Converting to chars------------------------------ def to_char(asker, c): if c.to_char() is not None: return c.to_char() result = char_literalizer.dispatch(asker, c) if result is not None: return result else: k = asker.ask_firmly(ascii_code(c)) return chr(ints.to_int(k)) char_literalizer = Dispatcher("character literalizer", ("char",)) @is_type(char_type) @char_literalizer(T.simple_char.head) def literalize_simple_char(asker, x): return chr(ints.to_int(asker, x)) ascii_code = term.simple("what is the ascii code of the character [c]?", "c")