-- Copyright 1993-1998, by the Cecil Project -- Department of Computer Science and Engineering, University of Washington -- See the LICENSE file for license information. (--DOC `intervals' are immutable indexed collections representing a finite arithmetic sequence of integers. An interval returned by `new_interval(start, stop)' represents the integers in order from start to stop, inclusive; if stop is less than start then the sequence is empty. The interval may optionally specify a step value between start and stop. If the step is negative, then the sequence goes from start down to stop, inclusive; if stop is greater than start, then the sequence is empty. --) template object interval isa i_indexed[int]; private field start(@:interval):int; private field stop(@:interval):int; (-- private --) field step(@:interval):int; (-- Predicate stuff. put this back in when we generate decent code for predicates signature method in_range(s:interval, i:int):bool; predicate forward_interval isa interval when interval.step > 0; private method in_range(s@:forward_interval, i:int):bool { i >= s.start & {i <= s.stop} } predicate backward_interval isa interval when interval.step < 0; private method in_range(s@:backward_interval, i:int):bool { i <= s.start & {i >= s.stop} } -- disjoint forward_interval, backward_interval; --) method in_range(s@:interval, i:int):bool (** inline **) { if(s.step > 0, { if(i >= s.start, { i <= s.stop }, { false }) }, { if(i <= s.start, { i >= s.stop }, { false }) } )} (-- if(s.step > 0, { i >= s.start & { i <= s.stop } }, { i <= s.start & { i >= s.stop } }) } --) method length(s@:interval):int { max((s.stop - s.start)/s.step + 1, 0) } method do(s@:interval, c:&(int):void):void (** inline **) { let var i:int := s.start; while({ in_range(s, i) }, { eval(c, i); i := i + s.step; }); } method do_associations(s@:interval, c:&(int,int):void):void (** inline **) { let var k:int := 0; do(s, &(i:int){ eval(c, k, i); k := k.succ; }); } method fetch(s@:interval, k:int, if_absent:&():int):int { if(k < 0 | { k >= s.length }, if_absent, { s.start + s.step * k }) } method includes(s@:interval, i:int):bool { in_range(s, i) & { (i - s.start) % s.step = 0 } } method print_string(s@:interval):string { "interval(" || s.start.print_string || if(s.step = 1, { "" }, { "," || (s.start + s.step).print_string }) || ".." || s.stop.print_string || ")" } method collection_name(@:interval):string { "interval" } method new_interval(start:int, stop:int):interval { new_interval(start, stop, 1) } method new_interval(start:int, stop:int, step:int):interval { concrete object isa interval { start := start, stop := stop, step := step }} (--DOC Intervals can be used to implement regular for loops. For example, for i := 1 to 10 do ... end can be expressed in Cecil as: new_interval(1, 10).do(&(i:int){ ... }); The `for' methods provide a convenient interface for this idiom; they also currently implement this idiom more efficiently. (Recently, compiler optimizations have been implemented that greatly reduce the performance cost of the new_interval(...).do(...) version; if debug_support is disabled, there should be no difference in performance between new_interval(...).do(...) and for(...).) --) method for(start:int, stop:int, body:&(int):void):void (** inline **) { for(start, stop, 1, body); } method for(start:int, stop:int, step:int, body:&(int):void):void (** inline **) { (-- the following is too slow right now: new_interval(start, stop, step).do(body); --) let var i:int := start; while({ if(step < 0, { i >= stop }, { i <= stop }) }, { eval(body, i); i := i + step; }); }