Giter Site home page Giter Site logo

amir-hadi / state-machine-cat Goto Github PK

View Code? Open in Web Editor NEW

This project forked from sverweij/state-machine-cat

1.0 1.0 0.0 21.31 MB

write beautiful state charts :scream_cat:

Home Page: https://state-machine-cat.js.org

License: MIT License

Makefile 1.47% JavaScript 89.51% HTML 5.79% Shell 3.22%

state-machine-cat's Introduction

State Machine cat

write beautiful state charts

build status Build Status coverage report Maintainability Test Coverage npm stable version MIT licensed state-machine-cat.js.org

What?

Makes this

docs/pics/sample.png

from this

initial,
doing: entry/ write unit test
       do/ write code
       exit/ ...,
# smcat recognizes initial
# and final states by name
# and renders them appropriately
final;

initial      => "on backlog" : item adds most value;
"on backlog" => doing        : working on it;
doing        => testing      : built & unit tested;
testing      => "on backlog" : test not ok;
testing      => final        : test ok;

Why

To enable me to make state charts ...

  • ... that look good
  • ... with the least effort possible
  • ... whithout having to interact with drag and drop tools. Entering text is fine, doing my own layout is not.
  • ... without having to dive into GraphViz dot each time. GraphViz is cool, but is was not designed to write & maintain conceptual documents in (You'll know what I'm talking about if you ever tried to get it to draw nested nodes. Or edges between those. )

Usage

On line

A no-frills interpreter on line: state-machine-cat.js.org.

Within the Atom editor

There's an Atom package with syntax highlighting, a previewer and some export options. You can install it from within Atom (search for state machine cat in the install section of the settings screen) or use apm i state-machine-cat-preview if you're a command line person.

OTOH. if you're a command line person the command line interface might be something for you too:

Command line interface

Just npm install --global state-machine-cat and run smcat

This is what smcat --help would get you:

Usage: smcat [options] [infile]

Options:
  -V, --version               output the version number
  -T --output-type <type>     svg|dot|smcat|json|ast|html|scxml|scjson|xmi (default: "svg")
  -I --input-type <type>      smcat|json (default: "smcat")
  -E --engine <type>          dot|circo|fdp|neato|osage|twopi (default: "dot")
  -d --direction <dir>        top-down|bottom-top|left-right|right-left (default: "top-down")
  -o --output-to <file>       File to write to. use - for stdout.
  --dot-graph-attrs <string>  graph attributes to pass to the dot render engine
  --dot-node-attrs <string>   node attributes to pass to the dot render engine
  --dot-edge-attrs <string>   edge attributes to pass to the dot render engine
  -l --license                Display license and exit
  -h, --help                  output usage information

... so to convert the above chart to sample.svg

bin/smcat docs/sample.smcat

Or, if you'd rather have the native GraphViz dot do that for you:

bin/smcat -T dot docs/sample.smcat -o - | dot -T svg -odoc/sample.svg

Leaving the options at the default settings usually deliver the best results already, so if they bewilder you: don't worry.

The --dot-graph-attrs (and the node and edge variants thereof) exist in case you want to override default attributes in the generated picture; e.g. to get a transparent background and draw edges as line segments instead of splines, use this:

bin/smcat --dot-graph-attrs "bgcolor=transparent splines=line" docs/sample.smcat

State chart XML (SCXML)

state machine cat can emit a valid core constructs scxml document. If you're into that sort of thing you can read all about it in State Machine Cat and SCXML.

XML Metadata Interchange (XMI)

If you want to write state charts in state machine cat and use that to make working state machines using a tool that eats XMI and emits code you can do just that - read more in State Machine Cat and XMI

Programmatically

After you npm i 'd state-machine-cat:

const smcat = require("state-machine-cat");

try {
    const lSVGInAString = smcat.render(
        `
            initial => backlog;
            backlog => doing;
            doing => test;
        `,
        {
            outputType: "svg"
        }
    );
    console.log(lSVGInAString);
} catch (pError) {
    console.error(pError);
}

Read more in docs/api.md

The language

Short tutorial

simplest

on => off;

rendition

  • smcat automatically declares the states. You can explicitly declare them if you want them to have more than a name only - see state declarations below.

labels

on => off: switch;

rendition

UML prescribes to place conditions after events, to place conditions within squares and to place actions after a /: from => to: event [conditions]/ actions, e.g. on => off: switch flicked [not an emergency]/ light off;.

You're free to do so, but smcat doesn't check for it. It internally takes the notation into account, though and if you choose to export to json, scxml or scjson you'll see them nicely split out.

on => off: switch flicked/
           light off;
off => on: switch flicked/
           light on;

rendition

You note that smcat rendered the states in this chart top down instead of left to right. It did that because we told it so. You can do that too with --direction on the command line, or in atom by direction -> left to right or direction -> top down from the chart's submenu.

notes

# this is a note
on => off;

rendition

state declarations

If you need to define activities (e.g. entry or exit triggers) on a state, you can explicitly declare the state and put the activites after a colon:

# yep, notes get rendered here as well
# multiple notes translate into multiple
# lines in notes in the diagram
doing: 
  entry/ make a feature branch
  exit/ deploy code on production
  ...;

rendition

smcat recognizes the entry/ and exit/ keywords and treats everything after it on the same line to be the 'body' of the trigger.

Here too: you're free to use them, but you don't have to. smcat takes them into account in its internal representation and uses them in exports to json, scxml and scjson.

state display names

If you want to use a display names that differ from how you name the states (e.g. if the display names are long), you can do so by adding a label to them:

on [label="Lamp aan"],
off [label="Lamp uit"];

off => on: on pushed;
on => off: off pushed;

rendition

initial and final

When initial or final, is part of a state's name smcat treats it as the UML 'pseudo states' initial and final respectively:

initial => todo;
todo    => doing;
doing   => done;
done    => final;

rendition

history

smcat recognizes states with history in their name als history states:

initial,
"power off",
running {  
  running.history;

  washing -> rinsing: 20 min;
  rinsing -> spinning: 20 min;
  spinning -> final: 20 min;
},
final;

initial => washing;
running => "power off": power out;
"power off" => running.history: restore power;

rendition

History states are shallow by default. If you want a history state to be deep just put that somewhere in the name (e.g. "running deep history" or running.history.deep) - and smcat will render them as such.

Choice - ^

smcat treats states starting with ^ as UML pseudo state choice. Strictly speaking 'choice' is a superfluous element of the UML state machine specification, but it is there and sometimes it makes diagrams easier to read.

^fraud?: transaction fraudulent?;

initial -> reserved;
reserved -> quoted:
    quote
    requested;
quoted -> ^fraud?: payment;
^fraud? -> ticketed: [no];
^fraud? -> removed: [yes];
ticketed -> final;
removed -> final;

rendition

Forks, joins and junctions - ]

In UML you can fork state transitions into multiple or join them into one with the fork (1 to many) join (many to 1) and junction (many to may) pseudo states. All three of them are represented by a black bar. To make a join, fork or junction pseudo state, start its name with a ]. Here's an example of a join:

a => ]join;
b => ]join;
]join => c;

rendition

State machine cat automatically derives which of the three types you meant by counting the number of incoming and the number of outgoing connections:

  • 1 incoming and multiple outgoing: it's a fork
  • multiple incoming and 1 outgoing: it's a join
  • all other cases: it's a junction

If you want to defy UML semantics you can do that with explicit type overrides .

a => ]kruising
b => ]kruising;
]kruising => c;
]kruising => d;

rendition

Terminate

UML has a special pseudo state to indicate your state machine didn't exit properly: terminate. If you want to use it, declare it explicitly:

Aahnohd [type=terminate label="Terminated"];

a => Aahnohd: [hit by meteorite];

rendition

For proper exits you'd typically use the final state.

Gotchas

  • when you need ;, ,, {, [ or spaces as part of a state - place em in quotes "a state"
  • Activities have the same restriction, except they allow spaces.
  • Labels have the same restriction as activities, except they allow for , too.
  • State declaration precedence is: deep wins from shallow; explicit wins from implicit
  • It's possible to declare the same state multiple times on the same level, buts smcat will take the last declaration into account only. For example:

This

# first declaration of "cool state"
"cool state",
"other state",
# second declaration of "cool state"
"cool state": cool down;

results in (/ is equivalent to):

# second declaration of "cool state"
"cool state": cool down,
"other state";

nested state machines

It's possible to have state machines within states. the states stopped, playing and pause can only occur when the tape player is on:

initial,
"tape player off",
"tape player on" {
  stopped => playing : play;
  playing => stopped : stop;
  playing => paused  : pause;
  paused  => playing : pause;
  paused  => stopped : stop;
};

initial           => "tape player off";
"tape player off" => stopped           : power;
"tape player on"  => "tape player off" : power;

rendition

parallel states

If stuff is happening in parallel within a state you can express that too. Just make sure the state has the word "parallel" in there:

initial,
bla.parallel {
    first{
        first.thing -> first.thang;
    },
    second{
        second.thing -> second.thang;
        second.thang -> second.thing;
    };
},
final;

initial -> ]split;
]split  -> first.thing;
]split  -> second.thing;
first.thang  -> ]merge;
second.thang -> ]merge;
]merge  -> final;

parallel

marking states as active

You can mark one or more states as active by adding active as an attribute for that state e.g. to make the do state an active one do this:

do [active];

initial -> plan;
plan    -> do;
do      -> study;
study   -> act;
act     -> plan;

which will result in

parallel

colors

As of version 4.2.0 state-machine-cat has (experimental) support for colors on both states and transitions.

For example, this ...

eat   [color="#008800"],
sleep [color="blue" active],
meow  [color="red"],
play  [color="purple"];

sleep -> meow  [color="red"]    : wake up;
meow  -> meow  [color="red"]    : no response from human;
meow  -> eat   [color="#008800"]: human gives food;
meow  -> play  [color="purple"] : human gives toy;
play  -> sleep [color="blue"]   : tired or bored; 
eat   -> sleep [color="blue"]   : belly full;

... would yield this diagram: colored states and transitions

What does 'experimental' mean?

The color attribute is probably here to stay.

However, I haven't found the balance between ease of use and expressiveness yet. Should the text in a state be rendered in the same color? should the background color automatically be rendered as well? In the same color, or in a shade smartly derived? Or should I include a bunch of color attributes (e.g. fillcolor, textcolor, textbgcolor) for ultimate control?

overriding the type of a state

As you read above, state machine cat derives the type of a state from its name. In some cases that might not be what you want. In those cases, you can override the type with the type attribute, like in this example for the initialized state.

initial,
starting,
initialized [color="red" type=regular],
running,
final;

initial     => starting;
starting    => initialized;
initialized => running;
initialized => stopped;
running     => stopped;
stopped     => final;

colored states and transitions

The values you can use for type:

type
regular
initial
final
terminate
parallel
history
deephistory
choice
fork
join
forkjoin
junction

grammar

I made the parser with pegjs - you can find it at src/parse/peg/smcat-parser.pegjs

Status

  • Thoroughly tested and good enough for public use.
  • Despite this you might bump into the occasional issue - don't hesitate to report it, either on GitLab or on GitHub.
  • Runs on latest versions of firefox, safari and chrome and node versions >= 6. Although it might run on other environments, it's not tested there. I will reject issues on these other environments, unless they're accompanied with sufficient enticement to make it worth my while.

docs/pic/smcat-full-small.png

state-machine-cat's People

Contributors

sverweij avatar

Stargazers

 avatar

Watchers

 avatar

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.