brucou / slim

Compiles a Kingly state machine drawn with the professional graph editor yEd
4 stars 0 forks source link

How to adapt to a javasript framework? #3

Closed j2l closed 2 years ago

j2l commented 2 years ago

Hello, Your approach from diagram to code is fantastic! I'm discovering state machines but know yED. Thank you! What would be need to generate enhanced javascript code like react, vue, svelte? (the later is very close to javascript) Adapt by hand yedEdgeLabelGrammar.ne and js? Something else? Thank you!

brucou commented 2 years ago

Bonjour Philippe,

yEd is about the best diagramming tools out there for modeling, and I tried many. I am glad you like it.

What do you want to achieve? You want to:

If you want to keep the same edge label grammar, then you don't need to change the yedEdgeLabelGrammar.ne. That grammar is pretty simple and is as follows:

MAIN -> OneTransitionLabel                             {% d => d[0] %}
| MultipleTransitionLabel                              {% d => d[0] %}
MultipleTransitionLabel -> ("|" OneTransitionLabel):+  {% d => d[0].map(x => x[1]) %}
OneTransitionLabel ->
  EventClause "[" GuardClause "]" _ "/" ActionsClause  {% d => ({event: d[0], guard: d[2], actions: d[6]}) %}
| EventClause "[" GuardClause "]" _                    {% d => ({event: d[0], guard: d[2], actions: []}) %}
| EventClause "/" ActionsClause                        {% d => ({event: d[0], guard: [], actions: d[2]}) %}
| EventClause                                          {% d => ({event: d[0], guard: [], actions: []}) %}
| "[" GuardClause "]" _ "/" ActionsClause              {% d => ({event: "", guard: d[1], actions: d[5]}) %}
| "/" ActionsClause                                    {% d => ({event: "", guard: [], actions: d[1]}) %}
| "[" GuardClause "]"                                  {% d => ({event: "", guard: d[1], actions: []}) %}

EventClause -> [^\/\[\]\|,]:+                           {% d => d[0].join('') %}
GuardClause -> Guard ("," Guard):*                     {% d =>  [d[0]].concat(d[1].map(dd => dd[1]))  %}
Guard -> [^\[\],]:*                                     {% d => (d[0] || []).join('') %}
ActionsClause -> Action ("," Action):*                 {% d =>  [d[0]].concat(d[1].map(dd => dd[1]))  %}
Action        -> [^\/\|\[\]\(\),]:* ActionsComments:*  {% d => (d[0] || []).join('') %}
ActionsComments -> "(" __ ")" _
StringLiteral -> [\w]:+
  _ -> [\s]:*
  __ -> [^()]:*

This file contains both a description of the label grammar with an eBNF-like syntax and instructions to convert a parse tree into an abstract syntax tree (the %d =>...). You can play with it in the Nearley playground. Simply copy paste that grammar into the playground. Type some edge label on the input field in the right, and you will see the results of the parsing. .ne denotes a Nearly extension. Nearley is the parser I use here. It is not the fastest parser but it is the most user-friendly (playground, ambiguity tolerance, etc.). The .ne file is compiled to a .js file that does the actual parsing. So Nearley is in fact a parser generator. It takes a language description file (.ne) and generates a parser (.js) with a CLI (cf. Nearley documentation).

If you want to change the output, then you need to modify index.js. Namely you probably should write new code starting there. You should be able to use what is before: the parsing logic, the file reading and writing, and the data transformations. What you will have to change is the compiledContents.

I hope that helps.

Tell me more about you want to do. If I understand your use case, I am happy to support you into generating your Svelte code. And also, you can speak French if that's better for you :-)

j2l commented 2 years ago

Bonjour ! Are you also French? I don't mind continuing writing in English to help others (use case through SEO) for the moment. I might switch to French if it goes deep though.

Thank you very much for all this information. OK, let's go ideal: I'd like to design in yED, and push a big red button (or a command line like slim or yed2kingly) to:

XState tried various javascript platforms (https://xstate.js.org/docs/recipes/svelte.html) and a particular svelte demo is pretty self explanatory about the diagram+code: https://github.com/kyrianda/svelte-xstate-inspect-plantuml/ But it totally lacks the awesomeness of designing in a robust (and so great at automatic layouts) diagramming solution like yED.

BTW, did you know you can start building yED diagrams from a table? https://yed.yworks.com/support/manual/import_excel.html This helps drafting big diagrams and avoid labeling mistakes for connections.

brucou commented 2 years ago

I do understand the French language :-) But yes English may be better. So:

Otherwise:

The great with state machines is that you don't really have to look at the code anymore. And if you keep it simple like Kingly tries to do, there is not much to learn and visualize. Anyways, I may try to turn the react-state-driven component into a svelte-state-driven component if you are interested to test it for your use case.

brucou commented 2 years ago

@j2l I just realized I already did a Svelte integration: https://github.com/brucou/svelte-machine-component :-)

Just that I did not document it. It is 60 lines of code, so not too hard to understand, but I should write a proper documentation for it. I may take advantage from your interest to refresh the implementation and document it. I don't really remember what I did there, that was two years ago and apparently I never used it - it is just so simple with Svelte to directly used the machine in a component that making a wrapper does not have so many advantages.

j2l commented 2 years ago

Hihi, I also forget what I did a few years ago. Thank you! I'll take time to study this. Rapidly, it seems complicated, maybe because of svelte back.

brucou commented 2 years ago

Yeah it took me a while to get into it. This uses Svelte slot props which is a bit of a convoluted feature in any template language (e.g., same applies to Vue or Angular). So basically it goes like this:

How does this work?

If you have a concrete example to use this with, I can guide you through how to do it. Just let me know. But again, I think that inlining works best, and that's probably why I did not publish this component in the end.

j2l commented 2 years ago

Hello! OK, I'm back to svelte and fsm. I hope you had time to finalize tests :smile:

As I wanted a real-life example to run svelte from, I started with the password fsm.

I successfully generated the password-meter.graphml.js password-meter.graphml.fsm.js.zip

I tried to add the js file from the doc (https://codesandbox.io/s/mqx96pm64j?from-embed=&file=/src/index.js) and the basic html (div app) and import it as svelte component import KinglyPassword from "./kinglyPassword.svelte";

I adapted the INIT_SCREEN:

  const INIT_SCREEN = `
    <section>
      <label for="password">Enter password</label>
      <input
        type="password"
        name="password"
        id="password"
        onkeyup="${inputEventHandler}"
      />

      <meter max="4" id="password-strength-meter"></meter>
      <p id="password-strength-text"></p>
      <button onclick="${buttonEventHandler}">Submit</button>
    </section>
  `;

But it never shows up (maybe because svelte needs @html?) I have no error in the console though, just the Kingly messages.

Maybe using the JS example is not the best way to make it work?

Si I tried another way I understand that, in App.svelte, I'll need:

import { NO_OUTPUT } from "kingly";
  import emitonoff from "emitonoff";
  import {
    events,
    states,
    getKinglyTransitions,
    createStateMachineFromGraph,
  } from "../password-meter.graphml.fsm.js";
  let commandHandlers, effectHandlers;
  let initEvent = {};
  let fsm = createStateMachineFromGraph(events, states, getKinglyTransitions);

But then, I don't know what to add to reach something like: <Machine commandHandlers=... initEvent=... fsm=... let:dispatch={dispatch}></Machine>

brucou commented 2 years ago

Great to hear that you are back to Svelte and fsm :-) I will add a Svelte example for the password meter (hopefully tonight) to the documentation. We can then take it from there and address any questions you may have. What do you think?

j2l commented 2 years ago

Yes, if you could use the graph js file I attached, it's perfect!

brucou commented 2 years ago

@j2l I made a copy paste of the Vue implementation of the password meter, and converted it to Svelte. Quick and dirty but there it is: https://codesandbox.io/s/inspiring-wright-tdrxk?file=/App.svelte

Will have a look at your password-meter.graphml...js file tomorrow. But generally speaking, and reviewing our exchange from the beginning, I was telling you that abstracting the fsm into a component resulted in somewhat convoluted code with Svelte and that directly using the code was not a bad option. Cf. code comments, I will abstract a bit more the code tomorrow.

Going to bed!

j2l commented 2 years ago

This is great! Thank you! Can't wait for a yed to svelte FSM to happen.

brucou commented 2 years ago

Here is the version with the event handler logic abstracted: https://codesandbox.io/s/fast-breeze-9736z?file=/App.svelte.

As you can see the abstraction is a 3-liner. That is very much how I would use Kingly fsm with Svelte. Abstracting with a <Machine> component, while seemingly attractive, is not the best option in my opinion with Svelte.

I realize that I still haven't looked at your file :-) Will do tomorrow. But you see that once you have your machine exported (that's fsm.js), it does not matter how you created that machine (with createStateMachineFromGraph, or from the slim compiler, or directly written by you).

More tomorrow

brucou commented 2 years ago

Well I did it now https://codesandbox.io/s/billowing-currying-0jlkr?file=/fsm.js

Gotchas:

j2l commented 2 years ago

Wow! Trop bien !

Thank you soooo much for delivering so fast and perfectly. I really appreciate you took the challenge. I'll study this ASAP. Closing.

Excellente journée à toi !!!

brucou commented 2 years ago

Merci, si tu as d'autres questions, tu me dis. Je vais ptet actualiser la docs et rajouter Svelte quand meme.

j2l commented 2 years ago

Ca marche, merci encore.