Code Monkey home page Code Monkey logo

automat's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

automat's Issues

WARNING: cljs.core/bit-or

Seeing a warning in cljs:

WARNING: cljs.core/bit-or, all arguments must be numbers, got [clj-nil number] inste
ad. at line 601 /Users/zalan/.boot/cache/tmp/Users/zalan/src/semion/onty/2ao/-bj0ipr
/main.out/automat/fsm.cljc                                                         

Simple test case:

(ns simple.core
  (:require [automat.core :as a]))

(a/compile [1])

Tested with:

  • boot: 2.5.5
  • clojure: 1.8.0
  • cljs: 1.8.40, 1.9.76, 1.9.225

It doesn't seem to have any affect in my simple use cases. Is this a known issue? Is this something to worry about?

No such var: automat.core/range

Hi there! Sorry to disturb you.
I try to use your library today, and when i tested it with example from README.md , i get error when i use automat.core/range, i put the screenshot of the error :
screen shot 2015-08-26 at 11 45 22 am

i use this version : [automat "0.1.3"]

Thank you!

[Feature request] Generate state transition table

Hello,

As FSM's can be represented by both a state diagram and a state transition table, I think it would be really nice if the lib could generate such tables. As an image or as HTML. HTML could be easy to implement, but I'm not sure since I haven't dig into the code yet.

What do you think ?

persisting automata

I am looking for a way to dump the state into EDN.

I can achieve the opposite using (a/->automaton-state fsm m) though.

Please advise.

reducers for actions on overlapping unions are not called

(require '[automat.core :as a]

(-> (a/or (a/interpose-$ :x [1 2 3])
          (a/interpose-$ :y [1 2 9]))
    (a/compile {:reducers {:x #(update-in %1 [:x] conj %2)
                           :y #(update-in %1 [:y] conj %2)}})
    (a/find {:x [] :y []} [1 2 3])
    :value)
;=>  {:x [3] :y [1 2]}

I would expect the reduction value to be {:x [1 2 3] :y [1 2]}.

It looks like only actions attached to the last union are triggered when the unions overlap.

(-> (a/or (a/interpose-$ :y [1 2 9]) ; :y actions will be replaced by overlapping :x actions
          (a/interpose-$ :x [1 2 3]))
    (a/compile {:reducers {:x #(update-in %1 [:x] conj %2)
                           :y #(update-in %1 [:y] conj %2)}})
    (a/find {:x [] :y []} [1 2 3])
    :value)
;=> {:x [1 2 3], :y []}
; no :y actions trigger

`permissable?` predicate?

I'm trying to express the concept of valid transitions between states for one of the domain entities in my system.

I have an fsm that does this nicely: [1 (a/* [2 3]) 4].

Requests to move the domain entity from its current state (any of 1, 2, 3, or 4) to another state (say from 1 to 4 or 1 to 2 or 2 to 3 or 3 to 2) come in via http requests and I'd like to return a 409 in the event that the transition would not be allowable given the above fsm.

However, to do that, it appears that I have to construct an entire state map with knowledge of the internal state-indexes used by the fsm once it's been compiled. There doesn't appear to be any way to turn the compiled fsm into, say, a state transition table like {1 #{2 4} 2 #{3} 3 #{2 4}} which could then be used in turn to implement a possible? or permissible? predicate.

So, am I missing something or could something like permissible? be implemented directly in automat? Ideally, it would be something like (a/permissible? fsm (things-current-state-as-named-in-fsm-data thing) proposed-state)

I will say that I may be fundamentally misunderstanding this library and maybe even FSMs in general so forgive me if that's the case and maybe point me in the direction of some reading I could do/a different library I could use.

Thanks in advance!

infinite loop with "or"

The or function in core may run into infinite loop at the minimization step. In particular, when it happens, the infinite loop will appear in the root function in the merge-pairs function. The occurrence of the infinite loop seems to be random, however, it will always appear if one runs or enough times.

To reproduce, run the following code:

(dotimes [i 100]
   (println (str "No." i))
   (a/or 1 2 3 4))

It may take a couple of runs of the above to generate an infinite loop. The process will have to be killed to stop it.

If we only or two states together, this bug doesn't appear.

More details on $

Could you add more details on when the reduce actions are being executed to the documentation, and show the actions in the view graph?

In the documentation it reads like the actions might be associated with the transitions, but they actually seem to be collected on each state, i.e. for :x :w input action :F is still executed because it's associated with state 1, not the transition :z.

(defn states
  [arg]
  (let [f (a/compile
            [(a/$ :A)
             (a/or
               [(a/$ :B) :x (a/$ :C)]
               [(a/$ :D) :y (a/$ :E) :z (a/$ :F)])
             (a/$ :G)
             :w
             (a/$ :H)]
            (apply merge
                   (map (fn [t] {t #(conj %1 [t %2])})
                        [:A :B :C :D :E :F :G :H])))])
  (:value (a/greedy-find f (a/start f []) arg)))

(states [:x :w])
=> [[:A :x] [:D :x] [:B :x] [:F :x] [:C :x] [:G :x] [:H :w]]
(states [:y :z :w])
=> [[:A :y] [:D :y] [:B :y] [:E :y] [:F :z] [:C :z] [:G :z] [:H :w]]

screen shot 2014-05-24 at 16 25 58

ClassCastException thrown by heterogenous FSMs

(require '[automat.core :as a])

(-> [:a \b]
    a/compile
    (a/find nil [:a :b]))
;=> java.lang.ClassCastException: clojure.lang.Keyword cannot be cast to java.lang.Number

Looks like chars and 1-character strings are coerced to ints: https://github.com/ztellman/automat/blob/8bd3989e0c9d987caafc3b5f0d64e7750d2a3d49/src/automat/core.clj#L19-24

Oddly, this works just fine:

(-> (a/or [:a \b] [:a :b])
    a/compile
    (a/find nil [:a :b]))
;=> #automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 2, :start-index 0, :stream-index 2, :value nil}

Automat "slow"

We discussed this on Slack briefly. I got this using a plain clojure.main repl. I've generated a few data sets using test.check, I've used 1055 as the length of input.

user=> (require '[automat.core :as a]
                '[criterium.core :as c])
user=> (let [data "7Z0YzI6qmKUR4QNOq81l409AC2Ng3c00tb9J4VOz5kBuGTqCAWmYTx4E7Xckp6662k0jd14ds9G1jt0ldShcf37POcfYqsPcqliwlcM0l6GYVF3850650biK89QL3N68Z19k57Eu3dLSukaWZCM32J3Dz99l4KF53JvkWraBwlX3FpU3TWM3X992a1tU2qL6q6Y0g4r4ay5QMuc1twL9xRJMn3WoeA7g8Oh3FoYMX3VD02FhaB76TwMm5kxf2mxK5HUmDu88gYsO4CS6paOqEf7s17hJ712y3xaCT9a3r71M4E2467jr0i4uGt267HMKQO7dqHywM8CFy72AIUq7OZ4K5Nn2t9JuL5Kqv79Y8ZE3aAS4tNeG8ckh201AfB11okTh5bT9oann21A6wN67vzq3Q31SyF5wg2Xx1885z56X4WLgkRfq5A3kFgmqAJdfmlcTp5Dr4404P2nWGmDBJ62dR100RQtLLjKN25a42Kebk1pWFbu6z56y5jHE0txpN9V87cb66Exp50Cg42Jl37Mbi2MqkW218LjNMTnRU7365RT8Uf3523X1cCZFWq7z2GSkc6uL2ai2d88B7a7i1k0h0d9K63765p3w4obwd32kIlQu5xy4yFQ3R91XP353c8J7H3Ep3Oo45p8F70y3649hqM310Gnv13gBXHdLyXs25od8tmwE5fHB3G06OaG877ymRI5J89KI1XylAx47o18H17CHC6P5fm6KC71n40WjK01P4rt06K06F8TM6OF81eq3wE4W0AO702PxEO042I4r9VUt8g0jp3057Sbju25U15HCP1742NEVTM1niDjb0S89885Nf93X97Cgsw5x3eu13ZU8cGbj246qt5t7lzzW08ozl1yykx73d7a74VNPYdZE4498XTgnzhv3R44p6iulMm9Vauv9sXG2Hb4TH4Kx9G6hK1X2MJEsHFm680ZueWNu98G3oVnA435SfwSA6umdt26g9hBoTGMXZ7K64yIs8kYxgwKi0Jqhvm3wdTJdn86u9HX5TeQh45Vc6TSZeCW2xLxNagu"
      fsm (a/compile [\f \o \o])]
  (c/bench
    (reduce
      #(a/advance fsm %1 %2 false)
      nil
      data))) 
Evaluation count : 339480 in 60 samples of 5658 calls.
             Execution time mean : 178.169999 µs
    Execution time std-deviation : 2.128803 µs
   Execution time lower quantile : 176.386367 µs ( 2.5%)
   Execution time upper quantile : 182.867510 µs (97.5%)
                   Overhead used : 1.727016 ns

Found 4 outliers in 60 samples (6.6667 %)
	low-severe	 1 (1.6667 %)
	low-mild	 3 (5.0000 %)
 Variance from outliers : 1.6389 % Variance is slightly inflated by outliers

If I'm not being daft, which is common enough, (/ 178.16 1055) is 0.169µs = 169ns per advancement.

Numbers have a similar result:

user=> (let [data [-3 25 1 -14 -2 15 -18 -11 30 3 -30 17 24 -20 27 13 -1 -17 -15 -23 -24 -25 22 11 12 -8 -24 10 11 14 18 -10 -10 19 -28 27 -26 -8 1 -22 20 4 -12 -13 -15 13 27 -8 30 -7 21 -5 25 -28 10 4 -18 24 3 -16 -20 -22 22 -28 -8 25 -29 -13 -16 -24 -9 1 1 7 -9 7 -26 -5 5 -2 19 25 29 25 -4 20 1 21 -3 21 -13 -11 -15 2 -3 -17 28 -12 -10 15 -7 -28 14 2 13 18 17 -17 7 1 17 11 23 -16 -8 22 -28 21 28 1 18 7 26 27 5 -14 7 16 -7 -21 -7 10 5 5 20 24 30 30 17 27 16 -17 -18 21 -9 -3 -30 3 -25 -24 7 25 22 -18 -3 -12 -8 14 -20 -4 15 7 -19 23 -29 2 1 29 -12 24 19 18 16 -17 20 27 -26 18 -26 16 -17 13 24 29 -23 21 -7 1 -2 -15 -6 17 19 5 -18 30 -7 5 2 -1 3 6 -24 3 -13 -18 5 4 6 -5 7 10 22 7 30 -9 6 -16 -27 22 26 -15 17 14 -10 24 -22 17 20 30 5 14 17 -26 -6 17 23 18 6 1 -25 -15 -29 -7 -20 -23 3 1 23 -1 22 -15 -11 2 -22 12 16 28 24 4 12 -11 -8 1 23 10 0 20 25 -9 -17 21 -21 10 7 -7 5 11 -8 -11 -15 29 8 -29 -24 -14 17 -19 -17 -18 16 16 3 13 -19 7 27 -29 15 -21 -26 24 2 10 -27 -26 13 17 29 -14 0 -26 -2 10 18 0 25 23 8 21 6 -6 -18 5 16 19 23 26 24 -27 -26 -11 -3 -17 -12 23 21 12 23 22 20 -27 -3 -3 15 -1 -20 5 -13 -21 30 -6 -18 -24 -4 20 -30 -15 -28 23 10 27 3 -21 18 -12 1 23 16 4 3 29 -27 -2 15 -26 2 -9 -14 28 18 -25 9 -17 7 -23 -29 -14 -23 -7 -25 16 3 -1 -27 -14 7 -25 28 -11 -27 -15 -27 -6 19 19 -15 -5 26 -9 -28 -4 7 -20 -26 7 8 28 0 14 -21 -16 6 30 10 -21 0 -8 -2 -25 -18 -21 20 -27 24 8 -22 18 -20 8 -10 -29 6 9 -7 4 -11 -17 -21 -4 -16 15 3 10 -23 8 -26 3 20 28 -3 25 20 -28 -28 5 -11 24 -3 -29 -21 27 -20 -12 2 3 22 25 22 25 29 3 26 27 -16 -10 30 25 -28 26 -13 -24 -16 -2 10 11 11 -2 -24 18 -22 14 -30 -25 -5 -23 -28 18 28 10 -16 6 7 -8 -22 23 -13 11 21 -3 -24 -4 22 -13 -8 27 -13 19 -29 10 -6 -7 -5 9 13 -19 -1 -9 30 -2 2 21 -13 9 -12 -5 -26 12 7 -23 20 -29 2 27 5 -14 14 2 14 2 -9 17 -13 -24 -9 -6 11 -6 -26 -27 -13 -1 28 -23 -19 22 3 -29 2 -13 9 -1 11 30 3 5 -17 -18 28 26 28 -9 -20 15 -4 -19 -14 -18 -8 -17 -22 -29 -24 20 12 12 16 7 -25 -6 14 0 -15 14 20 -25 23 10 -29 8 14 -19 -20 -3 25 -3 -7 -19 -11 0 -29 24 27 -28 -28 22 -17 27 12 -24 -26 -19 14 17 -25 -7 -13 18 28 21 24 22 -25 25 -18 -15 -2 25 26 -17 20 15 -7 30 18 20 -14 -12 -16 10 23 -27 -7 -14 -8 -8 -12 24 6 -4 30 -16 14 4 -15 27 -17 -14 -24 29 29 18 -7 -15 28 -11 7 6 18 30 -25 22 -2 -20 1 -23 -13 -28 -4 26 18 25 22 -18 -5 -25 -7 5 30 -1 -7 -8 -30 24 -13 19 18 26 1 -16 1 -1 25 16 10 9 1 12 -28 5 -27 -16 14 24 18 -1 -12 25 -19 30 24 -21 27 18 -10 -14 -7 -12 -26 -9 13 23 10 -6 1 -7 -16 25 20 -22 11 12 20 4 14 -6 18 19 5 -6 14 -29 -26 21 11 30 19 -24 -12 -6 -27 25 14 -1 25 -12 18 -17 0 -29 -24 -30 5 -20 29 24 -17 28 -11 -10 7 11 7 -22 2 21 14 -15 -29 8 16 16 10 9 -10 16 30 12 -2 -18 9 5 -20 13 10 -29 17 11 22 22 -27 -3 -9 -19 22 11 15 -22 -15 11 -27 15 15 -26 6 -2 2 16 16 -7 -1 -7 -2 -17 20 -12 -13 18 8 7 -3 9 30 13 -19 -4 -13 7 -12 24 -7 8 10 -5 14 6 26 -18 -21 -1 -4 28 4 2 -13 21 15 -4 12 6 -20 -6 23 -21 8 22 22 5 23 -12 -23 -16 -11 7 -12 20 -24 10 -23 16 7 20 3 -1 26 -24 23 -21 -9 27 -3 -23 -30 22 16 12 3 10 -14 -17 -6 16 -16 -17 -2 5 13 -6 23 27 -26 15 -7 21 -14 14 12 -8 -13 23 -5 -27 30 27 -5 26 29 29 28 1 -15 -26 20 30 17 1 2 -1 -3 13 13 -10 -13 13 -7 -15 -14 -26 -11 -3 1 21 -16 11 23 -1 6 17 19 2 2 -14 3 -27 9 2 14 -18 -30 22 3 -2 19 -26 30 29 -24 -16 -2 5 -6 -13 -21 -20 -17 23 15 30 15 -12 -5 -18 8 -2 -9 -13 -24 -20 -26 -27 -9 21 -30 -10 4 -27 22 25 18 8 -28 -16 6 30 -2]
      fsm (a/compile [50 20 10])]
  (c/bench
    (reduce
      #(a/advance fsm %1 %2 false)
      nil
      data)))

Evaluation count : 446280 in 60 samples of 7438 calls.
             Execution time mean : 139.068076 µs
    Execution time std-deviation : 12.810401 µs
   Execution time lower quantile : 134.080157 µs ( 2.5%)
   Execution time upper quantile : 168.040733 µs (97.5%)
                   Overhead used : 1.727016 ns

Found 9 outliers in 60 samples (15.0000 %)
	low-severe	 1 (1.6667 %)
	low-mild	 8 (13.3333 %)
 Variance from outliers : 65.3027 % Variance is severely inflated by outliers
nil

This is with this java:

❯ java -version
openjdk version "1.8.0_152"
OpenJDK Runtime Environment (build 1.8.0_152-b03)
OpenJDK 64-Bit Server VM (build 25.152-b03, mixed mode)

and clojure version 1.8.

I also tried with Oracle JDK:

user=> (let [data [-3 25 1 -14 -2 15 -18 -11 30 3 -30 17 24 -20 27 13 -1 -17 -15 -23 -24 -25 22 11 12 -8 -24 10 11 14 18 -10 -10 19 -28 27 -26 -8 1 -22 20 4 -12 -13 -15 13 27 -8 30 -7 21 -5 25 -28 10 4 -18 24 3 -16 -20 -22 22 -28 -8 25 -29 -13 -16 -24 -9 1 1 7 -9 7 -26 -5 5 -2 19 25 29 25 -4 20 1 21 -3 21 -13 -11 -15 2 -3 -17 28 -12 -10 15 -7 -28 14 2 13 18 17 -17 7 1 17 11 23 -16 -8 22 -28 21 28 1 18 7 26 27 5 -14 7 16 -7 -21 -7 10 5 5 20 24 30 30 17 27 16 -17 -18 21 -9 -3 -30 3 -25 -24 7 25 22 -18 -3 -12 -8 14 -20 -4 15 7 -19 23 -29 2 1 29 -12 24 19 18 16 -17 20 27 -26 18 -26 16 -17 13 24 29 -23 21 -7 1 -2 -15 -6 17 19 5 -18 30 -7 5 2 -1 3 6 -24 3 -13 -18 5 4 6 -5 7 10 22 7 30 -9 6 -16 -27 22 26 -15 17 14 -10 24 -22 17 20 30 5 14 17 -26 -6 17 23 18 6 1 -25 -15 -29 -7 -20 -23 3 1 23 -1 22 -15 -11 2 -22 12 16 28 24 4 12 -11 -8 1 23 10 0 20 25 -9 -17 21 -21 10 7 -7 5 11 -8 -11 -15 29 8 -29 -24 -14 17 -19 -17 -18 16 16 3 13 -19 7 27 -29 15 -21 -26 24 2 10 -27 -26 13 17 29 -14 0 -26 -2 10 18 0 25 23 8 21 6 -6 -18 5 16 19 23 26 24 -27 -26 -11 -3 -17 -12 23 21 12 23 22 20 -27 -3 -3 15 -1 -20 5 -13 -21 30 -6 -18 -24 -4 20 -30 -15 -28 23 10 27 3 -21 18 -12 1 23 16 4 3 29 -27 -2 15 -26 2 -9 -14 28 18 -25 9 -17 7 -23 -29 -14 -23 -7 -25 16 3 -1 -27 -14 7 -25 28 -11 -27 -15 -27 -6 19 19 -15 -5 26 -9 -28 -4 7 -20 -26 7 8 28 0 14 -21 -16 6 30 10 -21 0 -8 -2 -25 -18 -21 20 -27 24 8 -22 18 -20 8 -10 -29 6 9 -7 4 -11 -17 -21 -4 -16 15 3 10 -23 8 -26 3 20 28 -3 25 20 -28 -28 5 -11 24 -3 -29 -21 27 -20 -12 2 3 22 25 22 25 29 3 26 27 -16 -10 30 25 -28 26 -13 -24 -16 -2 10 11 11 -2 -24 18 -22 14 -30 -25 -5 -23 -28 18 28 10 -16 6 7 -8 -22 23 -13 11 21 -3 -24 -4 22 -13 -8 27 -13 19 -29 10 -6 -7 -5 9 13 -19 -1 -9 30 -2 2 21 -13 9 -12 -5 -26 12 7 -23 20 -29 2 27 5 -14 14 2 14 2 -9 17 -13 -24 -9 -6 11 -6 -26 -27 -13 -1 28 -23 -19 22 3 -29 2 -13 9 -1 11 30 3 5 -17 -18 28 26 28 -9 -20 15 -4 -19 -14 -18 -8 -17 -22 -29 -24 20 12 12 16 7 -25 -6 14 0 -15 14 20 -25 23 10 -29 8 14 -19 -20 -3 25 -3 -7 -19 -11 0 -29 24 27 -28 -28 22 -17 27 12 -24 -26 -19 14 17 -25 -7 -13 18 28 21 24 22 -25 25 -18 -15 -2 25 26 -17 20 15 -7 30 18 20 -14 -12 -16 10 23 -27 -7 -14 -8 -8 -12 24 6 -4 30 -16 14 4 -15 27 -17 -14 -24 29 29 18 -7 -15 28 -11 7 6 18 30 -25 22 -2 -20 1 -23 -13 -28 -4 26 18 25 22 -18 -5 -25 -7 5 30 -1 -7 -8 -30 24 -13 19 18 26 1 -16 1 -1 25 16 10 9 1 12 -28 5 -27 -16 14 24 18 -1 -12 25 -19 30 24 -21 27 18 -10 -14 -7 -12 -26 -9 13 23 10 -6 1 -7 -16 25 20 -22 11 12 20 4 14 -6 18 19 5 -6 14 -29 -26 21 11 30 19 -24 -12 -6 -27 25 14 -1 25 -12 18 -17 0 -29 -24 -30 5 -20 29 24 -17 28 -11 -10 7 11 7 -22 2 21 14 -15 -29 8 16 16 10 9 -10 16 30 12 -2 -18 9 5 -20 13 10 -29 17 11 22 22 -27 -3 -9 -19 22 11 15 -22 -15 11 -27 15 15 -26 6 -2 2 16 16 -7 -1 -7 -2 -17 20 -12 -13 18 8 7 -3 9 30 13 -19 -4 -13 7 -12 24 -7 8 10 -5 14 6 26 -18 -21 -1 -4 28 4 2 -13 21 15 -4 12 6 -20 -6 23 -21 8 22 22 5 23 -12 -23 -16 -11 7 -12 20 -24 10 -23 16 7 20 3 -1 26 -24 23 -21 -9 27 -3 -23 -30 22 16 12 3 10 -14 -17 -6 16 -16 -17 -2 5 13 -6 23 27 -26 15 -7 21 -14 14 12 -8 -13 23 -5 -27 30 27 -5 26 29 29 28 1 -15 -26 20 30 17 1 2 -1 -3 13 13 -10 -13 13 -7 -15 -14 -26 -11 -3 1 21 -16 11 23 -1 6 17 19 2 2 -14 3 -27 9 2 14 -18 -30 22 3 -2 19 -26 30 29 -24 -16 -2 5 -6 -13 -21 -20 -17 23 15 30 15 -12 -5 -18 8 -2 -9 -13 -24 -20 -26 -27 -9 21 -30 -10 4 -27 22 25 18 8 -28 -16 6 30 -2]
      fsm (a/compile [50 20 10])]
  (c/bench
    (reduce
      #(a/advance fsm %1 %2 false)
      nil
      data)))

Evaluation count : 428820 in 60 samples of 7147 calls.
             Execution time mean : 140.780361 µs
    Execution time std-deviation : 748.460193 ns
   Execution time lower quantile : 139.914201 µs ( 2.5%)
   Execution time upper quantile : 142.457726 µs (97.5%)
                   Overhead used : 1.727470 ns

Found 6 outliers in 60 samples (10.0000 %)
	low-severe	 6 (10.0000 %)
 Variance from outliers : 1.6389 % Variance is slightly inflated by outliers
nil
❯ java -version
java version "1.8.0_144"
Java(TM) SE Runtime Environment (build 1.8.0_144-b01)
Java HotSpot(TM) 64-Bit Server VM (build 25.144-b01, mixed mode)

Is this enough detail? Anything else I can do?

CompilerException when using aleph and automat together

I'm trying to use both aleph and automat in a project together. In :requireing libraries from these projects I end up with the error:

CompilerException java.lang.IllegalAccessError: vector does not exist, compiling:(byte_streams/graph.clj:1:1)

I've tried to track this down myself and see where byte_streams/graph.clj does the following so that it can refer to clj-tuple's vector. Beyond that I'm lost.

(:refer-clojure :exclude [vector type byte-array])

Here are the dependencies from project.clj:
:dependencies [[org.clojure/clojure "1.6.0"] [automat "0.1.3"] [aleph "0.4.0"]])

Here is the ns:
(ns nettink.core (:require [automat.core :as a] [aleph.tcp :as tcp]))

Not sure if there is a different way to :require these two projects together or not. Am hoping that you have combined the two before and can help me out.

Something about all-numerics strings causes failure (?)

The automata ["1"] won't accept the input "1":

avi.repl=> (a/advance (a/compile [:1]) nil :1 :error)
#automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 1, :start-index 0, :stream-index 1, :value nil}
avi.repl=> (a/advance (a/compile ["1"]) nil "1" :error)

ClassCastException java.lang.String cannot be cast to java.lang.Number  automat.stream/to-stream/reify--20992 (stream.clj:70)
avi.repl=> (a/advance (a/compile ["x1"]) nil "x1" :error)
#automat.core.CompiledAutomatonState{:accepted? true, :checkpoint nil, :state-index 1, :start-index 0, :stream-index 1, :value nil}
avi.repl=>

Compilation error in Clojure 1.8?

[automat "0.1.3"] in project.clj's dependencies. When I lein repl and try to require view, I see:

user=> (require '[automat.viz :refer (view)])

CompilerException java.lang.NoClassDefFoundError: IllegalName: compile__stub.automat.fsm.automat.fsm/State, compiling:(automat/fsm.clj:18:1)

Really sorry if this is a known problem, I tried googling and poking around github, but can't find anything, and am super-excited to try it!

[feature request] signal function return several values

It is desirable for the signal function to return multiple values (a seq of candidates, for example), and the FSM would try and take the first value that can advance to the next state. This way, the signal become context sensitive, giving different interpretations depending on the state.

Would it be possible to add something like this? I looked at the code, it seems to be easy to add this functionality to the base compiler, but I do not seem to grok the eval compiler to be able to add it myself.

svg viewing problem

When the result of automat's fsm->dot is run through rhizome's dot->svg, the resulting
svg fragment doesn't display the entire automata. Specifically, the svg's width and height
are larger than the viewBox dimensions:

(-> (automat/fsm->dot [1] {}) rhizome/dot->svg)
;=>  ... <svg width="242pt" height="72pt"  viewBox="0.00 0.00 174.00 52.00" ...

I need to call {:dpi 72} to get a correct rendering. I'm on ubuntu 12.04.

(-> (automat/fsm->dot [1] {:dpi 72}) rhizome/dot->svg)
;=>  ... <svg width="174pt" height="52pt"  viewBox="0.00 0.00 174.00 52.00" ...

I'm using the svg fragments to render the automata in gorilla-repl.

java -jar standalone.jar throws ExceptionInInitializerError - Caused by: j.l.ClassCastException: c.l.Var$Unbound cannot be cast to clojure.lang.MultiFn

I'm a bit lost having this problem only when i tried my app using the uberjar generated using lein uberjar

The error says ...
Caused by: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.MultiFn

(perhaps) Could be related with my AOT project.clj config???

:main roc.platform.main
:aot [roc.platform.main
          #"roc\.platform\.*" 
          #"roc\.[a-z]+$"]

the output error ...

$ java -jar target/roc-0.1.0-SNAPSHOT-standalone.jar 
Exception in thread "main" java.lang.ExceptionInInitializerError
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:344)
	at clojure.lang.RT.classForName(RT.java:2168)
	at clojure.lang.RT.classForName(RT.java:2177)
	at clojure.lang.RT.loadClassForName(RT.java:2196)
	at clojure.lang.RT.load(RT.java:443)
	at clojure.lang.RT.load(RT.java:419)
	at clojure.core$load$fn__5677.invoke(core.clj:5893)
	at clojure.core$load.invokeStatic(core.clj:5892)
	at clojure.core$load.doInvoke(core.clj:5876)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5697)
	at clojure.core$load_one.invoke(core.clj:5692)
	at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
	at clojure.core$load_lib.invokeStatic(core.clj:5736)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:421)
	at automat.compiler.core$loading__5569__auto____14606.invoke(core.cljc:1)
	at automat.compiler.core__init.load(Unknown Source)
	at automat.compiler.core__init.<clinit>(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:344)
	at clojure.lang.RT.classForName(RT.java:2168)
	at clojure.lang.RT.classForName(RT.java:2177)
	at clojure.lang.RT.loadClassForName(RT.java:2196)
	at clojure.lang.RT.load(RT.java:443)
	at clojure.lang.RT.load(RT.java:419)
	at clojure.core$load$fn__5677.invoke(core.clj:5893)
	at clojure.core$load.invokeStatic(core.clj:5892)
	at clojure.core$load.doInvoke(core.clj:5876)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5697)
	at clojure.core$load_one.invoke(core.clj:5692)
	at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
	at clojure.core$load_lib.invokeStatic(core.clj:5736)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:482)
	at automat.viz$loading__5569__auto____14604.invoke(viz.clj:1)
	at automat.viz__init.load(Unknown Source)
	at automat.viz__init.<clinit>(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:344)
	at clojure.lang.RT.classForName(RT.java:2168)
	at clojure.lang.RT.classForName(RT.java:2177)
	at clojure.lang.RT.loadClassForName(RT.java:2196)
	at clojure.lang.RT.load(RT.java:443)
	at clojure.lang.RT.load(RT.java:419)
	at clojure.core$load$fn__5677.invoke(core.clj:5893)
	at clojure.core$load.invokeStatic(core.clj:5892)
	at clojure.core$load.doInvoke(core.clj:5876)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5697)
	at clojure.core$load_one.invoke(core.clj:5692)
	at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
	at clojure.core$load_lib.invokeStatic(core.clj:5736)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:619)
	at roc.auto$loading__5569__auto____14602.invoke(auto.clj:1)
	at roc.auto__init.load(Unknown Source)
	at roc.auto__init.<clinit>(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:344)
	at clojure.lang.RT.classForName(RT.java:2168)
	at clojure.lang.RT.classForName(RT.java:2177)
	at clojure.lang.RT.loadClassForName(RT.java:2196)
	at clojure.lang.RT.load(RT.java:443)
	at clojure.lang.RT.load(RT.java:419)
	at clojure.core$load$fn__5677.invoke(core.clj:5893)
	at clojure.core$load.invokeStatic(core.clj:5892)
	at clojure.core$load.doInvoke(core.clj:5876)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5697)
	at clojure.core$load_one.invoke(core.clj:5692)
	at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
	at clojure.core$load_lib.invokeStatic(core.clj:5736)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:1289)
	at roc.platform.system$loading__5569__auto____24062.invoke(system.clj:1)
	at roc.platform.system__init.load(Unknown Source)
	at roc.platform.system__init.<clinit>(Unknown Source)
	at java.lang.Class.forName0(Native Method)
	at java.lang.Class.forName(Class.java:344)
	at clojure.lang.RT.classForName(RT.java:2168)
	at clojure.lang.RT.classForName(RT.java:2177)
	at clojure.lang.RT.loadClassForName(RT.java:2196)
	at clojure.lang.RT.load(RT.java:443)
	at clojure.lang.RT.load(RT.java:419)
	at clojure.core$load$fn__5677.invoke(core.clj:5893)
	at clojure.core$load.invokeStatic(core.clj:5892)
	at clojure.core$load.doInvoke(core.clj:5876)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$load_one.invokeStatic(core.clj:5697)
	at clojure.core$load_one.invoke(core.clj:5692)
	at clojure.core$load_lib$fn__5626.invoke(core.clj:5737)
	at clojure.core$load_lib.invokeStatic(core.clj:5736)
	at clojure.core$load_lib.doInvoke(core.clj:5717)
	at clojure.lang.RestFn.applyTo(RestFn.java:142)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$load_libs.invokeStatic(core.clj:5774)
	at clojure.core$load_libs.doInvoke(core.clj:5758)
	at clojure.lang.RestFn.applyTo(RestFn.java:137)
	at clojure.core$apply.invokeStatic(core.clj:648)
	at clojure.core$require.invokeStatic(core.clj:5796)
	at clojure.core$require.doInvoke(core.clj:5796)
	at clojure.lang.RestFn.invoke(RestFn.java:408)
	at clojure.core$eval145.invokeStatic(NO_SOURCE_FILE:7)
	at clojure.core$eval145.invoke(NO_SOURCE_FILE:7)
	at clojure.lang.Compiler.eval(Compiler.java:6927)
	at clojure.lang.Compiler.eval(Compiler.java:6916)
	at clojure.lang.Compiler.eval(Compiler.java:6890)
	at clojure.core$eval.invokeStatic(core.clj:3105)
	at clojure.core$eval.invoke(core.clj:3101)
	at roc.platform.main$_main.invokeStatic(main.clj:6)
	at roc.platform.main$_main.doInvoke(main.clj:4)
	at clojure.lang.RestFn.invoke(RestFn.java:397)
	at clojure.lang.AFn.applyToHelper(AFn.java:152)
	at clojure.lang.RestFn.applyTo(RestFn.java:132)
	at roc.platform.main.main(Unknown Source)
Caused by: java.lang.ClassCastException: clojure.lang.Var$Unbound cannot be cast to clojure.lang.MultiFn
	at automat.fsm__init.load(Unknown Source)
	at automat.fsm__init.<clinit>(Unknown Source)
	... 150 more

Thanks in advance for this great lib!

Documentation link incomplete

The README says to go to http://ideolalia.com/automat/ for the complete documentation, but that link only contains the docs for the viz namespace.

Support character ranges

I'd expect character ranges (e.g. the way regexes allow [A-Z]) to work, but they're not supported:

boot.user=> (view (a/range \A \Z))
java.lang.IllegalArgumentException: Don't know how to create a range from '\A' to '\Z'

`valid-transitions` fn?

Related to #23, the other question I'm interested in asking the fsm is, given the current state, what inputs would be valid. This would allow me to return to the user in the 409 the statement: you asked me to do this, which is wrong, but you could've asked me to do these, which would have been ok.

Again, given the tools available in fsm I'm unclear how to accomplish that goal. Any help there?

java.lang.RuntimeException: Method code too large!

Compiling a complex FSM throws java.lang.RuntimeException: Method code too large!

(require '[automat.core :as a])

(let [coll (range 20)]
  (->> coll
       (map #(interpose % coll))
       (apply a/or)
       a/compile))
;=> clojure.lang.Compiler$CompilerException: java.lang.RuntimeException: Method code too large!

Automaton with `a/not` triggers handlers unexpectedly

Again, apologies if I'm misunderstanding the docs - from the main docs "The not operator is equivalent to the regex ^ flag for negating character classes". Running viz/view on trivial (one-node) automata this fits my understanding, however the following example of a word-count automaton fails:

(defn word+ [v _] (inc v))

(def word [\a])

(def !word (a/not word))

(def word-count-with-not
  (a/compile 
    (a/*
      (a/or 
        [(a/+ !word)]
        [[(a/+ word) !word] (a/$ :word+)]))
    {:reducers {:word+ word+}}))

(:value (reduce #(a/advance word-count-with-not    %1 %2) 0 "aaa aa aaaaaa.")) ;=> 6?!

If I preprocess the machines with a signal instead, the logic seems to be correct:

(def word-count-with-signal
  (a/compile 
    (a/*
      (a/or 
        [(a/+ false)]
        [[(a/+ true) false] (a/$ :word+)]))
    {:signal (partial = \a)
     :reducers {:word+ word+}}))

(:value (reduce #(a/advance word-count-with-signal %1 %2) 0 "aaa aa aaaaaa.")) ;=> 3

Comparing the state machines with viz.view, it looks like the :word+ event is triggered, unexpectedly, after the 2nd (a/+ word) iteration.

Conflict with midje

Using automat together with midje causes exception on startup.

How to reproduce:
Create a new project, add dependencies:

[automat "0.1.3"]
[midje "1.8.2"]

Launch REPL, load midje:

=> (require '[midje.repl])
Run `(doc midje)` for Midje usage.
Run `(doc midje-repl)` for descriptions of Midje repl functions.

CompilerException java.lang.RuntimeException: Unable to resolve symbol: _wrapping-target_ in this context, compiling:(midje/repl.clj:32:3) 

It can be worked around by adding a third dependency:

[potemkin "0.4.2"]

Seems a minor issue, but it took me a while to figure out, eventually found this: marick/Midje#340
I believe it can be fixed by changing potemkin version for automat to 0.4.2. I could make a pull request, but there is no branch for 0.1.3 hotfixes :)

timeout example

While I think I know how I would do that (via reducer functions), I am looking for a recommendation how to achieve timeout transitions.

Inconsistency of $

First of all, thanks for the wonderful library! I'm trying to study FSM applications and doing some exercises. Suddenly I bumped into an issue:

This works:

(view  (a/interpose-$ 42 [1]))

While this doesn't:

(view [1 (a/$ 42)])

If I replace 42 with :foo, it works both ways and produces identical FSMs.
My use case is that I want to dispatch reducers with a function, not with a map:

(defn reducers [v]
  (fn [state _] (+ state v)))
...
(a/compile [ ... ]
             {:reducers reducers}))

Therefore I need to pass more than just a keyword, possibly a nested data structure.
Is this a bug or a feature? It would be great to have it working consistently, and also just $ is much shorter than interpose-$.

Non-numeric input when numeric input is expected causes error, not rejection

> (a/advance (a/compile [(int \f)]) nil \f ::fail)
ClassCastException java.lang.Character cannot be cast to java.lang.Number  automat.stream/to-stream/reify--91907 (stream.cljc:73)

to-stream's prior attempt to cover the narrow case of char input when expecting a number caused other issues (#34), but the char/int mismatch is just the most obvious/convenient/common case. Any FSM that requires a numeric input to advance, but encounters one that can't be coerced to a number will fail with a ClassCast instead of rejecting.

A straightforward way to address this would be to signal rejection in the reified nextNumericInput impl if the return isn't actually a number. That would cover the immediate problem.

One interesting/useful case that this wouldn't cover is where one has an FSM expecting a numeric input, it encounters something non-numeric, but you'd like to account for this via the :signal mechanism. Right now, that's not possible at all, but in such a case, it could "back off" to use nextInput, get the non-numeric result, apply the :signal fn, and then attempt to continue. This might belong in a separate enhancement issue, but I think it would nicely shave off a rough corner and make :signal much more general-purpose. At the moment, I'm "preprocessing" inputs to my FSM because some never get to :signal in the first place.

README example code errata

The following example under "Searching over streams" has some errors:

> (def f
    (a/compile
      [($:clear) (a/interpose-$ :conj (range 5))]
      {:clear (constantly [])
       :conj conj}))

I believe that should be (a/$ :clear) on line 3 and a missing {:reducers ...} wrapper on lines 4-5.

Graphviz Required to run View

Thanks for this amazing library Zach.
You may want to mention in the README.md that automat uses rhizome which in turn leverages Graphviz and that you need to have it installed first. If not for that fact that I suspected you were using other libraries of your own, I was stuck looking at a java error on UnixProcesses and no idea why!

Thanks again

Referencing a non-existent reducer should cause an error

If you mis-spell the name of a reducer function, it will be ignored silently, e.g.:

(let [fsm (a/compile [:foo :bar (a/$ :baar)]
                     {:reducers {:bar (fn [state _] (assoc state :bar true))}})]
  (reduce (partial a/advance fsm)
          {}
          [:foo :bar]))

I believe it's because of this line in the eval version, and this one in the base version, throwing away any nils after looking up the reducer in the reducers map. Wouldn't it be better to throw some sort of error if a requested reducer does not exist?

Inconsistency with Char

How to reproduce:

=> (a/advance (a/compile [\A]) nil \A)

ClassCastException java.lang.Character cannot be cast to java.lang.Number  automat.stream/to-stream/reify--19385 (stream.clj:70)

While this of course works:

=> (a/advance (a/compile [1]) nil 1)

And even this:

=> (a/advance (a/compile [\A]) nil 65)

Is there any fundamental reason for chars are mandatorily converted to ints when compilation happens?

Question, how do I view?

Hi, I'm new to clojure and when I try to do the example I'm unsuccessful.

$ lein repl
2016-12-21 09:00:52,663 [main] DEBUG org.jboss.logging - Logging Provider: org.jboss.logging.Slf4jLoggerProvider
nREPL server started on port 62218 on host 127.0.0.1 - nrepl://127.0.0.1:62218
REPL-y 0.3.7, nREPL 0.2.12
Clojure 1.8.0
Java HotSpot(TM) 64-Bit Server VM 1.8.0_25-b17
    Docs: (doc function-name-here)
          (find-doc "part-of-name-here")
  Source: (source function-name-here)
 Javadoc: (javadoc java-object-or-class-here)
    Exit: Control+D or (exit) or (quit)
 Results: Stored in vars *1, *2, *3, an exception in *e

user=> (require '[automat.viz :refer (view)])
nil
user=> (require '[automat.core :as a])
nil
user=> (view [1 2 3])

IOException error=2, No such file or directory  java.lang.UNIXProcess.forkAndExec (UNIXProcess.java:-2)
user=>

I'm on mac os x. What do I need to do to allow lein access to x11?

Wrong fsm/complement

The construction of the complement of an automaton (automat.fsm/complement) is wrong, as it appears by inspecting the svg of the complement of a, or of the complement of a*. Currently the function builds the complement by "switching" final and nonfinal states. This works only for complete DFAs but not for incomplete DFAs or NFAs (see https://en.wikipedia.com/wiki/Deterministic_finite_automaton for definitions). In the general case the construction should be as follows:

1- first, if the automaton is nondeterministic, make it deterministic; call d the obtained deterministic automaton;
2- then, if d is incomplete, make it complete by adding a "refuse all" nonfinal state S, and transitions s -[DEF]->S from all the states s to S; call c the resulting automaton;
3- finally, the final and nonfinal states of c are switched. Note that this way S becomes an "accept all" state.

In the current implementation both step 1 and step 2 are missing.

Customize viz (edge) labels

I'm generating FSMs that use integers as inputs/signals, but they're actually character codes. (Doing this so range and such are available.) I'd like to get characters on the visualizations' edges, but I don't see an easy way to do so. Looks like I could do unwise things to #'pprint-inputs as a workaround, but maybe I'm missing something more straightforward?

`greedy-find` not setting `:checkpoint`

I'm not sure if this is a bug, or just a confusion on my part about greedy-find vs find: The documentation is a little unclear, but it seems to suggest that the :checkpoint value would be set by greedy-find if a greedy match is found which skipped a non-greedy match.

For example with this automaton:

(def foo (a/compile 
          (a/or [:d :d (a/$ :first)]
                [:d :d :g (a/$ :second)])
          {:reducers {:first (fn [s t] :first)
                      :second (fn [s t] :second)}}))

I run e.g.:

boot.user=> (require '[automat.core :as a])
nil
boot.user=> (def foo (a/compile
       #_=>           (a/or [:d :d (a/$ :first)]
       #_=>                 [:d :d :g (a/$ :second)])
       #_=>           {:reducers {:first (fn [s t] :first)
       #_=>                       :second (fn [s t] :second)}}))
#'boot.user/foo
boot.user=> (a/find foo nil [:d :d])
#automat.compiler.core.CompiledAutomatonState{
    :accepted? true, :checkpoint nil, 
    :state-index 2, :start-index 0, 
    :stream-index 2, :value :first}
boot.user=> (a/greedy-find foo nil [:d :d])
#automat.compiler.core.CompiledAutomatonState{
    :accepted? true, :checkpoint nil, 
    :state-index 2, :start-index 0, 
    :stream-index 2, :value :first}
boot.user=> (a/find foo nil [:d :d :g])
#automat.compiler.core.CompiledAutomatonState{
    :accepted? true, :checkpoint nil, 
    :state-index 2, :start-index 0,
    :stream-index 2, :value :first}
boot.user=> (a/greedy-find foo nil [:d :d :g])
#automat.compiler.core.CompiledAutomatonState{
    :accepted? true, :checkpoint -->nil<---, 
    :state-index 3, :start-index 0,
    :stream-index 3, :value :second}

The first 3 calls are as expected. I'm not finding the full sequence [:d :d :g] because either I didn't pass in enough tokens, or because I did a non-greedy find.

In the 4th example, I find the full pattern, and get the expected :value. But should the :checkpoint not have been set to the previous value? (I'm not sure what to expect here - perhaps the :first symbol, or the state-index 2, but certainly something to indicate that the most recent non-greedy find.)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.