From SRS0=rmLH=SE=canyon.socialtext.net=matthew@bounce2.pobox.com  Mon Jan 14 14:55:05 2008
Return-Path: <SRS0=rmLH=SE=canyon.socialtext.net=matthew@bounce2.pobox.com>
X-Original-To: kragen@canonical.org
Delivered-To: kragen@canonical.org
Received: from fence.pobox.com (fence.pobox.com [208.210.124.76])
	by panacea.canonical.org (Postfix) with ESMTP id 7AA8D1835B8
	for <kragen@canonical.org>; Mon, 14 Jan 2008 14:55:00 -0500 (EST)
Received: from fence.pobox.com (localhost [127.0.0.1])
	by fence.pobox.com (Postfix) with ESMTP id 99BB5473E2
	for <kragen@canonical.org>; Mon, 14 Jan 2008 14:55:17 -0500 (EST)
Delivered-To: kragen@pobox.com
X-Pobox-Delivery-ID:
 A11E7306-C2DA-11DC-99BA-7F3F1315FD8C-74472045!fence.pobox.com
x-pobox-client-address: 66.92.2.173
x-pobox-client-name: dsl092-002-173.sfo1.dsl.speakeasy.net
Received: from canyon.socialtext.net (dsl092-002-173.sfo1.dsl.speakeasy.net
 [66.92.2.173]) by fence.pobox.com (Postfix) with ESMTP id 39444474AE for
 <kragen@pobox.com>; Mon, 14 Jan 2008 14:52:13 -0500 (EST)
Received: from matthew by canyon.socialtext.net with local (Exim 4.60)
 (envelope-from <matthew@canyon.socialtext.net>) id 1JEVLI-0006zL-59 for
 kragen@pobox.com; Mon, 14 Jan 2008 11:51:32 -0800
To: kragen@pobox.com
From: matthew@canonical.org
Subject: darcs patch: Modified vim file per some suggestions f... (and 4 more)
X-Mail-Originator: Darcs Version Control System
X-Darcs-Version: 1.0.5 (release)
DarcsURL: http://www.canonical.org/~kragen/sw/bicicleta/
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="=_"
Message-Id: <E1JEVLI-0006zL-59@canyon.socialtext.net>
Date: Mon, 14 Jan 2008 11:51:32 -0800

--=_
Content-Type: text/plain
Content-Transfer-Encoding: quoted-printable

Tue Apr 24 18:11:56 PDT 2007  matthew@canonical.org
  * Modified vim file per some suggestions from Kragen.  Made operator dete=
ction better, highlight self-names, and do not make quoted method names loo=
k like strings.

Fri Apr 27 20:42:32 PDT 2007  matthew@canonical.org
  * Add parse_file function and make bicicleta_run_script use it.  Ditch th=
e read_file thingy

Fri Apr 27 20:43:46 PDT 2007  matthew@canonical.org
  * Make 4.0.show DTRT.  Wrapped string_to_float and tacked on a 0 if it en=
ds in a dot

Fri Apr 27 20:52:26 PDT 2007  matthew@canonical.org
  * Move the core bicicleta code into its own file.  This relies on the par=
se_file patch earlier.

Sat Apr 28 11:30:57 PDT 2007  matthew@canonical.org
  * Fix comment highlighting.  This conflicts w/ operators using the # symb=
ol, causing operators using # to not get highlighted right.  However, this =
optimizes for the common case.

--=_
Content-Type: text/x-darcs-patch
Content-Transfer-Encoding: quoted-printable
Content-Description: A darcs patch for your repository!


New patches:

[Modified vim file per some suggestions from Kragen.  Made operator detecti=
on better, highlight self-names, and do not make quoted method names look l=
ike strings.
matthew@canonical.org**20070425011156] {
hunk ./bicicleta.vim 5
-" License: none.  public domain
+" License:      None.  Public Domain
hunk ./bicicleta.vim 28
-syn match bicicletaOperator "[+\-*@%/><]\+" display
+" This crazy regex was taken from the bicicleta lexer.  This would be easi=
er
+" to read if I could use variables in regexes.  Not sure if I can.
+syn match bicicletaOperator "\([\-\~\`\!\@\#\%\^\&\*\+\<\>\?\/\|\\]\)\|\(\=
([\-\~\`\!\@\#\%\^\&\*\+\<\>\?\/\|\\]\|=3D\)\([\-\~\`\!\@\#\%\^\&\*\+\<\>\?=
\/\|\\]\|=3D\)\+\)" display
hunk ./bicicleta.vim 36
-syn region bicicletaString matchgroup=3DbicicletaStringDelimiter start=3D"=
'" end=3D"'"  skip=3D"\\\\\|\\'" contains=3DbicicletaSpecialChar,bicicletaS=
pecialCharError,bicicletaInterpolation keepend
+syn region bicicletaString matchgroup=3DbicicletaStringDelimiter start=3D"=
\(.\)\@<!'" end=3D"'"  skip=3D"\\\\\|\\'" contains=3DbicicletaSpecialChar,b=
icicletaSpecialCharError,bicicletaInterpolation keepend
hunk ./bicicleta.vim 52
-" The "self" name for a given object.
+" The "self" name for a given object, e.g. {self: ...}
hunk ./bicicleta.vim 61
-syn region bicicletaMethod matchgroup=3DbicicletaStringDelimiter start=3D"=
'" end=3D"'\(\s*=3D\(=3D\)\@!\)\@=3D" skip=3D"\\\\\|\\'"  oneline fold
+syn region bicicletaMethod start=3D"'" end=3D"'\(\s*=3D\(=3D\)\@!\)\@=3D" =
skip=3D"\\\\\|\\'"  oneline fold
+
+" Highlight the self-name specially, since it's most important.  E.g.
+" self.foo.bar.baz.  self is a different color.
+syn match bicicletaObjectUse "\([a-zA-Z0-9_.]\+\)\@\<!\([a-zA-Z0-9_]\+\(\.=
\)\@=3D\)"
hunk ./bicicleta.vim 104
+  HiLink bicicletaObjectUse         Type
+
}

[Add parse_file function and make bicicleta_run_script use it.  Ditch the r=
ead_file thingy
matthew@canonical.org**20070428034232] {
hunk ./bicicleta.ml 281
-let parse string =3D Bicicleta_parser.main =

-  (drop_unnecessary_newlines Bicicleta_lexer.next)
-  (Lexing.from_string string) ;;
+let _parser_wrapper =3D
+        Bicicleta_parser.main
+        (drop_unnecessary_newlines Bicicleta_lexer.next) ;;
+
+let parse string =3D _parser_wrapper (Lexing.from_string string) ;;
+let parse_file filename =3D
+    let file =3D open_in_bin filename in
+        _parser_wrapper (Lexing.from_channel file) ;;
+
hunk ./bicicleta_run_script.ml 26
-(* wtf? There's no way to read the entire contents of a file in the
-   standard library? *)
-let buf =3D String.create 4096 ;;
-let rec read_file inp string =3D
-  let nbytes =3D input inp buf 0 (String.length buf) in
-    if nbytes =3D 0 then string =

-    else read_file inp (string ^ (String.sub buf 0 nbytes)) ;;
-
hunk ./bicicleta_run_script.ml 27
-  let program =3D read_file (open_in_bin Sys.argv.(1)) "" =

-  in let parsed =3D parse program
+  let parsed =3D parse_file Sys.argv.(1)
}

[Make 4.0.show DTRT.  Wrapped string_to_float and tacked on a 0 if it ends =
in a dot
matthew@canonical.org**20070428034346] {
hunk ./bicicleta_lib.ml 547
+let my_float_to_string f =3D =

+    let str_f =3D string_of_float f in
+    let l =3D String.length str_f in
+    if l > 0 && str_f.[l-1] =3D '.' then str_f ^ "0" else str_f
+
hunk ./bicicleta_lib.ml 554
-    UserData (UserString (string_of_float x))))
+    UserData (UserString (my_float_to_string x))))
hunk ./bicicleta_lib.ml 711
+    assert(lib_eval_is "4.0.show" (UserString "4.0"));
+    assert(lib_eval_is "0.0.show" (UserString "0.0"));
}

[Move the core bicicleta code into its own file.  This relies on the parse_=
file patch earlier.
matthew@canonical.org**20070428035226] {
hunk ./bicicleta_lib.ml 32
-let prog_sys_source =3D "{prog: =

-# Collect: map+filter, in a more listcompy shape.
-# WORDY! Uck!  Avoids prog.if because prog.if depends on collect.
-collect =3D {collect: arg1 =3D collect.item,
-    cursor =3D collect.for_each.cursor
-    item =3D collect.cursor.item
-    where =3D prog.sys.bool.true
-    next =3D collect { cursor =3D collect.cursor.advanced }
-    '()' =3D collect.cursor.empty.if_true(
-        then =3D collect.cursor
-        else =3D collect.where.if_true(
-            then =3D collect.arg1 @ collect.next()
-            else =3D collect.next()))
-}
-# General conditional construct.
-if =3D {if:
-    alternatives =3D if.'reflective positional arguments'
-    else =3D prog.error(\"no default case given\")
-    '()' =3D if.alternatives.empty.if_true(
-        then =3D if.else
-        else =3D if.alternatives.item.key.if_true(
-            then =3D if.alternatives.item.value
-            else =3D if(alternatives =3D if.alternatives.cursor.advanced))=
)
-}
-# General loop.
-loop =3D {loop:
-    '()' =3D prog.if(loop.while -> loop(prev =3D loop), else =3D loop.prev=
.result)
-    show =3D prog.if(
-        loop.while -> (loop.result.show + \"; \" + loop{prev =3D loop}.sho=
w)
-        else =3D \"\"
-    )
-}
-sys =3D {
-list =3D {op: '()' =3D op.'reflective positional arguments'}
-object =3D intrinsics.proto_object {self:
-        # One day, things will inherit from this implicitly if not =

-        # otherwise specified.
-        # These two methods are intended to be defined differently on erro=
rs.
-        '!!' =3D {op: '()' =3D self}
-        is_ok =3D prog.sys.bool.true
-        # But not these.
-        '->' =3D {op: arg1 =3D self, '()' =3D {key =3D self, value =3D op.=
arg1} }
-        'reflective positional slot names' =3D =

-            intrinsics{self, prog=3Dprog}.'reflective positional slot name=
s'
-        'reflective slot value' =3D {op: arg1=3D\"x\",
-            '()' =3D intrinsics{self, op.arg1.userdata}.'reflective slot v=
alue'}
-        'reflective positional arguments' =3D prog.collect(f:
-            self.'reflective slot value'(f.item), =

-            for_each =3D self.'reflective positional slot names')
-        # For now we're spelling the 'cons' operation with '@'.
-        '@' =3D {'@': prog.sys.nil
-            '()' =3D prog.sys.nil {cons:
-                empty =3D prog.sys.bool.false
-                item =3D self
-                advanced =3D '@'.arg1
-                '++' =3D prog.sys.nil.'++' {op: =

-                    '()' =3D cons.item @ (cons.advanced ++ op.arg1)
-                }
-                show_list_tail =3D prog.if(
-                    cons.advanced.empty -> (cons.item.show + \"]\")
-                    else =3D cons.item.show + \", \" + =

-                        cons.advanced.show_list_tail
-                )
-                show =3D \"[\" + cons.show_list_tail
-            }
-        }
-        show =3D \";object;\"  # for now
-    }
-nil =3D {self:
-        cursor =3D self
-        empty =3D prog.sys.bool.true
-        '++' =3D {op: self, '()' =3D op.arg1}
-        show =3D \"[]\"
-        show_list_tail =3D \"]\"
-    }
-bool =3D
-    {bool: =

-        true =3D {self: =

-            if_true =3D {if:
-                '()' =3D if.then
-                then =3D prog.error(\"no consequent given\")
-                else =3D prog.error(\"no alternate given\")
-            }
-            not =3D bool.false
-            if_false =3D self.not.if_true
-            '||' =3D {or: '()' =3D self}
-            '&&' =3D {and: arg1 =3D self, '()' =3D and.arg1}
-            show =3D \"true\"
-        }
-        false =3D bool.true {self:
-            if_true =3D bool.true.if_true{if: '()' =3D if.else}
-            not =3D bool.true
-            '||' =3D {or: arg1 =3D self, '()' =3D or.arg1}
-            '&&' =3D {and: '()' =3D self}
-            show =3D \"false\"
-        }
-    }
-
-# prog.sys.normal_commutative_number:
-
-# This is a base class for number-like things that support the usual
-# commutative numerical operations, such as native integers, integers
-# in Z_n, floating-point numbers, rational numbers, vectors,
-# polynomials, rational expressions, continued fractions, complex
-# numbers, sampled audio signals, dates, time intervals, images,
-# multidimensional arrays, and so on.
-
-# Here's a table of the levels of support you can provide to the
-# methods herein.
-
-# If you implement:     You get proper support for:
-# add                   +
-# add, negated          +, negated, -
-# multiply              *
-# multiply, reciprocal  *, reciprocal, /
-# power                 **
-# modulo                %
-# less_than             <, =3D=3D, >, <=3D, >=3D, !=3D, between
-# less_than, coerce(0)  <, =3D=3D, >, <=3D, >=3D, !=3D, between, abs, nega=
tive, positive
-
-# You can also override 'subtract', 'divide', 'equals', and
-# 'greaterthan' to get the expected effects; there are default
-# implementations built from the above six methods.  If you do that,
-# you may want to override 'reverse -' and 'reverse /' as well.
-
-# If you override the 'coerce' method to convert objects to some type
-# your arithmetic methods know how to support, it will convert them to
-# the right type ahead of time.
-
-# The 'coerce' method need not return objects supporting the same
-# protocol as your own, although they need to support 'negated' if you
-# want to subtract them, and they need to support 'reciprocal' if you
-# want to divide by them.  So the 'coerce' method for a date object
-# might coerce things to time intervals and then support + and - with
-# them.  By the same token, your arithmetic methods might return
-# objects of some other type --- but remember that 'negated' might get
-# fed to 'add', and 'reciprocal' might get fed to 'multiply'.
-
-# In order to support arithmetic on either side of the operator, the
-# operator methods, such as '+', delegate to the 'reverse ...' method
-# on the other object if the 'add' method fails --- and they provide a
-# 'reverse +' method that does the right thing, in case the other
-# object wants to delegate to it.  However, the 'reverse ...' methods
-# depend on the operations being commutative; this is in exchange for
-# the relatively loose requirements on 'coerce' described above.
-
-# No implementations are provided for 'reverse **' and 'reverse %'.
-
-normal_commutative_number =3D {self:
-        coerce =3D {op: arg1 =3D 1, '()' =3D op.arg1}
-        binop =3D {op: arg1 =3D 1, other =3D self.coerce(op.arg1)}
-
-        normal_binop =3D self.binop {op:
-            op =3D self.add
-            revop =3D op.arg1.'reverse +'
-            result =3D op.op(op.other)
-            commuted =3D op {op: '()' =3D op.result}
-            '()' =3D op.result !! op.revop(self)
-        }
-        '+' =3D self.normal_binop
-        'reverse +' =3D self.'+'.commuted
-        subtract =3D {op: arg1=3D2, '()' =3D self.add(op.arg1.negated)}
-        '-' =3D self.normal_binop {op: op =3D self.subtract, =

-            revop =3D op.arg1.'reverse -'}
-        'reverse -' =3D self.binop {op: '()' =3D self.negated.add(op.other=
)}
-
-        '*' =3D self.normal_binop {op: op =3D self.multiply, =

-            revop =3D op.arg1.'reverse *' }
-        'reverse *' =3D self.'*'.commuted
-        divide =3D {op: arg1=3D2, '()' =3D self.multiply(op.arg1.reciproca=
l)}
-        '/' =3D self.normal_binop {op: op =3D self.divide, =

-            revop =3D op.arg1.'reverse /'}
-        'reverse /' =3D self.binop {op: '()' =3D self.reciprocal.multiply(=
op.other)}
-
-        '%' =3D self.normal_binop {op: op =3D self.modulo, =

-            revop =3D op.arg1.'reverse %' }
-        '**' =3D self.normal_binop {op: op =3D self.power, =

-            revop =3D op.arg1.'reverse **'}
-
-        '<' =3D self.normal_binop {op: op =3D self.less_than, =

-            revop =3D op.arg1.'reverse <'}
-        greaterthan =3D {op: '()' =3D op.arg1 < self}
-        '>' =3D self.normal_binop {op: op =3D self.greaterthan, =

-            revop =3D op.arg1.'reverse >'}
-        'reverse <' =3D self.'>'.commuted
-        'reverse >' =3D self.'<'.commuted
-        # BEWARE! As the Common Lisp HyperSpec says about EQUALP:
-        # Object equality is not a concept for which there is a uniquely
-        # determined correct algorithm. The appropriateness of an equality
-        # predicate can be judged only in the context of the needs of some
-        # particular program. Although these functions take any type of
-        # argument and their names sound very generic, equal and equalp
-        # are not appropriate for every application.
-        equals =3D {op: arg1 =3D 2
-            '()' =3D (self < op.arg1).not && (self > op.arg1).not}
-        '=3D=3D' =3D self.normal_binop {op: op =3D self.equals, =

-            revop =3D op.arg1.'reverse =3D=3D'}
-        'reverse =3D=3D' =3D self.'=3D=3D'.commuted
-        inverse_comparator =3D self.normal_binop {op:
-            baseop =3D self.less_than
-            revop =3D op.arg1.'reverse >=3D'
-            op =3D {ge: '()' =3D op.baseop(ge.arg1).not}
-        }
-        '>=3D' =3D self.inverse_comparator
-        '<=3D' =3D self.inverse_comparator {op: baseop =3D self.greatertha=
n, =

-            revop =3D op.arg1.'reverse <=3D'}
-        'reverse >=3D' =3D self.'<=3D'.commuted
-        'reverse <=3D' =3D self.'>=3D'.commuted
-        '!=3D' =3D self.inverse_comparator {op: baseop =3D self.'=3D=3D',
-            revop =3D op.arg1.'reverse !=3D'}
-        'reverse !=3D' =3D self.'!=3D'.commuted
-        between =3D {op: arg1 =3D 2.negated, arg2 =3D 2
-            '()' =3D (self >=3D op.arg1) && (self < op.arg2)}
-        negative =3D self < 0
-        positive =3D self.negative.not
-        abs =3D self.negative.if_true(then=3Dself.negated, else=3Dself)
-    }
-
-# XXX Doesn't work because we don't have integer divmod yet.
-rational =3D =

-    prog.sys.normal_commutative_number {self:
-        numer =3D 1
-        denom =3D 2
-        new =3D {op: arg1 =3D 6, arg2 =3D 9,
-            g =3D op.arg1.gcd(op.arg2)
-            numer =3D op.arg1 / op.g
-            denom =3D op.arg2 / op.g
-            denom_is_1 =3D op.denom.is_ok && (op.denom =3D=3D 1)
-            ok_test =3D (op.numer * op.denom)
-            '()' =3D prog.if(
-                op.denom_is_1 -> op.numer,
-                op.ok_test.is_ok -> self { numer =3D op.numer, denom =3D o=
p.denom },
-                else =3D op.ok_test)
-        }
-        show =3D \"{numer} <hr> {denom}\" % self
-        coerce =3D {op: arg1 =3D 2
-            '()' =3D prog.if(
-                op.arg1.denom.is_ok -> op.arg1,
-                op.arg1.as_integer.is_ok -> self.new(op.arg1.as_integer, 1=
),
-                else =3D prog.error(\"could not coerce {arg1} to rational\=
" % op)
-            )
-        }
-        add =3D {op: arg1 =3D self.new(2, 3),
-            '()' =3D self.new(
-                (self.numer * op.arg1.denom) + (self.denom * op.arg1.numer=
),
-                self.denom * op.arg1.denom
-            )
-        }
-        negated =3D self.new(self.numer.negated, self.denom)
-        multiply =3D self.add {op:
-            '()' =3D self.new(self.numer * op.arg1.numer, =

-                            self.denom * op.arg1.denom)
-        }
-        reciprocal =3D self.new(self.denom, self.numer)
-        less_than =3D {op: arg1 =3D self.new(2, 4),
-            '()' =3D (self.numer * op.arg1.denom) < (self.denom * op.arg1.=
numer)
-        }
-        as_float =3D self.numer.as_float / self.denom.as_float
-        as_integer =3D prog.if(self.denom =3D=3D 1, =

-            then=3Dself.numer,
-            else=3Dprog.error(\"{frac} is not an integer\" % {frac=3Dself}=
))
-    }
-
-# prog.sys.native_integer:
-
-# The idea is that the interpreter will derive things from this object
-# by overriding their \"userdata\" to point to an opaque integer
-# object that can be passed to the native methods that do show,
-# as_float, add, negated, multiply, divmod, and less_than.
-
-# I'm still undecided about what those operations should return ---
-# just the userdata result (making this code a little uglier) or the
-# native_integer object (in which case the primitive arithmetic
-# operations as well as the parser have to know about the
-# prog.sys.native_integer name)?  For now I'm going with the former.
-
-native_integer =3D
-    prog.sys.normal_commutative_number {self:
-        userdata =3D 5.userdata # as an example
-        new =3D {new: arg1 =3D 6.userdata
-            '()' =3D prog.sys.native_integer { userdata =3D new.arg1 }}
-        intrinsics =3D intrinsics{self.userdata}
-        show =3D prog.sys.native_string.new(self.intrinsics.integer_show)
-        as_integer =3D self
-        as_float =3D prog.sys.native_float.new(self.intrinsics.integer_as_=
float)
-        coerce =3D {op: arg1 =3D 2, '()' =3D op.arg1.as_integer}
-        intrinsic_op =3D {op: arg1 =3D 3,
-            intrinsics =3D self.intrinsics {
-                arg2 =3D op.arg1.userdata
-                # these two are for less_than:
-                true =3D prog.sys.bool.true
-                false =3D prog.sys.bool.false
-            }
-            userdata =3D op.intrinsics.integer_add
-            '()' =3D self.new(op.userdata)   # by default
-        }
-        add =3D self.intrinsic_op
-        negated =3D self.new(self.intrinsics.integer_negated)
-        multiply =3D self.intrinsic_op {op:
-            userdata =3D op.intrinsics.integer_multiply
-        }
-        modulo =3D self.intrinsic_op {op:
-            userdata =3D op.intrinsics.integer_divmod.mod
-        }
-        # As Jamie McCarthy points out, rationals made out of machine
-        # integers are really pretty limited... but for now I'm using them
-        # anyway.
-        divide =3D {op: arg1 =3D 2, '()' =3D prog.sys.rational.new(self, o=
p.arg1)}
-        reciprocal =3D 1 / self
-        gcd =3D self.binop {op: arg1 =3D 5
-            '()' =3D (op.arg1 =3D=3D 0).if_true(then =3D self, =

-                else =3D op.arg1.gcd(self % op.arg1))
-        }
-        less_than =3D self.intrinsic_op {op:
-            '()' =3D op.intrinsics.integer_less_than
-        }
-
-        # Here are some faster versions of ops with less indirection, =

-        # but which fail badly in mixed-mode arithmetic.
-        #'+' =3D {op: '()' =3D self.new(
-        #    intrinsics{self.userdata, op.arg1.userdata}.integer_add)}
-        #'-' =3D {op: '()' =3D self.new(
-        #    intrinsics{self.userdata, op.arg1.userdata}.integer_subtract)=
}
-        #'<' =3D {op:
-        #    '()' =3D intrinsics{
-        #        self.userdata, op.arg1.userdata,
-        #        true=3Dself.true,
-        #        false=3Dself.false
-        #    }.integer_less_than
-        #}
-    }
-
-# The float userdata has divide instead of modulo; otherwise it's
-# quite similar to the integer.  My original design let it inherit
-# pretty much all the methods, because the primitives were properties
-# of the userdata, so they were named things like 'show' instead of
-# 'integer_show', but I decided sticking all the operations in an
-# 'intrinsics' namespace was simpler.
-
-native_float =3D prog.sys.native_integer {self:
-        userdata =3D 3.2.userdata
-        new =3D {new: arg1 =3D 3.5.userdata
-            '()' =3D prog.sys.native_float { userdata =3D new.arg1 }}
-        show =3D prog.sys.native_string.new(self.intrinsics.float_show)
-        as_float =3D self
-        as_integer =3D prog.error(\"Can't coerce floats to integers\")
-        modulo =3D prog.error(\"Can't take modulo of floats yet\")
-        coerce =3D {op: arg1 =3D 2, '()' =3D op.arg1.as_float}
-        add =3D self.intrinsic_op {op:
-            userdata =3D op.intrinsics.float_add
-        }
-        negated =3D self.new(self.intrinsics.float_negated)
-        multiply =3D self.intrinsic_op {op:
-            userdata =3D op.intrinsics.float_multiply
-        }
-        divide =3D self.intrinsic_op {op:
-            userdata =3D op.intrinsics.float_divide
-        }
-        less_than =3D self.intrinsic_op {op:
-            '()' =3D op.intrinsics.float_less_than
-        }
-    }
-
-native_string =3D {self:
-        userdata =3D \"bethmolnar\".userdata
-        new =3D {new: arg1 =3D \"matthew\".userdata
-            '()' =3D prog.sys.native_string { userdata =3D new.arg1 }}
-        show =3D self
-        intrinsic_op =3D {op: arg1 =3D \"kafka\",
-            intrinsics =3D intrinsics{ self.userdata, op.arg1.userdata }
-            userdata =3D op.intrinsics.string_concat
-            '()' =3D self.new(op.userdata)   # by default
-        }
-        '+' =3D self.intrinsic_op
-        '[]' =3D self.intrinsic_op {op: arg1=3D0, =

-            userdata =3D op.intrinsics.string_index
-        }
-        '=3D=3D' =3D {op:
-            '()' =3D intrinsics {
-                arg1 =3D self.userdata
-                arg2 =3D op.arg1.userdata
-                true =3D prog.sys.bool.true
-                false =3D prog.sys.bool.false
-            }.string_equal
-        }
-        length =3D self.intrinsic_op(op: =

-            '()' =3D prog.sys.native_integer.new(op.intrinsics.string_leng=
th)
-        )
-        cursor =3D {c: =

-            ii =3D 0
-            empty =3D c.ii >=3D self.length
-            item =3D self[c.ii]
-            advanced =3D c { ii =3D c.ii + 1 }
-            cursor =3D c
-        }
-        join =3D {join: arg1 =3D prog.sys.list(\"x\", \"y\", \"z\")
-            list =3D join.arg1.cursor
-            '()' =3D prog.if(join.list.empty -> \"\", else =3D join.nonemp=
ty_case)
-            nonempty_case =3D prog.loop(loop: while =3D loop.iter.empty.no=
t
-                result =3D loop.prev.result + self + loop.iter.item
-                prev =3D { result =3D join.list.item, iter =3D join.list }
-                iter =3D loop.prev.iter.advanced
-            )
-        }
-        # XXX this code is horrible:
-        '%' =3D {op: arg1 =3D {this=3D\"example\"}
-            '()' =3D \"\".join(prog.collect(c: c.item.render, for_each=3Do=
p.tokens))
-            lit_token =3D {t: arg1 =3D \"bubble wrap\", render =3D t.arg1}
-            var_token =3D {t: arg1 =3D \"this\",
-                render =3D op.arg1.'reflective slot value'(t.arg1).show}
-            # XXX rewrite this without explicit recursion?
-            tokens =3D {t: ip =3D self.cursor # iter position
-                parse =3D prog.if(
-                    (t.ip.item =3D=3D \"{\") -> t.parse_name,  # }
-                    else =3D t.parse_lit
-                )
-                cursor =3D t
-                item =3D t.parse.item
-                empty =3D t.ip.empty
-                advanced =3D op.tokens { ip =3D t.parse.ip }
-                parse_lit =3D {pl:
-                    pos =3D prog.sys.find(\"{\", in =3D t.ip)
-                    item =3D op.lit_token { \"\".join(pl.pos.before) }
-                    ip =3D pl.pos.after
-                }
-                next_char =3D t.ip.advanced.item
-                parse_name =3D prog.if(
-                    # special case for {{} and {}} (to represent literal {=
 })
-                    ((t.next_char =3D=3D \"{\") || (t.next_char =3D=3D \"}=
\")) ->
-                        {lit: closer =3D t.ip.advanced.advanced  # {
-                            rv =3D prog.if((lit.closer.item =3D=3D \"}\") =
-> {
-                                ip =3D lit.closer.advanced
-                                item =3D op.lit_token { t.next_char }
-                            }
-                            else =3D prog.error(\"faulty format\", \"\".jo=
in(t.ip)))
-                            ip =3D lit.rv.ip
-                            item =3D lit.rv.item
-                        }
-                    # normal case    =

-                    else =3D {pn:  # {
-                        # XXX are we sure t.ip can't be empty??
-                        end =3D prog.sys.find(\"}\", in =3D t.ip.advanced)
-                        ip =3D prog.if(
-                            pn.end.after.empty.not -> pn.end.after.advance=
d
-                            else =3D prog.sys.nil
-                        )
-                        item =3D op.var_token { \"\".join(pn.end.before) }
-                    }
-                )
-            }
-        }
-    }
-find =3D {find: arg1 =3D \"x\", in =3D \"wxyz\"
-        if =3D find.arg1.'=3D=3D'
-        cursor =3D find.in.cursor
-        advanced =3D find(cursor =3D find.cursor.advanced)
-        '()' =3D prog.if(
-            (find.cursor.empty || find.if(find.cursor.item)) -> {
-                after =3D find.cursor
-                before =3D prog.sys.nil
-            }
-            else =3D {
-                after =3D find.advanced.after
-                before =3D find.cursor.item @ find.advanced.before
-            }
-        )
-    }
-
-}}" ;;
-
hunk ./bicicleta_lib.ml 169
-  (parse prog_sys_source) ;;
+  (parse_file "./prog.bicicleta") ;;
addfile ./prog.bicicleta
hunk ./prog.bicicleta 1
+{prog: =

+# Collect: map+filter, in a more listcompy shape.
+# WORDY! Uck!  Avoids prog.if because prog.if depends on collect.
+collect =3D {collect: arg1 =3D collect.item,
+    cursor =3D collect.for_each.cursor
+    item =3D collect.cursor.item
+    where =3D prog.sys.bool.true
+    next =3D collect { cursor =3D collect.cursor.advanced }
+    '()' =3D collect.cursor.empty.if_true(
+        then =3D collect.cursor
+        else =3D collect.where.if_true(
+            then =3D collect.arg1 @ collect.next()
+            else =3D collect.next()))
+}
+# General conditional construct.
+if =3D {if:
+    alternatives =3D if.'reflective positional arguments'
+    else =3D prog.error("no default case given")
+    '()' =3D if.alternatives.empty.if_true(
+        then =3D if.else
+        else =3D if.alternatives.item.key.if_true(
+            then =3D if.alternatives.item.value
+            else =3D if(alternatives =3D if.alternatives.cursor.advanced))=
)
+}
+# General loop.
+loop =3D {loop:
+    '()' =3D prog.if(loop.while -> loop(prev =3D loop), else =3D loop.prev=
.result)
+    show =3D prog.if(
+        loop.while -> (loop.result.show + "; " + loop{prev =3D loop}.show)
+        else =3D ""
+    )
+}
+sys =3D {
+list =3D {op: '()' =3D op.'reflective positional arguments'}
+object =3D intrinsics.proto_object {self:
+        # One day, things will inherit from this implicitly if not =

+        # otherwise specified.
+        # These two methods are intended to be defined differently on erro=
rs.
+        '!!' =3D {op: '()' =3D self}
+        is_ok =3D prog.sys.bool.true
+        # But not these.
+        '->' =3D {op: arg1 =3D self, '()' =3D {key =3D self, value =3D op.=
arg1} }
+        'reflective positional slot names' =3D =

+            intrinsics{self, prog=3Dprog}.'reflective positional slot name=
s'
+        'reflective slot value' =3D {op: arg1=3D"x",
+            '()' =3D intrinsics{self, op.arg1.userdata}.'reflective slot v=
alue'}
+        'reflective positional arguments' =3D prog.collect(f:
+            self.'reflective slot value'(f.item), =

+            for_each =3D self.'reflective positional slot names')
+        # For now we're spelling the 'cons' operation with '@'.
+        '@' =3D {'@': prog.sys.nil
+            '()' =3D prog.sys.nil {cons:
+                empty =3D prog.sys.bool.false
+                item =3D self
+                advanced =3D '@'.arg1
+                '++' =3D prog.sys.nil.'++' {op: =

+                    '()' =3D cons.item @ (cons.advanced ++ op.arg1)
+                }
+                show_list_tail =3D prog.if(
+                    cons.advanced.empty -> (cons.item.show + "]")
+                    else =3D cons.item.show + ", " + =

+                        cons.advanced.show_list_tail
+                )
+                show =3D "[" + cons.show_list_tail
+            }
+        }
+        show =3D ";object;"  # for now
+    }
+nil =3D {self:
+        cursor =3D self
+        empty =3D prog.sys.bool.true
+        '++' =3D {op: self, '()' =3D op.arg1}
+        show =3D "[]"
+        show_list_tail =3D "]"
+    }
+bool =3D
+    {bool: =

+        true =3D {self: =

+            if_true =3D {if:
+                '()' =3D if.then
+                then =3D prog.error("no consequent given")
+                else =3D prog.error("no alternate given")
+            }
+            not =3D bool.false
+            if_false =3D self.not.if_true
+            '||' =3D {or: '()' =3D self}
+            '&&' =3D {and: arg1 =3D self, '()' =3D and.arg1}
+            show =3D "true"
+        }
+        false =3D bool.true {self:
+            if_true =3D bool.true.if_true{if: '()' =3D if.else}
+            not =3D bool.true
+            '||' =3D {or: arg1 =3D self, '()' =3D or.arg1}
+            '&&' =3D {and: '()' =3D self}
+            show =3D "false"
+        }
+    }
+
+# prog.sys.normal_commutative_number:
+
+# This is a base class for number-like things that support the usual
+# commutative numerical operations, such as native integers, integers
+# in Z_n, floating-point numbers, rational numbers, vectors,
+# polynomials, rational expressions, continued fractions, complex
+# numbers, sampled audio signals, dates, time intervals, images,
+# multidimensional arrays, and so on.
+
+# Here's a table of the levels of support you can provide to the
+# methods herein.
+
+# If you implement:     You get proper support for:
+# add                   +
+# add, negated          +, negated, -
+# multiply              *
+# multiply, reciprocal  *, reciprocal, /
+# power                 **
+# modulo                %
+# less_than             <, =3D=3D, >, <=3D, >=3D, !=3D, between
+# less_than, coerce(0)  <, =3D=3D, >, <=3D, >=3D, !=3D, between, abs, nega=
tive, positive
+
+# You can also override 'subtract', 'divide', 'equals', and
+# 'greaterthan' to get the expected effects; there are default
+# implementations built from the above six methods.  If you do that,
+# you may want to override 'reverse -' and 'reverse /' as well.
+
+# If you override the 'coerce' method to convert objects to some type
+# your arithmetic methods know how to support, it will convert them to
+# the right type ahead of time.
+
+# The 'coerce' method need not return objects supporting the same
+# protocol as your own, although they need to support 'negated' if you
+# want to subtract them, and they need to support 'reciprocal' if you
+# want to divide by them.  So the 'coerce' method for a date object
+# might coerce things to time intervals and then support + and - with
+# them.  By the same token, your arithmetic methods might return
+# objects of some other type --- but remember that 'negated' might get
+# fed to 'add', and 'reciprocal' might get fed to 'multiply'.
+
+# In order to support arithmetic on either side of the operator, the
+# operator methods, such as '+', delegate to the 'reverse ...' method
+# on the other object if the 'add' method fails --- and they provide a
+# 'reverse +' method that does the right thing, in case the other
+# object wants to delegate to it.  However, the 'reverse ...' methods
+# depend on the operations being commutative; this is in exchange for
+# the relatively loose requirements on 'coerce' described above.
+
+# No implementations are provided for 'reverse **' and 'reverse %'.
+
+normal_commutative_number =3D {self:
+        coerce =3D {op: arg1 =3D 1, '()' =3D op.arg1}
+        binop =3D {op: arg1 =3D 1, other =3D self.coerce(op.arg1)}
+
+        normal_binop =3D self.binop {op:
+            op =3D self.add
+            revop =3D op.arg1.'reverse +'
+            result =3D op.op(op.other)
+            commuted =3D op {op: '()' =3D op.result}
+            '()' =3D op.result !! op.revop(self)
+        }
+        '+' =3D self.normal_binop
+        'reverse +' =3D self.'+'.commuted
+        subtract =3D {op: arg1=3D2, '()' =3D self.add(op.arg1.negated)}
+        '-' =3D self.normal_binop {op: op =3D self.subtract, =

+            revop =3D op.arg1.'reverse -'}
+        'reverse -' =3D self.binop {op: '()' =3D self.negated.add(op.other=
)}
+
+        '*' =3D self.normal_binop {op: op =3D self.multiply, =

+            revop =3D op.arg1.'reverse *' }
+        'reverse *' =3D self.'*'.commuted
+        divide =3D {op: arg1=3D2, '()' =3D self.multiply(op.arg1.reciproca=
l)}
+        '/' =3D self.normal_binop {op: op =3D self.divide, =

+            revop =3D op.arg1.'reverse /'}
+        'reverse /' =3D self.binop {op: '()' =3D self.reciprocal.multiply(=
op.other)}
+
+        '%' =3D self.normal_binop {op: op =3D self.modulo, =

+            revop =3D op.arg1.'reverse %' }
+        '**' =3D self.normal_binop {op: op =3D self.power, =

+            revop =3D op.arg1.'reverse **'}
+
+        '<' =3D self.normal_binop {op: op =3D self.less_than, =

+            revop =3D op.arg1.'reverse <'}
+        greaterthan =3D {op: '()' =3D op.arg1 < self}
+        '>' =3D self.normal_binop {op: op =3D self.greaterthan, =

+            revop =3D op.arg1.'reverse >'}
+        'reverse <' =3D self.'>'.commuted
+        'reverse >' =3D self.'<'.commuted
+        # BEWARE! As the Common Lisp HyperSpec says about EQUALP:
+        # Object equality is not a concept for which there is a uniquely
+        # determined correct algorithm. The appropriateness of an equality
+        # predicate can be judged only in the context of the needs of some
+        # particular program. Although these functions take any type of
+        # argument and their names sound very generic, equal and equalp
+        # are not appropriate for every application.
+        equals =3D {op: arg1 =3D 2
+            '()' =3D (self < op.arg1).not && (self > op.arg1).not}
+        '=3D=3D' =3D self.normal_binop {op: op =3D self.equals, =

+            revop =3D op.arg1.'reverse =3D=3D'}
+        'reverse =3D=3D' =3D self.'=3D=3D'.commuted
+        inverse_comparator =3D self.normal_binop {op:
+            baseop =3D self.less_than
+            revop =3D op.arg1.'reverse >=3D'
+            op =3D {ge: '()' =3D op.baseop(ge.arg1).not}
+        }
+        '>=3D' =3D self.inverse_comparator
+        '<=3D' =3D self.inverse_comparator {op: baseop =3D self.greatertha=
n, =

+            revop =3D op.arg1.'reverse <=3D'}
+        'reverse >=3D' =3D self.'<=3D'.commuted
+        'reverse <=3D' =3D self.'>=3D'.commuted
+        '!=3D' =3D self.inverse_comparator {op: baseop =3D self.'=3D=3D',
+            revop =3D op.arg1.'reverse !=3D'}
+        'reverse !=3D' =3D self.'!=3D'.commuted
+        between =3D {op: arg1 =3D 2.negated, arg2 =3D 2
+            '()' =3D (self >=3D op.arg1) && (self < op.arg2)}
+        negative =3D self < 0
+        positive =3D self.negative.not
+        abs =3D self.negative.if_true(then=3Dself.negated, else=3Dself)
+    }
+
+# XXX Doesn't work because we don't have integer divmod yet.
+rational =3D =

+    prog.sys.normal_commutative_number {self:
+        numer =3D 1
+        denom =3D 2
+        new =3D {op: arg1 =3D 6, arg2 =3D 9,
+            g =3D op.arg1.gcd(op.arg2)
+            numer =3D op.arg1 / op.g
+            denom =3D op.arg2 / op.g
+            denom_is_1 =3D op.denom.is_ok && (op.denom =3D=3D 1)
+            ok_test =3D (op.numer * op.denom)
+            '()' =3D prog.if(
+                op.denom_is_1 -> op.numer,
+                op.ok_test.is_ok -> self { numer =3D op.numer, denom =3D o=
p.denom },
+                else =3D op.ok_test)
+        }
+        show =3D "{numer} <hr> {denom}" % self
+        coerce =3D {op: arg1 =3D 2
+            '()' =3D prog.if(
+                op.arg1.denom.is_ok -> op.arg1,
+                op.arg1.as_integer.is_ok -> self.new(op.arg1.as_integer, 1=
),
+                else =3D prog.error("could not coerce {arg1} to rational" =
% op)
+            )
+        }
+        add =3D {op: arg1 =3D self.new(2, 3),
+            '()' =3D self.new(
+                (self.numer * op.arg1.denom) + (self.denom * op.arg1.numer=
),
+                self.denom * op.arg1.denom
+            )
+        }
+        negated =3D self.new(self.numer.negated, self.denom)
+        multiply =3D self.add {op:
+            '()' =3D self.new(self.numer * op.arg1.numer, =

+                            self.denom * op.arg1.denom)
+        }
+        reciprocal =3D self.new(self.denom, self.numer)
+        less_than =3D {op: arg1 =3D self.new(2, 4),
+            '()' =3D (self.numer * op.arg1.denom) < (self.denom * op.arg1.=
numer)
+        }
+        as_float =3D self.numer.as_float / self.denom.as_float
+        as_integer =3D prog.if(self.denom =3D=3D 1, =

+            then=3Dself.numer,
+            else=3Dprog.error("{frac} is not an integer" % {frac=3Dself}))
+    }
+
+# prog.sys.native_integer:
+
+# The idea is that the interpreter will derive things from this object
+# by overriding their "userdata" to point to an opaque integer
+# object that can be passed to the native methods that do show,
+# as_float, add, negated, multiply, divmod, and less_than.
+
+# I'm still undecided about what those operations should return ---
+# just the userdata result (making this code a little uglier) or the
+# native_integer object (in which case the primitive arithmetic
+# operations as well as the parser have to know about the
+# prog.sys.native_integer name)?  For now I'm going with the former.
+
+native_integer =3D
+    prog.sys.normal_commutative_number {self:
+        userdata =3D 5.userdata # as an example
+        new =3D {new: arg1 =3D 6.userdata
+            '()' =3D prog.sys.native_integer { userdata =3D new.arg1 }}
+        intrinsics =3D intrinsics{self.userdata}
+        show =3D prog.sys.native_string.new(self.intrinsics.integer_show)
+        as_integer =3D self
+        as_float =3D prog.sys.native_float.new(self.intrinsics.integer_as_=
float)
+        coerce =3D {op: arg1 =3D 2, '()' =3D op.arg1.as_integer}
+        intrinsic_op =3D {op: arg1 =3D 3,
+            intrinsics =3D self.intrinsics {
+                arg2 =3D op.arg1.userdata
+                # these two are for less_than:
+                true =3D prog.sys.bool.true
+                false =3D prog.sys.bool.false
+            }
+            userdata =3D op.intrinsics.integer_add
+            '()' =3D self.new(op.userdata)   # by default
+        }
+        add =3D self.intrinsic_op
+        negated =3D self.new(self.intrinsics.integer_negated)
+        multiply =3D self.intrinsic_op {op:
+            userdata =3D op.intrinsics.integer_multiply
+        }
+        modulo =3D self.intrinsic_op {op:
+            userdata =3D op.intrinsics.integer_divmod.mod
+        }
+        # As Jamie McCarthy points out, rationals made out of machine
+        # integers are really pretty limited... but for now I'm using them
+        # anyway.
+        divide =3D {op: arg1 =3D 2, '()' =3D prog.sys.rational.new(self, o=
p.arg1)}
+        reciprocal =3D 1 / self
+        gcd =3D self.binop {op: arg1 =3D 5
+            '()' =3D (op.arg1 =3D=3D 0).if_true(then =3D self, =

+                else =3D op.arg1.gcd(self % op.arg1))
+        }
+        less_than =3D self.intrinsic_op {op:
+            '()' =3D op.intrinsics.integer_less_than
+        }
+
+        # Here are some faster versions of ops with less indirection, =

+        # but which fail badly in mixed-mode arithmetic.
+        #'+' =3D {op: '()' =3D self.new(
+        #    intrinsics{self.userdata, op.arg1.userdata}.integer_add)}
+        #'-' =3D {op: '()' =3D self.new(
+        #    intrinsics{self.userdata, op.arg1.userdata}.integer_subtract)=
}
+        #'<' =3D {op:
+        #    '()' =3D intrinsics{
+        #        self.userdata, op.arg1.userdata,
+        #        true=3Dself.true,
+        #        false=3Dself.false
+        #    }.integer_less_than
+        #}
+    }
+
+# The float userdata has divide instead of modulo; otherwise it's
+# quite similar to the integer.  My original design let it inherit
+# pretty much all the methods, because the primitives were properties
+# of the userdata, so they were named things like 'show' instead of
+# 'integer_show', but I decided sticking all the operations in an
+# 'intrinsics' namespace was simpler.
+
+native_float =3D prog.sys.native_integer {self:
+        userdata =3D 3.2.userdata
+        new =3D {new: arg1 =3D 3.5.userdata
+            '()' =3D prog.sys.native_float { userdata =3D new.arg1 }}
+        show =3D prog.sys.native_string.new(self.intrinsics.float_show)
+        as_float =3D self
+        as_integer =3D prog.error("Can't coerce floats to integers")
+        modulo =3D prog.error("Can't take modulo of floats yet")
+        coerce =3D {op: arg1 =3D 2, '()' =3D op.arg1.as_float}
+        add =3D self.intrinsic_op {op:
+            userdata =3D op.intrinsics.float_add
+        }
+        negated =3D self.new(self.intrinsics.float_negated)
+        multiply =3D self.intrinsic_op {op:
+            userdata =3D op.intrinsics.float_multiply
+        }
+        divide =3D self.intrinsic_op {op:
+            userdata =3D op.intrinsics.float_divide
+        }
+        less_than =3D self.intrinsic_op {op:
+            '()' =3D op.intrinsics.float_less_than
+        }
+    }
+
+native_string =3D {self:
+        userdata =3D "bethmolnar".userdata
+        new =3D {new: arg1 =3D "matthew".userdata
+            '()' =3D prog.sys.native_string { userdata =3D new.arg1 }}
+        show =3D self
+        intrinsic_op =3D {op: arg1 =3D "kafka",
+            intrinsics =3D intrinsics{ self.userdata, op.arg1.userdata }
+            userdata =3D op.intrinsics.string_concat
+            '()' =3D self.new(op.userdata)   # by default
+        }
+        '+' =3D self.intrinsic_op
+        '[]' =3D self.intrinsic_op {op: arg1=3D0, =

+            userdata =3D op.intrinsics.string_index
+        }
+        '=3D=3D' =3D {op:
+            '()' =3D intrinsics {
+                arg1 =3D self.userdata
+                arg2 =3D op.arg1.userdata
+                true =3D prog.sys.bool.true
+                false =3D prog.sys.bool.false
+            }.string_equal
+        }
+        length =3D self.intrinsic_op(op: =

+            '()' =3D prog.sys.native_integer.new(op.intrinsics.string_leng=
th)
+        )
+        cursor =3D {c: =

+            ii =3D 0
+            empty =3D c.ii >=3D self.length
+            item =3D self[c.ii]
+            advanced =3D c { ii =3D c.ii + 1 }
+            cursor =3D c
+        }
+        join =3D {join: arg1 =3D prog.sys.list("x", "y", "z")
+            list =3D join.arg1.cursor
+            '()' =3D prog.if(join.list.empty -> "", else =3D join.nonempty=
_case)
+            nonempty_case =3D prog.loop(loop: while =3D loop.iter.empty.no=
t
+                result =3D loop.prev.result + self + loop.iter.item
+                prev =3D { result =3D join.list.item, iter =3D join.list }
+                iter =3D loop.prev.iter.advanced
+            )
+        }
+        # XXX this code is horrible:
+        '%' =3D {op: arg1 =3D {this=3D"example"}
+            '()' =3D "".join(prog.collect(c: c.item.render, for_each=3Dop.=
tokens))
+            lit_token =3D {t: arg1 =3D "bubble wrap", render =3D t.arg1}
+            var_token =3D {t: arg1 =3D "this",
+                render =3D op.arg1.'reflective slot value'(t.arg1).show}
+            # XXX rewrite this without explicit recursion?
+            tokens =3D {t: ip =3D self.cursor # iter position
+                parse =3D prog.if(
+                    (t.ip.item =3D=3D "{") -> t.parse_name,  # }
+                    else =3D t.parse_lit
+                )
+                cursor =3D t
+                item =3D t.parse.item
+                empty =3D t.ip.empty
+                advanced =3D op.tokens { ip =3D t.parse.ip }
+                parse_lit =3D {pl:
+                    pos =3D prog.sys.find("{", in =3D t.ip)
+                    item =3D op.lit_token { "".join(pl.pos.before) }
+                    ip =3D pl.pos.after
+                }
+                next_char =3D t.ip.advanced.item
+                parse_name =3D prog.if(
+                    # special case for {{} and {}} (to represent literal {=
 })
+                    ((t.next_char =3D=3D "{") || (t.next_char =3D=3D "}"))=
 ->
+                        {lit: closer =3D t.ip.advanced.advanced  # {
+                            rv =3D prog.if((lit.closer.item =3D=3D "}") ->=
 {
+                                ip =3D lit.closer.advanced
+                                item =3D op.lit_token { t.next_char }
+                            }
+                            else =3D prog.error("faulty format", "".join(t=
.ip)))
+                            ip =3D lit.rv.ip
+                            item =3D lit.rv.item
+                        }
+                    # normal case    =

+                    else =3D {pn:  # {
+                        # XXX are we sure t.ip can't be empty??
+                        end =3D prog.sys.find("}", in =3D t.ip.advanced)
+                        ip =3D prog.if(
+                            pn.end.after.empty.not -> pn.end.after.advance=
d
+                            else =3D prog.sys.nil
+                        )
+                        item =3D op.var_token { "".join(pn.end.before) }
+                    }
+                )
+            }
+        }
+    }
+find =3D {find: arg1 =3D "x", in =3D "wxyz"
+        if =3D find.arg1.'=3D=3D'
+        cursor =3D find.in.cursor
+        advanced =3D find(cursor =3D find.cursor.advanced)
+        '()' =3D prog.if(
+            (find.cursor.empty || find.if(find.cursor.item)) -> {
+                after =3D find.cursor
+                before =3D prog.sys.nil
+            }
+            else =3D {
+                after =3D find.advanced.after
+                before =3D find.cursor.item @ find.advanced.before
+            }
+        )
+    }
+
+}}
}

[Fix comment highlighting.  This conflicts w/ operators using the # symbol,=
 causing operators using # to not get highlighted right.  However, this opt=
imizes for the common case.
matthew@canonical.org**20070428183057] {
hunk ./bicicleta.vim 30
-syn match bicicletaOperator "\([\-\~\`\!\@\#\%\^\&\*\+\<\>\?\/\|\\]\)\|\(\=
([\-\~\`\!\@\#\%\^\&\*\+\<\>\?\/\|\\]\|=3D\)\([\-\~\`\!\@\#\%\^\&\*\+\<\>\?=
\/\|\\]\|=3D\)\+\)" display
+syn match bicicletaOperator "\([\-\~\`\!\@\%\^\&\*\+\<\>\?\/\|\\]\)\|\(\([=
\-\~\`\!\@\%\^\&\*\+\<\>\?\/\|\\]\|=3D\)\([\-\~\`\!\@\#\%\^\&\*\+\<\>\?\/\|=
\\]\|=3D\)\+\)" display
}

Context:

[updated from matthew's version to add disclaimer of copyright
kragen@pobox.com**20070424082159] =

[adding bicicleta.vim syntax definitions file for vim
kragen@pobox.com**20070424082020] =

[made string interpolation '%' work
kragen@pobox.com**20070419124524
 =

 I had written down code in my notebook on 2007-03-23, but it was a little
 buggy, and it depended on prog.sys.find, which I hadn't written until
 2007-03-28.
 =

 Some bugs (now fixed):
 - was returning a lit_token of pl.pos.before without bothering to make
   pl.pos.before into a string;
 - the usual self-name vs. method-name problem (closer.item vs. lit.closer.=
item)
   --- sure will be nice to have that IDE;
 - parse_lit was looking for a "{" instead of a "}" to terminate the litera=
l
   text (although the version in the notebook was correct);
 - parse_name's else case, which makes a var_token from pn.end.before, was
   starting pn.end from t.ip rather than t.ip.advanced, so the '{' was incl=
uded
   in the variable name.
 =

 The "find" from the notebook also forgot to consider the case of running o=
ut of
 sequence to iterate over.
 =

] =

[updated some out-of-date comments and TODO items
kragen@pobox.com**20070419122850] =

[slight simplification to prog.collect
kragen@pobox.com**20070419122759] =

[fixed small bug in string iteration: .cursor.cursor not existing
kragen@pobox.com**20070419122635] =

[switched from ./build to Makefile
kragen@pobox.com**20070419120225] =

[changed changes to CHANGES, updated
kragen@pobox.com**20070419120129] =

[made objects inherit from prog.sys.object by default (2007-03-26)
kragen@pobox.com**20070418214402
 =

 Now {} is really prog.sys.object{}.  There's an
 intrinsics.proto_object for an object that really doesn't have any
 methods --- used, for instance, by the definition of prog.sys.object
 to keep it from inheriting from itself.
 =

 Because inheritance is (unnecessarily) eager in the parent, doing this
 in the straightforward way would produce an infinite recursive loop in
 the definitions within prog.sys.object, unless they were all careful
 to inherit explicitly from intrinsics.proto_object.  So it's done in a
 less straightforward way --- the lookup of prog.sys.object isn't done
 until and unless method lookup gets all the way to where it's needed.
] =

[added some more string operations (2007-03-26)
kragen@pobox.com**20070418213224
 =

 Now, strings support '=3D=3D', '[]', 'length', iteration, and 'join'
 operations.  There's code in there for a '%' operation, but it's kind
 of overcomplicated and depends on a 'find' algorithm (like Common Lisp
 FIND or C++ STL find) that hasn't been written yet.
 =

] =

[fixed collect a bit
kragen@pobox.com**20070418213001
 =

 I was trying to imitate the distinction in Python's iteration protocol
 between "iterators", which are cursors used for iteration, and
 "iterables", which are things from which you can extract such a
 cursor.  This is somewhat confused by the fact that Python includes
 iterators in the set of iterables.
 =

 But I had forgotten to make prog.collect work on iterables as well as
 iterators.
 =

] =

[added a 'show' method for lists
kragen@pobox.com**20070418212438] =

[fixed prog.sys.collect so it might actually work
kragen@pobox.com**20070418212421] =

[added prog.sys.list to create lists easily (2007-03-26)
kragen@pobox.com**20070418212355] =

[added string concatenation (2007-03-26)
kragen@pobox.com**20070418212245] =

[TAG bicicleta-4
kragen@pobox.com**20070418212026] =

Patch bundle hash:
cb3a9c50c687fa2f477d2b4d6f3ec30655e66e4d

--=_--

.


