mirror of
				https://github.com/KevinMidboe/linguist.git
				synced 2025-10-29 17:50:22 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			218 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Alloy
		
	
	
	
	
	
			
		
		
	
	
			218 lines
		
	
	
		
			6.4 KiB
		
	
	
	
		
			Alloy
		
	
	
	
	
	
module examples/systems/views
 | 
						|
 | 
						|
/*
 | 
						|
 * Model of views in object-oriented programming.
 | 
						|
 *
 | 
						|
 * Two object references, called the view and the backing,
 | 
						|
 * are related by a view mechanism when changes to the
 | 
						|
 * backing are automatically propagated to the view. Note
 | 
						|
 * that the state of a view need not be a projection of the
 | 
						|
 * state of the backing; the keySet method of Map, for
 | 
						|
 * example, produces two view relationships, and for the
 | 
						|
 * one in which the map is modified by changes to the key
 | 
						|
 * set, the value of the new map cannot be determined from
 | 
						|
 * the key set. Note that in the iterator view mechanism,
 | 
						|
 * the iterator is by this definition the backing object,
 | 
						|
 * since changes are propagated from iterator to collection
 | 
						|
 * and not vice versa. Oddly, a reference may be a view of
 | 
						|
 * more than one backing: there can be two iterators on the
 | 
						|
 * same collection, eg. A reference cannot be a view under
 | 
						|
 * more than one view type.
 | 
						|
 *
 | 
						|
 * A reference is made dirty when it is a backing for a view
 | 
						|
 * with which it is no longer related by the view invariant.
 | 
						|
 * This usually happens when a view is modified, either
 | 
						|
 * directly or via another backing. For example, changing a
 | 
						|
 * collection directly when it has an iterator invalidates
 | 
						|
 * it, as does changing the collection through one iterator
 | 
						|
 * when there are others.
 | 
						|
 *
 | 
						|
 * More work is needed if we want to model more closely the
 | 
						|
 * failure of an iterator when its collection is invalidated.
 | 
						|
 *
 | 
						|
 * As a terminological convention, when there are two
 | 
						|
 * complementary view relationships, we will give them types
 | 
						|
 * t and t'. For example, KeySetView propagates from map to
 | 
						|
 * set, and KeySetView' propagates from set to map.
 | 
						|
 *
 | 
						|
 * author: Daniel Jackson
 | 
						|
 */
 | 
						|
 | 
						|
open util/ordering[State] as so
 | 
						|
open util/relation as rel
 | 
						|
 | 
						|
sig Ref {}
 | 
						|
sig Object {}
 | 
						|
 | 
						|
-- t->b->v in views when v is view of type t of backing b
 | 
						|
-- dirty contains refs that have been invalidated
 | 
						|
sig State {
 | 
						|
  refs: set Ref,
 | 
						|
  obj: refs -> one Object,
 | 
						|
  views: ViewType -> refs -> refs,
 | 
						|
  dirty: set refs
 | 
						|
--  , anyviews: Ref -> Ref -- for visualization
 | 
						|
  }
 | 
						|
-- {anyviews = ViewType.views}
 | 
						|
 | 
						|
sig Map extends Object {
 | 
						|
  keys: set Ref,
 | 
						|
  map: keys -> one Ref
 | 
						|
  }{all s: State |  keys + Ref.map in s.refs}
 | 
						|
sig MapRef extends Ref {}
 | 
						|
fact {State.obj[MapRef] in Map}
 | 
						|
 | 
						|
sig Iterator extends Object {
 | 
						|
  left, done: set Ref,
 | 
						|
  lastRef: lone done
 | 
						|
  }{all s: State | done + left + lastRef in s.refs}
 | 
						|
sig IteratorRef extends Ref {}
 | 
						|
fact {State.obj[IteratorRef] in Iterator}
 | 
						|
 | 
						|
sig Set extends Object {
 | 
						|
  elts: set Ref
 | 
						|
  }{all s: State | elts in s.refs}
 | 
						|
sig SetRef extends Ref {}
 | 
						|
fact {State.obj[SetRef] in Set}
 | 
						|
 | 
						|
abstract sig ViewType {}
 | 
						|
one sig KeySetView, KeySetView', IteratorView extends ViewType {}
 | 
						|
fact ViewTypes {
 | 
						|
  State.views[KeySetView] in MapRef -> SetRef
 | 
						|
  State.views[KeySetView'] in SetRef -> MapRef
 | 
						|
  State.views[IteratorView] in IteratorRef -> SetRef
 | 
						|
  all s: State | s.views[KeySetView] = ~(s.views[KeySetView'])
 | 
						|
  }
 | 
						|
 | 
						|
/**
 | 
						|
 * mods is refs modified directly or by view mechanism
 | 
						|
 * doesn't handle possibility of modifying an object and its view at once?
 | 
						|
 * should we limit frame conds to non-dirty refs?
 | 
						|
 */
 | 
						|
pred modifies [pre, post: State, rs: set Ref] {
 | 
						|
  let vr = pre.views[ViewType], mods = rs.*vr {
 | 
						|
    all r: pre.refs - mods | pre.obj[r] = post.obj[r]
 | 
						|
    all b: mods, v: pre.refs, t: ViewType |
 | 
						|
      b->v in pre.views[t] => viewFrame [t, pre.obj[v], post.obj[v], post.obj[b]]
 | 
						|
    post.dirty = pre.dirty +
 | 
						|
      {b: pre.refs | some v: Ref, t: ViewType |
 | 
						|
          b->v in pre.views[t] && !viewFrame [t, pre.obj[v], post.obj[v], post.obj[b]]
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
pred allocates [pre, post: State, rs: set Ref] {
 | 
						|
  no rs & pre.refs
 | 
						|
  post.refs = pre.refs + rs
 | 
						|
  }
 | 
						|
 | 
						|
/** 
 | 
						|
 * models frame condition that limits change to view object from v to v' when backing object changes to b'
 | 
						|
 */
 | 
						|
pred viewFrame [t: ViewType, v, v', b': Object] {
 | 
						|
  t in KeySetView => v'.elts = dom [b'.map]
 | 
						|
  t in KeySetView' => b'.elts = dom [v'.map]
 | 
						|
  t in KeySetView' => (b'.elts) <: (v.map) = (b'.elts) <: (v'.map)
 | 
						|
  t in IteratorView => v'.elts = b'.left + b'.done
 | 
						|
  }
 | 
						|
 | 
						|
pred MapRef.keySet [pre, post: State, setRefs: SetRef] {
 | 
						|
  post.obj[setRefs].elts = dom [pre.obj[this].map]
 | 
						|
  modifies [pre, post, none]
 | 
						|
  allocates [pre, post, setRefs]
 | 
						|
  post.views = pre.views + KeySetView->this->setRefs + KeySetView'->setRefs->this
 | 
						|
  }
 | 
						|
 | 
						|
pred MapRef.put [pre, post: State, k, v: Ref] {
 | 
						|
  post.obj[this].map = pre.obj[this].map ++ k->v
 | 
						|
  modifies [pre, post, this]
 | 
						|
  allocates [pre, post, none]
 | 
						|
  post.views = pre.views
 | 
						|
  }
 | 
						|
 | 
						|
pred SetRef.iterator [pre, post: State, iterRef: IteratorRef] {
 | 
						|
  let i = post.obj[iterRef] {
 | 
						|
    i.left = pre.obj[this].elts
 | 
						|
    no i.done + i.lastRef
 | 
						|
    }
 | 
						|
  modifies [pre,post,none]
 | 
						|
  allocates [pre, post, iterRef]
 | 
						|
  post.views = pre.views + IteratorView->iterRef->this
 | 
						|
  }
 | 
						|
 | 
						|
pred IteratorRef.remove [pre, post: State] {
 | 
						|
  let i = pre.obj[this], i' = post.obj[this] {
 | 
						|
    i'.left = i.left
 | 
						|
    i'.done = i.done - i.lastRef
 | 
						|
    no i'.lastRef
 | 
						|
    }
 | 
						|
  modifies [pre,post,this]
 | 
						|
  allocates [pre, post, none]
 | 
						|
  pre.views = post.views
 | 
						|
  }
 | 
						|
 | 
						|
pred IteratorRef.next [pre, post: State, ref: Ref] {
 | 
						|
  let i = pre.obj[this], i' = post.obj[this] {
 | 
						|
    ref in i.left
 | 
						|
    i'.left = i.left - ref
 | 
						|
    i'.done = i.done + ref
 | 
						|
    i'.lastRef = ref
 | 
						|
    }
 | 
						|
  modifies [pre, post, this]
 | 
						|
  allocates [pre, post, none]
 | 
						|
  pre.views = post.views
 | 
						|
  }
 | 
						|
 | 
						|
pred IteratorRef.hasNext [s: State] {
 | 
						|
  some s.obj[this].left
 | 
						|
  }
 | 
						|
 | 
						|
assert zippishOK {
 | 
						|
  all
 | 
						|
    ks, vs: SetRef,
 | 
						|
    m: MapRef,
 | 
						|
    ki, vi: IteratorRef,
 | 
						|
    k, v: Ref |
 | 
						|
    let s0=so/first,
 | 
						|
    s1=so/next[s0],
 | 
						|
    s2=so/next[s1],
 | 
						|
    s3=so/next[s2],
 | 
						|
    s4=so/next[s3],
 | 
						|
    s5=so/next[s4],
 | 
						|
    s6=so/next[s5],
 | 
						|
    s7=so/next[s6] |
 | 
						|
  ({
 | 
						|
    precondition [s0, ks, vs, m]
 | 
						|
    no s0.dirty
 | 
						|
    ks.iterator [s0, s1, ki]
 | 
						|
    vs.iterator [s1, s2, vi]
 | 
						|
    ki.hasNext [s2]
 | 
						|
    vi.hasNext [s2]
 | 
						|
    ki.this/next [s2, s3, k]
 | 
						|
    vi.this/next [s3, s4, v]
 | 
						|
    m.put [s4, s5, k, v]
 | 
						|
    ki.remove [s5, s6]
 | 
						|
    vi.remove [s6, s7]
 | 
						|
  } => no State.dirty)
 | 
						|
  }
 | 
						|
 | 
						|
pred precondition [pre: State, ks, vs, m: Ref] {
 | 
						|
  // all these conditions and other errors discovered in scope of 6 but 8,3
 | 
						|
  // in initial state, must have view invariants hold
 | 
						|
  (all t: ViewType, b, v: pre.refs |
 | 
						|
    b->v in pre.views[t] => viewFrame [t, pre.obj[v], pre.obj[v], pre.obj[b]])
 | 
						|
  // sets are not aliases
 | 
						|
--  ks != vs
 | 
						|
  // sets are not views of map
 | 
						|
--  no (ks+vs)->m & ViewType.pre.views
 | 
						|
  // no iterator currently on either set
 | 
						|
--  no Ref->(ks+vs) & ViewType.pre.views
 | 
						|
  }
 | 
						|
 | 
						|
check zippishOK for 6 but 8 State, 3 ViewType expect 1
 | 
						|
 | 
						|
/** 
 | 
						|
 * experiment with controlling heap size
 | 
						|
 */
 | 
						|
fact {all s: State | #s.obj < 5}
 |