# This is the standard definition header file of Zilk. It assumes that # $Object and the rest of the built-in classes have already been declared # by the Zilk initialization. $IMPORT is ["org.zilk", "java.lang", "java.awt", "javax.swing", "java.util", "java.awt.event", "java.io"] # How to print a list object. $LISTSEPARATOR is "," $LISTLEFTBRACKET is "[" $LISTRIGHTBRACKET is "]" # Override this method to make your objects to be able to be false def $Object.isTrue true enddef # Override these methods to make indexing work for your methods def $Object.getIndex |key| true enddef def $Object.setIndex |key, value| value enddef def $Object.delIndex |key| key enddef # The iterator method each. The other iterator methods are defined based on this, # so it is enough to override this method in your own class for the rest to work. def $Object.each |f first last| i is this.iterator(first last) while i.hasNext() f(i.next()) endwhile enddef # Iterator method for the $Integer class. def $Integer.iterator class IntegerIterator def initialize |integer| @count, @limit is -1, integer - 1 enddef def hasNext @count < @limit enddef def next @count is @count + 1 enddef endclass IntegerIterator(this) enddef # Iterator method for the list class. def $List.iterator |first last| class ListIterator def initialize |list first last| unless first first is 0 endunless unless last last is list.length - 1 endunless @count, @limit, @list is first, last, list enddef def hasNext @count <= @limit enddef def next result is list[count] @count is count + 1 result enddef endclass ListIterator(this first last) enddef # Iterator method for the $String class. def $String.iterator |first last| class StringIterator def initialize |list first last| unless first first is 0 endunless unless last last is list.length - 1 endunless @count, @limit, @list is first, last, list enddef def hasNext @count <= @limit enddef def next result is list[count] @count is count + 1 result enddef endclass StringIterator(this first last) enddef # Iterator method for the $Range class. def $Range.iterator |first last| class RangeIterator def initialize |range first last| unless first first is range.first endunless unless last last is range.last endunless @range is range @count is first - 1 if not range.includeEnd @limit is last - 1 else @limit is last endif enddef def hasNext @count < @limit enddef def next @count is @count + 1 @count enddef endclass RangeIterator(this first last) enddef # Iterator loop with indices def $Object.eachWithIndex |f first last| count is 0 this.each( {|element| f(element count) count is count + 1 } first last) enddef # Reverse a string def $String.reverse result is "" this.each(lambda|c| result is c + result endlambda) result enddef # Reverse a list def $List.reverse result is $List.create(this.length) count is this.length - 1 this.each(lambda|x| result[count] is x count is count - 1 endlambda) result enddef def $List.reverse! first, last is 0, this.length - 1 while first < last this[first], this[last] is this[last], this[first] first, last is first+1, last-1 endwhile this enddef # Partition an object def $Object.partition |f first last| pos, neg, posCount, negCount is [], [], 0, 0 this.each({|x| if f(x) pos[posCount] is x posCount is posCount + 1 else neg[negCount] is x negCount is negCount + 1 endif } first last) [pos neg] enddef # Sort a list with quicksort def $List.sort |comparator| unless (comparator) comparator is {|x y| x < y} endunless def pivotComp |pivot| { |x| comparator(x pivot) } enddef def qsort |list| if list.length > 1 pivot is list[0] parts is list.partition(pivotComp(pivot) 1) qsort(parts[0]) + pivot + qsort(parts[1]) else list endif enddef qsort(this) enddef # Optimize this later to work in place. def $List.sort! |comparator| this[0...this.length] is this.sort(comparator) enddef # Split a string into a list of pieces at separator character c def $String.split |c| unless(c) c is " " endunless begin, current, result, count is 0, 0, [], 0 while current < this.length while current < this.length and not c.contains(this[current]) current is current + 1 endwhile result[count] is this[begin...current] count is count + 1 current is current + 1 begin is current endwhile result enddef # String utility methods. def $String.startsWith|s| this[0...s.length] eq s enddef def $String.endsWith|s| this[this.length - s.length...this.length] eq s enddef def $String.eachLine |f| this.split("\n").each(f) enddef def $String.chomp|s| unless s s is "\n" endunless if this.endsWith(s) this[0...this.length - s.length] else this endif enddef def $String.contains |c| count is 0 while count < this.length if this[count] eq c return true endif count is count + 1 endwhile return false enddef # The mapping iterator def $Object.map |f first last| result is [] this.each( { |element| result.append!(f(element)) } first last) result enddef # The filter iterator def $Object.filter |f first last| result is [] this.each({|element| if f(element) result.append!(element) endif} first last) result end def $Object.filterIndices |f first last| result is [] this.eachWithIndex({ |element idx| if f(element) result.append!(idx) endif } first last) result end # The accumulator def $Object.accumulate |f init first last| result is init this.each({|x| result is f(result, x)} first last) result enddef # Makes using Java classes a bit easier. def java |name| classFound is false try return __javaclass name rescue $IMPORT.each( { |path| try result is __javaclass path + "." + name classFound is true break # note: return would jump into each, not out of it altogether rescue endtry } ) endtry if classFound result else throw "Cannot find class " + name endif enddef # We get a dictionary class for free... Dictionary is java("java.util.HashMap") # ...just have to override these methods to allow indexing. def Dictionary.getIndex |key| this.get(key) enddef def Dictionary.setIndex |key value| this.put(key, value) enddef def Dictionary.delIndex |key| this.remove(key) enddef # Similarly, we get the set class. Set is java("java.util.HashSet") def Set.iterator this.iterator() enddef # Appends the other object in the end of this list. def $Object.append! |other| if this instanceof $List # grow the list in place without creating a copy this[this.length] is other this else if other instanceof $List [this] + [other] else [this] + other endif endif enddef # Appends this object and the other object into a new list def $Object.append |other| if this instanceof $List if other instanceof $List this + [other] else this + other endif else if other instanceof $List [this] + [other] else [this] + other endif endif enddef # Let's try recursion, just to see if the Zilk call mechanism and list addition # work correctly. We'll come up with a more efficient version later. def $List.flatten if(this = []) return [] endif head is this[0] if head instanceof $List head is head.flatten() else head is [head] endif if(this.length > 1) head + (this[1...this.length].flatten()) else head endif enddef def $List.flatten! this[0...this.length] is this.flatten() enddef def $Object.find |key first last| result is false this.each({ |element| if element = key result is true break endif } first last) result enddef def $Object.findIndices |key first last| result is [] this.eachWithIndex({ |element idx| if element = key result.append!(idx) endif } first last) result enddef # Convert a list to a string. def $List.toString result is $LISTLEFTBRACKET separator is $LISTSEPARATOR last is this.length - 1 this.eachWithIndex({ |element idx| if(element instanceof $List) result is result + element.toString() else result is result + element endif if idx < last result is result + separator endif }) result + $LISTRIGHTBRACKET enddef # The method join as in Ruby. I know, it's almost the same as $List.toString. def $List.join |separator| result is "" unless separator separator is $LISTSEPARATOR endunless last is this.length - 1 this.eachWithIndex({ |element idx| if element instanceof $List result is result + element.toString() else result is result + element endif if idx < last result is result + separator endif }) result enddef def $Object.call |name| this.__call(name, *rest) enddef # A delegate class somewhat in the style of C#. class Delegate def initialize @callList is rest enddef def toFunction tmpRest is rest @callList.each({|f| f(*tmpRest)}) enddef def addMethod |f| @callList[@callList.length] is f enddef def removeMethod |f| count is 0 while(count < @callList.length) if(@callList[count] = f) del @callList[count] return f endif count is count + 1 endwhile nil enddef endclass System is java("java.lang.System") $STDIN is System.in $STDOUT is System.out InputStreamReader is java("InputStreamReader") BufferedReader is java("BufferedReader") unless $APPLET File is java("java.io.File") $PATH is System.getenv("PATH").split("" + File.pathSeparatorChar) endunless def BufferedReader.eachLine |f| line, result is 0, nil while nil neq line line is this.readLine() if nil neq line result is f(line) endif endwhile this.close() result enddef def BufferedReader.iterator class BufferedReaderIterator def initialize |fr| @fr is fr @c is @fr.read() enddef def hasNext @c != -1 enddef def next result is @c.char @c is @fr.read() if @c = -1 @fr.close() endif result enddef endclass BufferedReaderIterator(this) enddef $STDIOREADER is BufferedReader(InputStreamReader($STDIN)) def readLine $STDIOREADER.readLine() enddef def read result is $STDIOREADER.read() if result = -1 nil else result.char endif end # A friendly wrapper around the Zilk keyword __execute. Note that this has to # be a lambda so that the file is executed in the context of the caller. The # wrapper uses the global variable $PATH to look around if it cannot find the file. execute is lambda |filename| unless $APPLET f is File(filename) if(f.exists()) return __execute filename endif f is File(filename + $EXTENSION) if(f.exists()) return __execute filename + $EXTENSION endif count is 0 while(count < $PATH.length) fullname is $PATH[count] + File.separatorChar + filename f is File(fullname) if(f.exists()) return __execute fullname endif f is File(fullname + $EXTENSION) if(f.exists()) return __execute fullname + $EXTENSION endif count is count + 1 endwhile throw "File " + filename + " not found" else try return __execute filename rescue return __execute filename + $EXTENSION endtry endunless endlambda # A faster version of each specifically for $Range. def $Range.each |f| first, last, result is this.first, this.last, nil if first instanceof $Integer and last instanceof $Integer and first first is this.first - 1 if not this.includeEnd last is last - 1 endif while first < last first is first + 1 result is f(first) endwhile else throw "Iteration only works for integer ranges" endif result enddef # A faster version of each specifically for $List. def $List.each |f first last| unless first first is 0 endunless unless last last is this.length - 1 endunless count, result is first-1, nil while count < last count is count + 1 result is f(this[count]) endwhile result enddef # A simple interactive Zilk shell. Each line is executed as it is entered, except # that lines that end with \ are considered to continue from the next line. def irz println "Interactive Zilk prompt, 2005 Ilkka Kokkarinen" line is 0 while true print $PROMPT line is readLine() if line = nil break endif while line[-1] = '\\' print $PROMPT line is line.chomp("\\") + readLine() endwhile try p is compile line result is p() print "---> " println result rescue println "Error: " + caught endtry endwhile enddef # Construct a list from any given arguments. def $List.initialize tmpThis is this rest.each({|x| tmpThis.append!(x)}) enddef # Create an n-dimensional list recursively from given dimensions. def $List.create |size| unless size return nil endunless result is [] result[size - 1] if rest tmpRest is rest (0...size).each({ |x| result[x] is $List.create(*tmpRest) }) endif result enddef