Arc Forumnew | comments | leaders | submitlogin
Rainbow bugs
7 points by rocketnia 5054 days ago | 8 comments
I've made several posts like this for Jarc. Rainbow was going to have a turn at some point... and that point is now. If you, conanite, have a better idea of what I should do with this kind of report, such as a) put it in the bug tracker at GitHub, b) email it to you, or c) put it anywhere but the Arc Forum front page :-p then I apologize.

  (pr "Test 1 ")
  
  (let the-bound-symbol-name "the binding for the symbol"
    (def make-bound-symbol (ignore (o result the-bound-symbol-name))
      nil       ; dummy expression
      result))
  
  (= test1 (make-bound-symbol "a dummy value"))
  
  (prn:if
    
    (is test1 "the binding for the symbol")
    "succeeded."
    
    (and (isa test1 'bound-symbol)
         (is (tostring:pr test1) "the-bound-symbol-name"))
    "failed with the expected result."
    
    "failed with an unexpected result."
    )
  
  
  (pr "Test 2 ")
  (= test2 (list:errsafe:eval '(list:errsafe list.nil)))
  (prn:if (iso test2 '(((()))))
    "succeeded."
          (iso test2 '(()))
    "failed with the expected result."
    "failed with an unexpected result.")
  
  
  (pr "Test 3 ")
  (= test3 (errsafe:list:let nil 3 nil))
  (prn:if (iso test3 '(()))
    "succeeded."
          (iso test3 '())
    "failed with the expected result."
    "failed with an unexpected result.")
  
  
  (pr "Test 4 ")
  (= test4 (errsafe:string '(4)))
  (prn:if (is test4 "4")
    "succeeded."
          (is test4 nil)
    "failed with the expected result."
    "failed with an unexpected result.")
Also, a file with the following contents doesn't parse:

  #| This file
     has nothing in it. |#
  #;(To be more exact,
      it doesn't have any expressions.)
  ; It doesn't even have a newline at the end.
I didn't feel the need to bring this stuff up--I just avoided the comment issues, used [apply + "" _] instead of 'string, and refrained from using nil in ssyntax or destructuring--but then I discovered Test 1, and I figured that was odd enough to be worth breaking the silence.

In my code I use a certain pattern for default-less optional arguments, and it happened to fit the bug exactly:

  (w/uniq missing
    (def do-something (a b c (o d missing))
      (unless (args-are-okay a b c d) (err "argument error"))
      (if (is d missing)
        (do-one-thing a b c)
        (do-another-thing a b c d))))
The essential prerequisites seem to be the non-global lexical scope of 'missing, the multiple-expression body, and the multiple-parameter argument list. Those are the qualities I couldn't strip out of the above test. However, I couldn't fix my code by wrapping the function body in (do ...)--I ended up using a global variable instead--so at least one of those prerequisites seems to be a bit slippery.


4 points by conanite 5052 days ago | link

Wow, this is totally awesome, thanks! The arc forum front page is the ideal place for these bugs :))

test1: a special-case optimisation for the particular case of a 2-arg function where the second was optional with a default value being a lexically-bound symbol failed to evaluate the default value, and instead just inserted the symbol name itself into the function's arguments. Fixed

test2: rainbow didn't handle list.nil ssyntax; fixed

test3: allowing 'nil as a parameter name broke rainbow; allowed in arc3.1. For example, (def nilarg (nil) nil). Fixed.

test4: rainbow refused to deal with (coerce '(12 34 56) 'string), arc3.1 returns "123456". Rainbow would only coerce list of chars to string, thus breaking (string '(4)). Fixed.

Multiline comments ... I didn't support them initially as they're not used anywhere in Official Arc, and hadn't noticed them in use anywhere else. I'll get to this after a bit of sleep ...

worth breaking the silence.

I appreciate it. I hope this rainbow works better for you!

-----

1 point by rocketnia 5047 days ago | link

Whoops, I simplified test 4 a bit too much before I posted it.

  (pr "Test 6 ")
  (= test6 (let args (list t nil 'nil () '() "" '(#\e "s")
                           '(nil "" () (t 6)))
             (list (apply string args)
                   (apply apply + "" args)
                   (apply + "" args))))
  (prn:if (iso test6 '("test6" "test6" "test6"))
    "succeeded."
          (iso test6 '("te\"s\"nil\"\"()(t 6)"
                       "te\"s\"t6"
                       "te\"s\"nil\"\"()(t 6)"))
    "failed with the result expected on Rainbow."
          (iso test6 (list "tnilnilnilnilesnilnil(t 6)"
                           (+ "t(#" #\\ "e \"s\")(t 6)")
                           (+ "t(#" #\\ "e \"s\")(nil \"\" nil (t 6))")))
    "failed with the result expected on Jarc."
    "failed with an unexpected result.")
It's probably more convoluted than it has to be, but I'm leaving it that way this time. ^_^

Note that the string literal "...\e..." crashes Rainbow and the string literal "...\\e..." (which is what I really want) behaves incorrectly on Jarc.

Also, if I paste the above code into the Rainbow REPL directly, Rainbow almost always errors out and exits. The paste works if I remove the Jarc case or the Rainbow case, and I've also found it to work if there's been quite a bit of other input during the session. There's a similar issue on Jarc (which prints a stack trace about not having enough memory to read from the input), and, well, it could have something to do with my system.

-----

1 point by jazzdev 5046 days ago | link

the string literal "...\\e..." (which is what I really want) behaves incorrectly on Jarc.

Yeah, I intentionally don't handle escaped strings in Jarc the same as Arc. One frustration in Java is using regular expressions. You have to say

  \\s*(\\d+)\\s*
when you mean

  \s*(\d+)\s*
and regex's are hard enough to read without making them more complex. So in Jarc, I decided that only \n \r \t and \\ would have a special meaning. Every other \ is treated as a literal backslash. So Jarc treats \e as the two characters \ and e. Yeah. Non-standard, non-intuitive. No argument there. But it made regex's so much nicer to read that I decided it was a worthwhile trade-off.

This has made me realize why Perl has a separate syntax for regex.

Jarc should probably have a declare to turn this behavior on and off. At least that would provide compatibility with Arc. Jarc doesn't have \x or \u syntax either, which it should.

-----

1 point by rocketnia 5045 days ago | link

Well, I don't disagree with you on most of those topics, but you missed what I was saying. Your special meaning of \\ seems to be two backslashes. ^_^

  arc> (prn "...\\e...")   ; Arc 3.1
  ...\e...
  "...\\e..."
  
  Jarc> (prn "...\\e...")  ; Jarc
  ...\\e...
  nil
Note that if you were to "fix" this, it would mean certain regexes would use "\\\\", and that sounds like it could be what you want the least. As for me, I don't mind whatever you choose as long as it's intentional. :-p

-----

1 point by jazzdev 5042 days ago | link

Yeah, \\ means \\. It's \n \r \t and \" that are treated specially.

Though I realized Jarc needs a way to enter non-printable characters, so I've added

  \f
  \a
  \e
  \0*oo*
  \x*hh*
  \u*hhhh*
None of those interfere with regular expressions. Though I still don't have an intuitive way to put \e in a string. You can do \x5ce now (in Jarc 16) but that's ugly. I guess \\ needs to be just \ when it's followed by n r t f a e " 0 x or h. That's getting a bit complex, but I guess it's intuitive. Or maybe I should just add a regular expression literal with / delimiters like Perl has.

-----

2 points by rocketnia 5052 days ago | link

For a second I thought there were no improvements, but then I ran Ant again. :-p Worked like a charm.

I've found another bug for you:

  (pr "Test 5 ")
  (= test5 (tostring:catch:after (throw pr!problem) pr!-free))
  (prn:case test5
    "problem-free"  "succeeded."
    "problem"       "failed with the expected result."
                    "failed with an unexpected result.")
It seems 'after doesn't do much....

-----

1 point by conanite 5048 days ago | link

Fixed, finally. Rainbow had an optimisation that prevented copying the whole stack if it was not necessary upon entering a continuation. Alas, for the sake of the tests, that particular optimisation is gone. If I can figure out how to get it back, I will. This is the somewhat violent test case I developed for continuations in combination with 'protect, based on a previous test for co-routines:

  (accum trace 
    (assign proc-A (fn (my-b)
      (trace 'proc-A-start)
      (assign inner-A (fn (n)
        (trace (sym:string 'inner-A-start- n))
        (after (assign my-b (do (trace 'pre-ccc-my-b) (ccc my-b))) (trace (sym:string 'after-ccc-my-b- n)))
        (trace 'end-inner-A)
        (if (> n 0) (after (inner-A (- n 1)) (trace (sym:string 'after-inner-A-tail-call- n))))))
     (after (inner-A 5) (trace 'after-initial-inner-A-call))))

    (assign proc-B (fn (my-a)
      (trace 'proc-B-start)
      (assign inner-B (fn (x)
        (trace 'inner-B-start)
        (after (assign my-a (do (trace 'pre-ccc-my-a) (ccc my-a))) (trace 'after-ccc-my-a))
        (trace 'end-inner-B)
        (after (inner-B 0) (trace 'after-inner-B-tail-call))))))

    (after (proc-A proc-B) (trace 'final-after)))
Here's the expected output:

  (proc-A-start inner-A-start-5 pre-ccc-my-b proc-B-start after-ccc-my-b-5
   end-inner-A inner-A-start-4 pre-ccc-my-b inner-B-start pre-ccc-my-a
   after-ccc-my-a after-ccc-my-b-4 after-inner-A-tail-call-5
   after-ccc-my-b-5 end-inner-A inner-A-start-4 pre-ccc-my-b
   after-ccc-my-b-4 after-inner-A-tail-call-5 after-ccc-my-a end-inner-B
   inner-B-start pre-ccc-my-a after-ccc-my-a after-inner-B-tail-call
   after-ccc-my-b-4 after-inner-A-tail-call-5 after-ccc-my-b-4 end-inner-A
   inner-A-start-3 pre-ccc-my-b after-ccc-my-b-3 after-inner-A-tail-call-4
   after-inner-A-tail-call-5 after-ccc-my-a end-inner-B inner-B-start
   pre-ccc-my-a after-ccc-my-a after-inner-B-tail-call
   after-inner-B-tail-call after-ccc-my-b-4 after-inner-A-tail-call-5
   after-ccc-my-b-3 end-inner-A inner-A-start-2 pre-ccc-my-b
   after-ccc-my-b-2 after-inner-A-tail-call-3 after-inner-A-tail-call-4
   after-inner-A-tail-call-5 after-ccc-my-a end-inner-B inner-B-start
   pre-ccc-my-a after-ccc-my-a after-inner-B-tail-call
   after-inner-B-tail-call after-inner-B-tail-call after-ccc-my-b-4
   after-inner-A-tail-call-5 after-ccc-my-b-2 end-inner-A inner-A-start-1
   pre-ccc-my-b after-ccc-my-b-1 after-inner-A-tail-call-2
   after-inner-A-tail-call-3 after-inner-A-tail-call-4
   after-inner-A-tail-call-5 after-ccc-my-a end-inner-B inner-B-start
   pre-ccc-my-a after-ccc-my-a after-inner-B-tail-call
   after-inner-B-tail-call after-inner-B-tail-call after-inner-B-tail-call
   after-ccc-my-b-4 after-inner-A-tail-call-5 after-ccc-my-b-1 end-inner-A
   inner-A-start-0 pre-ccc-my-b after-ccc-my-b-0 after-inner-A-tail-call-1
   after-inner-A-tail-call-2 after-inner-A-tail-call-3
   after-inner-A-tail-call-4 after-inner-A-tail-call-5 after-ccc-my-a
   end-inner-B inner-B-start pre-ccc-my-a after-ccc-my-a
   after-inner-B-tail-call after-inner-B-tail-call after-inner-B-tail-call
   after-inner-B-tail-call after-inner-B-tail-call after-ccc-my-b-4
   after-inner-A-tail-call-5 after-ccc-my-b-0 end-inner-A
   after-inner-A-tail-call-1 after-inner-A-tail-call-2
   after-inner-A-tail-call-3 after-inner-A-tail-call-4
   after-inner-A-tail-call-5 after-initial-inner-A-call final-after)
I have no idea whether it is correct; all I can say is that arc3.1 gives the same result. Mere compatibility isn't such a high standard, at least in this case :)

Thanks again for the bug, keep them coming!

-----

1 point by conanite 5052 days ago | link

Ouch!

'after works to cleanup after errors; but it's not implemented for jumping into continuations. This one is hurting, I haven't found a quick solution. Continuations are mean enough alone; in combination with 'protect I'm lost. More news later ... and thanks for the bug report!

then I ran Ant again - oh, the joy of compiled languages :)

-----