from cozy.target_syntax import * from cozy.syntax_tools import pprint, fresh_var, free_vars, free_funcs, all_exps, alpha_equivalent, mk_lambda from cozy.contexts import Context from cozy.typecheck import is_collection, is_numeric, is_scalar from cozy.pools import Pool, RUNTIME_POOL from cozy.solver import ModelCachingSolver from cozy.evaluation import eval, eval_bulk from cozy.structures import extension_handler from cozy.logging import task, event from cozy.state_maintenance import mutate from cozy.opts import Option consider_maintenance_cost = Option( "consider-maintenance-cost", bool, False, description= "Experimental option that lets Cozy use ops for the cost model. Note that Cozy will become much slower with this option on." ) class Order(Enum): EQUAL = 0 LT = 1 GT = 2 AMBIGUOUS = 3 def flip(order): """Flips the direction of the Order. Less than becomes greater than (and vice versa). Equals and ambiguous
from itertools import chain from cozy.common import typechecked from cozy.target_syntax import * from cozy.typecheck import is_collection from cozy.solver import valid from cozy.syntax_tools import pprint, subst, enumerate_fragments, cse, shallow_copy, mk_lambda from cozy.handle_tools import reachable_handles_at_method, implicit_handle_assumptions_for_method from cozy.incrementalization import delta_form from cozy.opts import Option invariant_preservation_check = Option("invariant-preservation-check", bool, True) @typechecked def add_implicit_handle_assumptions(spec : Spec) -> Spec: """ At the start of every method, for all reachable handles (i.e. those stored on the data structure plus those in arguments): If two different handles have the same address, then they have the same value. """ spec = shallow_copy(spec) new_methods = [] for m in spec.methods: handles = reachable_handles_at_method(spec, m) new_assumptions = implicit_handle_assumptions_for_method(handles, m) m = shallow_copy(m) m.assumptions = list(m.assumptions) + new_assumptions new_methods.append(m) spec.methods = new_methods return spec
"""Well-formedness tests for Cozy expressions.""" import itertools from cozy.common import typechecked, OrderedSet from cozy.typecheck import is_collection, is_scalar from cozy.target_syntax import * from cozy.syntax_tools import pprint, free_vars from cozy.solver import ModelCachingSolver from cozy.pools import RUNTIME_POOL, STATE_POOL from cozy.opts import Option from cozy.structures import extension_handler from cozy.contexts import Context, RootCtx, shred allow_conditional_state = Option("allow-conditional-state", bool, True) do_expensive_checks = Option("expensive-wf-checks", bool, True) allow_int_arithmetic_state = Option("allow-int-arith-state", bool, True) class ExpIsNotWf(Exception): def __init__(self, e, offending_subexpression, reason): super().__init__(reason) self.e = e self.offending_subexpression = offending_subexpression self.reason = reason def exp_wf_nonrecursive(solver, e : Exp, context : Context, pool = RUNTIME_POOL, assumptions : Exp = T): state_vars = OrderedSet(v for v, p in context.vars() if p == STATE_POOL) args = OrderedSet(v for v, p in context.vars() if p == RUNTIME_POOL) assumptions = EAll([assumptions, context.path_condition()]) h = extension_handler(type(e))
from cozy.syntax import Query, Op, Exp, EVar, EAll from cozy.target_syntax import EStateVar from cozy.syntax_tools import pprint, unpack_representation, shallow_copy, wrap_naked_statevars from cozy.timeouts import Timeout from cozy import jobs from cozy.contexts import Context from cozy.opts import Option from cozy.cost_model import CostModel from . import core from .impls import Implementation nice_children = Option( "nice-children", bool, False, description='Apply a high Unix "niceness" value to child processes. ' + "Cozy can spawn a lot of child processes during synthesis, so this " + "option exists to help control resource usage.") log_dir = Option( "log-dir", str, "/tmp", description="Location to place log files for child processes.") class ImproveQueryJob(jobs.Job): @typechecked def __init__(self, state: [EVar],
from cozy.target_syntax import (EStateVar, EMap, EFilter, EFlatMap, TMap, EMakeMap2, EMapKeys, EMapGet, EHasKey) from cozy.structures import all_extension_handlers from cozy.syntax_tools import pprint, fresh_var, free_vars, freshen_binders, alpha_equivalent, all_types from cozy.evaluation import eval_bulk, construct_value, values_equal from cozy.typecheck import is_numeric, is_collection, is_ordered, is_hashable from cozy.cost_model import CostModel, Order from cozy.pools import Pool, RUNTIME_POOL, STATE_POOL, pool_name from cozy.contexts import Context, RootCtx, UnderBinder, more_specific_context from cozy.logging import task, task_begin, task_end, event, verbose from cozy.opts import Option do_enumerate = Option( "enumeration", bool, True, description="Enable brute-force enumeration. " + "Disabling this option cripples Cozy, but makes the effect of the " + "acceleration rules more apparent.") @functools.total_ordering class Fingerprint(object): """A summary of an expression's behavior on some inputs. An expression's fingerprint is derived from a set of example inputs. Two expressions with different fingerprints are known to behave differently. Two expressions with the same fingerprint might be semantically equivalent (i.e. behave the same on all inputs), or they might just appear to be semantically equivalent on the given inputs.
import itertools from cozy.common import fresh_name from cozy import syntax from cozy import target_syntax from cozy.syntax_tools import free_vars, pprint, fresh_var, strip_EStateVar, subst, BottomUpRewriter, alpha_equivalent from cozy.typecheck import is_numeric from cozy.solver import valid from cozy.opts import Option from cozy.structures import extension_handler from cozy.evaluation import construct_value skip_stateless_synthesis = Option( "skip-stateless-synthesis", bool, False, description= "Do not waste time optimizing expressions that do not depend on the data structure state" ) update_numbers_with_deltas = Option("update-numbers-with-deltas", bool, False) def mutate(e: syntax.Exp, op: syntax.Stm) -> syntax.Exp: """Return the new value of `e` after executing `op`.""" if isinstance(op, syntax.SNoOp): return e elif isinstance(op, syntax.SAssign): return _do_assignment(op.lhs, op.rhs, e) elif isinstance(op, syntax.SCall): if op.func == "add": return mutate(
"""Helper method to do a little simplification on expressions. This is useful both to make expressions visually simpler for presentation and to make them simpler for the synthesis backend. The most important function is `simplify`. """ from cozy.target_syntax import * from cozy.typecheck import is_collection, is_numeric from cozy.syntax_tools import BottomUpRewriter, alpha_equivalent, cse, compose, pprint, mk_lambda, replace from cozy.evaluation import construct_value, eval from cozy.solver import valid, satisfy from cozy.opts import Option checked_simplify = Option("checked-simplification", bool, False) def is_simple(t): if is_numeric(t): return True if isinstance(t, TString) or isinstance(t, TEnum) or isinstance(t, TBool) or isinstance(t, TNative): return True if isinstance(t, TTuple) and all(is_simple(tt) for tt in t.ts): return True if isinstance(t, TRecord) and all(is_simple(tt) for f, tt in t.fields): return True return False class _V(BottomUpRewriter): def __init__(self, debug=False): self.debug = debug
from queue import Empty from cozy.common import typechecked, OrderedSet from cozy.target_syntax import * from cozy.syntax_tools import all_types, free_vars, pprint, unpack_representation, shallow_copy, wrap_naked_statevars from cozy.typecheck import is_scalar from cozy.timeouts import Timeout from cozy import jobs from cozy.contexts import RootCtx from cozy.opts import Option from cozy.cost_model import CostModel from . import core from .impls import Implementation nice_children = Option("nice-children", bool, False) log_dir = Option("log-dir", str, "/tmp") SynthCtx = namedtuple("SynthCtx", ["all_types", "basic_types"]) LINE_BUFFER_MODE = 1 # see help for open() function class ImproveQueryJob(jobs.Job): @typechecked def __init__(self, ctx: SynthCtx, state: [EVar], assumptions: [Exp], q: Query, k, hints: [Exp] = [], freebies: [Exp] = [],
from cozy.syntax_tools import all_types, alpha_equivalent, BottomUpExplorer, BottomUpRewriter, free_vars, pprint, subst, implies, fresh_var, mk_lambda, all_exps, equal, is_scalar, tease_apart, shallow_copy, enumerate_fragments2, wrap_naked_statevars import cozy.incrementalization as inc from cozy.timeouts import Timeout, TimeoutException from cozy.cost_model import CompositeCostModel from cozy import jobs from cozy.solver import valid from cozy.opts import Option from cozy.pools import STATE_POOL from . import core from .impls import Implementation from .grammar import BinderBuilder from .acceleration import AcceleratedBuilder from .misc import rewrite_ret, queries_equivalent accelerate = Option("acceleration-rules", bool, True) nice_children = Option("nice-children", bool, False) log_dir = Option("log-dir", str, "/tmp") SynthCtx = namedtuple("SynthCtx", ["all_types", "basic_types"]) LINE_BUFFER_MODE = 1 # see help for open() function class ImproveQueryJob(jobs.Job): @typechecked def __init__(self, ctx: SynthCtx, state: [EVar], assumptions: [Exp], q: Query, k, hints: [Exp] = [],
from collections import OrderedDict from functools import total_ordering, lru_cache import itertools from cozy.common import typechecked, partition, make_random_access from cozy.target_syntax import * from cozy.syntax_tools import BottomUpExplorer, pprint, equal, fresh_var, mk_lambda, free_vars, subst, alpha_equivalent, all_exps, cse from cozy.typecheck import is_collection from cozy.pools import RUNTIME_POOL, STATE_POOL from cozy.solver import valid, satisfiable, REAL, SolverReportedUnknown, IncrementalSolver from cozy.evaluation import eval from cozy.opts import Option assume_large_cardinalities = Option("assume-large-cardinalities", int, 1000) integer_cardinalities = Option("try-integer-cardinalities", bool, True) # In principle these settings are supposed to improve performance; in practice, # they do not. incremental = False use_indicators = False class Cost(object): WORSE = "worse" BETTER = "better" UNORDERED = "unordered" def compare_to(self, other, assumptions : Exp = T, solver : IncrementalSolver = None): raise NotImplementedError() class CostModel(object): def cost(self, e, pool): raise NotImplementedError()
from cozy.syntax_tools import subst, pprint, free_vars, fresh_var, alpha_equivalent, strip_EStateVar, freshen_binders, wrap_naked_statevars, break_conj, inline_lets from cozy.wf import exp_wf from cozy.common import No, OrderedSet, unique, OrderedSet, StopException from cozy.solver import valid, solver_for_context, ModelCachingSolver from cozy.evaluation import construct_value from cozy.cost_model import CostModel, Order, LINEAR_TIME_UOPS from cozy.opts import Option from cozy.pools import Pool, RUNTIME_POOL, STATE_POOL, pool_name from cozy.contexts import Context, all_subexpressions_with_context_information, replace from cozy.logging import task, event from cozy.structures import extension_handler from .acceleration import try_optimize from .enumeration import Enumerator, Fingerprint, retention_policy eliminate_vars = Option("eliminate-vars", bool, False) enable_blacklist = Option( "enable-blacklist", bool, False, description='If enabled, skip expressions that have been ' + 'found not useful or invalid during improvement searching') check_blind_substitutions = Option( "blind-substitutions", bool, True, description='"Blind substitutions" allow Cozy to try replacing expressions ' + "with other expressions, even when the substitution appears wrong. " + "In some cases, this allows Cozy to quickly discover nonintuitive " + "solutions.") enable_eviction = Option("eviction", bool, True)
from cozy.typecheck import is_collection, is_scalar from cozy.syntax_tools import subst, pprint, free_vars, fresh_var, alpha_equivalent, enumerate_fragments, strip_EStateVar, freshen_binders, wrap_naked_statevars, break_conj from cozy.wf import ExpIsNotWf, exp_wf from cozy.common import No, OrderedSet, ADT, Visitor, fresh_name, unique, pick_to_sum, OrderedDefaultDict, OrderedSet, group_by, find_one, extend, StopException from cozy.solver import satisfy, satisfiable, valid, IncrementalSolver, ModelCachingSolver from cozy.evaluation import eval, eval_bulk, mkval, construct_value, uneval, eq from cozy.cost_model import CostModel, Order, rt as runtime, asymptotic_runtime, max_storage_size, LINEAR_TIME_UOPS from cozy.opts import Option from cozy.pools import Pool, ALL_POOLS, RUNTIME_POOL, STATE_POOL, pool_name from cozy.contexts import Context, shred, replace from cozy.logging import task, event from .acceleration import try_optimize from .enumeration import Enumerator, fingerprint, eviction_policy eliminate_vars = Option("eliminate-vars", bool, False) incremental = Option( "incremental", bool, False, description="Experimental option that can greatly improve performance.") class NoMoreImprovements(Exception): pass def exploration_order(targets: [Exp], context: Context, pool: Pool = RUNTIME_POOL): """
import itertools from cozy.common import find_one from cozy.target_syntax import * from cozy.syntax_tools import fresh_var, free_vars, free_funcs, mk_lambda, strip_EStateVar, alpha_equivalent, compose, nnf, map_value_func from cozy.typecheck import is_collection, retypecheck from cozy.contexts import Context, all_subexpressions_with_context_information, replace from cozy.pools import Pool, RUNTIME_POOL, STATE_POOL from cozy.structures.heaps import TMinHeap, TMaxHeap, EMakeMinHeap, EMakeMaxHeap, EHeapPeek2 from cozy.evaluation import construct_value, uneval, eval from cozy.opts import Option from cozy.wf import repair_well_formedness accelerate = Option("acceleration-rules", bool, True, description="Enable rewrite rules that try to optimize " + "expressions during synthesis. This usually " + "improves Cozy's performance.") def find_one_or_fail(iter): res = find_one(iter) if res is None: raise ValueError() return res def try_optimize(e: Exp, context: Context, pool: Pool): """Yields expressions for the given context and pool. The expressions are likely to be semantically equivalent to `e` and likely
from cozy.typecheck import is_collection, is_scalar from cozy.syntax_tools import subst, pprint, free_vars, fresh_var, alpha_equivalent, strip_EStateVar, freshen_binders, wrap_naked_statevars, break_conj from cozy.wf import exp_wf from cozy.common import No, OrderedSet, unique, OrderedSet, StopException from cozy.solver import valid, solver_for_context from cozy.evaluation import construct_value from cozy.cost_model import CostModel, Order, LINEAR_TIME_UOPS from cozy.opts import Option from cozy.pools import Pool, RUNTIME_POOL, STATE_POOL, pool_name from cozy.contexts import Context, shred, replace from cozy.logging import task, event from .acceleration import try_optimize from .enumeration import Enumerator, Fingerprint, fingerprint, fingerprints_match, fingerprint_is_subset, eviction_policy eliminate_vars = Option("eliminate-vars", bool, False) enable_blacklist = Option("enable-blacklist", bool, False) check_all_substitutions = Option("check-all-substitutions", bool, True) enable_eviction = Option("eviction", bool, True) def exploration_order(targets: [Exp], context: Context, pool: Pool = RUNTIME_POOL): """ What order should subexpressions of the given targets be explored for possible improvements? Yields (target, subexpression, subcontext, subpool) tuples. """
import igraph from cozy.common import fresh_name, find_one, typechecked, OrderedSet from cozy.syntax import * from cozy.target_syntax import EFilter, EDeepIn, EStateVar from cozy.syntax_tools import subst, free_vars, fresh_var, alpha_equivalent, all_exps, BottomUpRewriter, BottomUpExplorer, pprint, replace, shallow_copy, tease_apart, wrap_naked_statevars, rewrite_ret from cozy.handle_tools import reachable_handles_at_method, implicit_handle_assumptions_for_method import cozy.state_maintenance as inc from cozy.opts import Option from cozy.simplification import simplify from cozy.solver import valid, ModelCachingSolver from cozy.logging import task, event from .misc import queries_equivalent, pull_temps dedup_queries = Option("deduplicate-subqueries", bool, True) def simplify_or_ignore(e): ee = simplify(e) return ee if ee.size() < e.size() else e def safe_feedback_arc_set(g, method): """ Compute the feedback arc set for directed graph `g`. This function works around a potential segfault in igraph: https://github.com/igraph/igraph/issues/858 """ assert g.is_directed()
import sys import traceback from cozy.target_syntax import * from cozy.syntax_tools import subst, pprint, free_vars, free_funcs, BottomUpExplorer, BottomUpRewriter, equal, fresh_var, alpha_equivalent, all_exps, implies, mk_lambda, enumerate_fragments2, strip_EStateVar from cozy.wf import ExpIsNotWf, exp_wf, exp_wf_nonrecursive from cozy.common import OrderedSet, ADT, Visitor, fresh_name, typechecked, unique, pick_to_sum, cross_product, OrderedDefaultDict, OrderedSet, group_by, find_one from cozy.solver import satisfy, satisfiable, valid, IncrementalSolver from cozy.evaluation import eval, eval_bulk, mkval, construct_value, uneval from cozy.cost_model import CostModel, Cost from cozy.opts import Option from cozy.pools import ALL_POOLS, RUNTIME_POOL, STATE_POOL, pool_name from .cache import Cache, SeenSet save_testcases = Option("save-testcases", str, "", metavar="PATH") hyperaggressive_culling = Option("hyperaggressive-culling", bool, False) hyperaggressive_eviction = Option("hyperaggressive-eviction", bool, True) reject_symmetric_binops = Option("reject-symmetric-binops", bool, False) eliminate_vars = Option("eliminate-vars", bool, True) reset_on_success = Option("reset-on-success", bool, False) enforce_seen_wf = Option("enforce-seen-set-well-formed", bool, False) enforce_strong_progress = Option("enforce-strong-progress", bool, False) enforce_exprs_wf = Option("enforce-expressions-well-formed", bool, False) preopt = Option("optimize-accelerated-exps", bool, True) check_depth = Option("proof-depth", int, 4) incremental = Option("incremental", bool, False, description="Experimental option that can greatly improve performance.") # When are costs checked? CHECK_FINAL_COST = True # compare overall cost of each candidiate to target CHECK_SUBST_COST = False # compare cost of each subexp. to its replacement
""" While the syntax module declares the core _input_ language, this module declares additional syntax extensions that can appear in the _target_ language: the primitives the tool can output and use during synthesis. """ from cozy.syntax import * from cozy.common import declare_case, typechecked, fresh_name from cozy.opts import Option enforce_estatevar_wf = Option("enforce-well-formed-state-var-boundaries", bool, False) # Misc TRef = declare_case(Type, "TRef", ["t"]) EEnumToInt = declare_case(Exp, "EEnumToInt", ["e"]) EBoolToInt = declare_case(Exp, "EBoolToInt", ["e"]) EStm = declare_case(Exp, "EStm", ["stm", "e"]) # State var barrier: sub-expression should be maintained as a fresh state var EStateVar = declare_case(Exp, "EStateVar", ["e"]) class IllegalStateVarBoundary(Exception): pass old = EStateVar.__init__ def f(self, e):
from cozy.contexts import Context from cozy.typecheck import is_collection, is_numeric, is_scalar from cozy.pools import Pool, RUNTIME_POOL from cozy.solver import ModelCachingSolver from cozy.evaluation import eval, eval_bulk from cozy.structures import extension_handler from cozy.logging import task, event from cozy.state_maintenance import mutate from cozy.polynomials import Polynomial, DominantTerm from cozy.opts import Option from cozy.structures.treemultiset import ETreeMultisetElems, ETreeMultisetPeek cost_model_selection = Option("cost-model", int, 2, description="Cost model to use. " + "Higher numbers are much slower to evaluate but often yield better results. " + "0: optimize for expression size. " + "1: optimize for asymptotic runtime. " + "2: optimize for a mix of asymptotic runtime, storage size, and exact runtime. " + "3: optimize for a mix of asymptotic runtime and state maintenance cost.") class Order(Enum): EQUAL = "=" LT = "<" GT = ">" AMBIGUOUS = "?" def flip(order): """Flips the direction of the Order. Less than becomes greater than (and vice versa). Equals and ambiguous are unchanged.
"""A small logging framework that supports timing and indented log messages. Important functions: - task: a context manager to wrap self-contained tasks - event: print a log message (indented based on active tasks) """ from collections import defaultdict from contextlib import contextmanager import datetime from cozy.opts import Option verbose = Option("verbose", bool, False) _times = defaultdict(float) _task_stack = [] _begin = datetime.datetime.now() def log(string): if verbose.value: print(string) def task_begin(name, **kwargs): start = datetime.datetime.now() _task_stack.append((name, start)) if not verbose.value: return indent = " " * (len(_task_stack) - 1)
from cozy.handle_tools import reachable_handles_at_method, implicit_handle_assumptions import cozy.state_maintenance as inc from cozy.opts import Option from cozy.simplification import simplify from cozy.solver import valid, ModelCachingSolver from cozy.logging import task, event from cozy.graph_theory import DirectedGraph from cozy.contexts import Context, RootCtx from cozy.wf import repair_well_formedness from .misc import queries_equivalent, pull_temps dedup_queries = Option( "deduplicate-subqueries", bool, True, description="Use the solver to deduplicate the internal queries that Cozy " + "adds to the implementation. While deduplication is slow (it involves " + "a solver call), it results in far fewer query synthesis threads.") def simplify_or_ignore(e): ee = simplify(e) return ee if ee.size() < e.size() else e class Implementation(object): @typechecked def __init__(self, spec: Spec, concretization_functions: [(EVar, Exp)], query_specs: [Query], query_impls: OrderedDict, updates: defaultdict, handle_updates: defaultdict):
import itertools from cozy.common import pick_to_sum, cross_product, group_by, find_one from cozy.target_syntax import * from cozy.syntax_tools import subst, mk_lambda, free_vars, is_scalar, pprint, strip_EStateVar from cozy.typecheck import is_collection, is_numeric from cozy.pools import STATE_POOL, RUNTIME_POOL, ALL_POOLS from cozy.opts import Option from .core import ExpBuilder build_exprs = Option("build-exprs", bool, True) class BinderBuilder(ExpBuilder): def __init__(self, binders: [EVar], state_vars: [EVar], args: [EVar] = []): super().__init__() self.binders = binders self.state_vars = state_vars self.args = args def __repr__(self): return "BinderBuilder(binders={!r}, state_vars={!r}, args={!r})".format( self.binders, self.state_vars, self.args) def build(self, cache, size): # print("Cache:") # for (e, sz, pool) in cache: # from cozy.syntax_tools import pprint # print(" @size={}, pool={}\t:\t{}".format(sz, pool, pprint(e))) binders_by_type = group_by(self.binders, lambda b: b.type)
from cozy.typecheck import is_collection from cozy.syntax_tools import subst, pprint, free_vars, fresh_var, alpha_equivalent, enumerate_fragments, strip_EStateVar, freshen_binders, wrap_naked_statevars, break_conj from cozy.wf import ExpIsNotWf, exp_wf from cozy.common import OrderedSet, ADT, Visitor, fresh_name, unique, pick_to_sum, OrderedDefaultDict, OrderedSet, group_by, find_one, extend, StopException from cozy.solver import satisfy, satisfiable, valid, IncrementalSolver, ModelCachingSolver from cozy.evaluation import eval, eval_bulk, mkval, construct_value, uneval, eq from cozy.cost_model import CostModel, Order, rt as runtime, asymptotic_runtime, max_storage_size, LINEAR_TIME_UOPS from cozy.opts import Option from cozy.pools import Pool, ALL_POOLS, RUNTIME_POOL, STATE_POOL, pool_name from cozy.contexts import Context, shred, replace from cozy.logging import task, event from .acceleration import try_optimize from .enumeration import Enumerator, fingerprint, eviction_policy eliminate_vars = Option("eliminate-vars", bool, False) incremental = Option( "incremental", bool, False, description="Experimental option that can greatly improve performance.") class NoMoreImprovements(Exception): pass def exploration_order(targets: [Exp], context: Context, pool: Pool = RUNTIME_POOL): """
"""Helper class to implement interruptable tasks.""" from multiprocessing import Process, Array, Queue from queue import Queue as PlainQueue, Empty, Full import time import threading import sys from cozy.opts import Option do_profiling = Option("profile", bool, False, description="Profile Cozy itself") class Job(object): def __init__(self): self._thread = Process(target=self._run, daemon=True) self._flags = Array("b", [False] * 3) # flags[0] - stop_requested? # flags[1] - done? # flags[2] - true iff completed with no exception def start(self): self._thread.start() def _run(self): try: if do_profiling.value: import cProfile import tempfile
from cozy.common import fresh_name from cozy import syntax from cozy import target_syntax from cozy.syntax_tools import free_vars, pprint, fresh_var, mk_lambda, alpha_equivalent, strip_EStateVar, subst from cozy.typecheck import is_numeric from cozy.solver import valid from cozy.opts import Option skip_stateless_synthesis = Option( "skip-stateless-synthesis", bool, False, description= "Do not waste time optimizing expressions that do not depend on the data structure state" ) def delta_form(members: [(str, syntax.Type)], op: syntax.Op) -> { str: syntax.Exp }: """ Given the abstract state of a data structure and an imperative operation that modifies it, return new values for the abstract state in terms of the old values. Input: members - a list of (id, type) tuples specifying the abstract state op - an imperative operation Output: dictionary mapping id->new_exp (suitable for use by syntax_tools.subst)