Starting JS-Interpreter from a specific block

385 views
Skip to first unread message

Livio Cicala

unread,
Oct 16, 2022, 2:15:09 PM10/16/22
to Blockly
Does anyone know how to start JS-Interpreter from a specific block or function?

Normally it starts from the beginning but I need to call different parts of the program in response to different events.

My Dot Net application runs locally on a PC and I have external events (buttons, keyboard keys, mouse etc ...)

And from each event I need to call a different function in the Blockly workspace.

If anyone is interested in contributing to my application this is the download link:
 
This app communicates via the "Theremino System Slots" with the other (more than 150) apps of the System, all completely free and open source. In this way the Blockly application can access enormous resources, from oscilloscopes to radiation detectors, from speech recognition to earthquake detectors, to quality midi sounds, etc ...

You can see (and download) all the other apps here:




Message has been deleted

Neil Fraser

unread,
Oct 16, 2022, 2:32:22 PM10/16/22
to blo...@googlegroups.com
The best way to inject events into the JS-Interpreter is to use "appendCode".  Thus Blockly would generate a bunch of functions which you'd pass to JS-Interpreter:
var myInterpreter = new Interpreter(`
function onFoo() {...}

function onBar() {...}

`);
When you run this, the functions are created, but that's it.  Then whenever you want to call one of them, you'd do:

myInterpreter.appendCode('onFoo();');

Then run the interpreter.


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/33c8f167-0672-4ad0-80da-c42ad77c5e04n%40googlegroups.com.


--

Livio Cicala

unread,
Oct 16, 2022, 2:38:45 PM10/16/22
to Blockly
I noticed that the download link is not working but you can copy the link and download it using a different page of the browser.

I can assure you that our applications are safe and checked with the best security programs, if you have any doubts before opening the ZIP test it with the best security programs, for example these:
https://virusscan.jotti.org

I hope I have not broken any rules of the site, in that case tell me, thanks.

Livio Cicala

unread,
Oct 16, 2022, 2:54:29 PM10/16/22
to Blockly
Thanks Neil for the super-fast response.

I think I understand, tomorrow I will try to write it and I will work hard to do it alone.

I am an expert programmer, with over 50 years of experience, in all classic programming languages but I have no experience of WEB languages such as Java, JavaScript and the like. And I understand them little. So if I can't, I'll bother you again.

Livio Cicala

unread,
Oct 18, 2022, 1:03:41 PM10/18/22
to Blockly
After several experiments I realized that what I need is a little different. 
I try to explain myself better.

It is not a question of adding named functions and then calling them but of calling the "function" type blocks that the user himself creates in the workspace.

I am attaching an image that shows what I am trying to do.
The two buttons on the left are dynamically created when the user creates the function blocks in the workspace and pressing them should execute them.

CallingFunctions.png

I also attach links to two ZIP files:

- The first contains the application ready for those who want to try it

- The second the project with the sources for those who want to try to complete it.
http://www.theremino.com/files/Theremino_Blockly_V1.0.40.zip

Livio Cicala

unread,
Oct 18, 2022, 1:06:08 PM10/18/22
to Blockly
I noticed that the download links are not working but you can copy the link and download it using a different page of the browser.

Mark Friedman

unread,
Oct 18, 2022, 4:36:27 PM10/18/22
to blo...@googlegroups.com
Livio,

  Have you seen these resources for using the JS Interpreter:
  Do any of the above help answer your question?  If not, can you be specific about what you need?

  Hope this helps.
 
-Mark


--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.

Livio Cicala

unread,
Oct 19, 2022, 4:01:28 AM10/19/22
to Blockly
Thanks Mark!
These three links will surely be of great help to me.
I begin to understand that there are no ready-made functions to call a specific block, but that I have to read the Java Script produced and work on it.

I'll have to find a way to start execution from a specific line of JavaScript, or rather from a line that is the first of a highlighted block or "function block" of Blockly.

And I'll probably have to stack the return address as well, so I can do a "return" at the end of the called function.

And while doing these operations the interpreter must not be re-initialized, otherwise its internal states and variables are lost.

------------------------

I will try to learn how to do this. The problem is that I am a senior programmer with over 40 years of experience and over two hundred important open-source applications appreciated all over the world, written in C, C ++, Pascal, Cobol, Fortran, BasicScripts, CSharp, VbNet, etc. But I don't know JavaScript at all. And I don't even know all the other web languages, Html, Java, Phyton etc .. So when I read JavaScript it seems Ostrogoth to me.

Also I have always been a fundamentalist maniac of indentation and for me every opening parenthesis has to move the indent to the right and every closing parenthesis has to move it to the left in exactly the same starting column, so when I read JavaScript with its weird indent habits, the eyes cross and understanding drops to zero.

However, I will persist until I am successful. And my application will open Blockly access to huge Input-Output resources to build DIY scientific applications that run locally on a PC (to see them follow the links I posted in previous posts).

Gregory Dyke

unread,
Oct 19, 2022, 8:30:54 AM10/19/22
to blo...@googlegroups.com
Hi Livio

Could you clarify what you want from your screenshot above?
  • What happens when "run" is pressed? I assume the sayHallo function is called (because there are 3 top level blocks: 2 function declarations and a function call)
  • At what point are the "sayHallo" and "sayGoodbye" buttons created? As soon as they are added to the workspace? Or after run is pressed?
I ask because there will be some difficulty to have something consistent if the workspace is a combination of function definitions and code that is executed immediately when run is called on the generated code from the workspace

Let's assume for simplicity:
  • you want pressing the buttons to happen in the same execution environment (so if you have a sayMyNumber function and an incrementMyNumber function, alternatively pressing these buttons will say 0, then 1 then 2 then 3 etc.)
  • either the workspace code is run on every change (seems unsafe) or the buttons are only expected to work after run is already pressed
There are then 3 parts to build up:
  • set up the interpreter with defined functions
  • create the buttons
  • trigger code execution when the buttons are pressed
Set up the interpreter with defined functions

var latestCode = generateCode();
myInterpreter = new Interpreter(latestCode, initApi);
myInterpreter.run(); // this "initialises" the interpreter so that it is in a state ready for functions to be called

Trigger code execution when the buttons are pressed

This is as Neil mentioned above
myInterpreter.appendCode('sayHallo();'); // add the "sayHallo()" call to the not-yet-executed code in the interpreter
myInterpreter.run(); // now execute the not-yet-executed code

Create the buttons

There are several ways you could do this:
  • look for function definitions in the generated code using a regex
  • use workspace.topLevelBlocks to get all your blocks and filter to only include function blocks (I think it's block.type == "function")
  • modify the code generator for function blocks to also call an API that you define that will create a button
Because there is not an exact match between the user-visible function names and the generated code function names
(e.g. to avoid clashed with reserved names, I think the 3rd option is the right way to go). As a proof of concept,
I would probably go with the 2nd option and create my buttons based on that.

Hope this helps
Greg

Livio Cicala

unread,
Oct 19, 2022, 1:49:15 PM10/19/22
to Blockly
Hi Greg, thanks !!!
You are giving me just the advice I need and it is a great help. 

Now I answer your questions:

Yes, when you press RUN, exactly what you expected happens and the precise sequence is this:
- I initialize the JS interpreter
- I repeatedly call the Step () function until it answers false because the code is finished.
So the SayHallo block is executed, which in turn calls the SayHallo function, which then in turn calls SayGoodbay and everything works as expected, it talks it waits for the right pauses and finally stops.

The SayHallo and SayGoodBye buttons are dynamically created each change of the workspace  using AddChangeListener and watching the Blockly.Events.CHANGE / .CREATE / .DELETE and .MOVE events
And then reading the code with: Blockly.JavaScript.workspaceToCode (workspace);
From then on I play at home in DotNet and then the buttons are always created and disappear exactly as they should. And they also do this when editing function names.

You should try my application on a PC which (apart from calling the functions) already works very well.

---------------------

As for what to do when pressing buttons, I'm still browsing through various possibilities.

The best would be to be able to call related functions stopping whatever is happening, so you would have to save the current "program counter" to a stack and then reset it at the end of the function and let execution continue where it came from. But if this is too difficult we could (at least at first) run the function and then stop when finished.

-----------------------

I attach the ZIP files of the new version 41:

- The first contains the application ready for those who want to try it

- The second the project with the sources for those who want to try to complete it.

Livio Cicala

unread,
Oct 19, 2022, 4:36:32 PM10/19/22
to Blockly
Hello everybody,
I tried Neil and Mark's solution and it works.

Two problems remain:
1) If there are blocks outside each function, they are executed before the function you want to call.
2) It is not possible to save a return address on the stack and continue with the interrupted execution after the function has been executed.

These problems exist whether you use myInterpreter.run(); whether you call myInterpreter.step() repeatedly; as I prefer to have the hilight of the blocks and the possibility to stop the execution at any moment (incidentally calling from DotNet I reached a very fast execution speed, about 100 uS each statement on a fast computer).

To get better I think I will have to write a function similar to myInterpreter.step (); but improved in two respects:
- It must be able to start from any block you want.
- It must allow you to save the return address in a stack.

Sure Neil would do it better than I can, but I'll try and succeed. It won't be as perfect as Neil could but I will be able to make it work.

(sorry for my bad English)
-----------------------

I attach the ZIP files of the new version 42:

- The first contains the application ready for those who want to try it

Mark Friedman

unread,
Oct 19, 2022, 5:04:45 PM10/19/22
to blo...@googlegroups.com
Livio,

  Unfortunately, I mis-pasted a link in the email that I sent you, so you might have missed the Blockly documentation for running the JS Interpreter.  In that documentation is the section on stepping the interpreter, which shows a code snippet for slowing down the interpreter:

function nextStep() {
  if (myInterpreter.step()) {
    setTimeout(nextStep, 10);
  }
}
nextStep();

You can use a variant of that (recently posted by Neil) to stop the interpreter when the user takes some action:

var stepPid;
function StepUntilStopped() {
  if (myInterpreter.step()) {
    stepPid = setTimeout(nextStep, 10);
  }
}
stepUntilStopped();
function stop() {
  clearTimeout(stepPid);
}

When the user takes their action you would call the stop() function.  To resume, you could just call stepUntilStopped() again.  The interpreter is essentially saving the return address for you during each step of the interpreter.

-Mark


Livio Cicala

unread,
Oct 20, 2022, 3:39:22 AM10/20/22
to Blockly
Thanks Greg,
in fact I am already using a method very similar to this one, but more elaborate, which allows to:
- Execute step by step, highlight blocks and even to stop execution at any time.
- Make the "wait seconds" blocks work and be able to stop them in the middle without having to wait for them to expire if they are very long (and it was difficult).
- Execute also step by step, showing the block being executed and the values ​​of the variables at each step.
- Achieve decent execution speeds (about 10,000 instructions per second), and at the same time be able to stop at any time, to be able to control motors and PIDs in real time, almost with the same convenience and speed as our other automation applications.

What's still missing is the ability to start execution from a block of your choice (probably based on the block ID)
And also the ability to interrupt the execution without reinitializing the interpreter as I am doing.
And have the ID of the block (or maybe the micro-instruction) that was last executed in order to be able to save it to a stack.

It is true that the myInterpreter.step() function saves the return address but does so internally and does not provide it.
And also this function cannot restart from that point after executing another function because to execute other code you always use myInterpreter.step() and then that address is lost.

In other words, myInterpreter.step() can only stop and restart where it left off, it cannot perform any other functions and thus "go back" to where it left off.
I really think I'll have to replace this feature with a more elaborate one.
It will take me ten times longer than Neil because I will have to learn a lot but in the end I will succeed.

Thanks again very much for your attention, your suggestions are of great help to me.

Livio Cicala

unread,
Oct 20, 2022, 3:42:14 AM10/20/22
to Blockly
I wrote Thanks Greg, but in reality it was: Greg and Mark !!!
(and another time sorry for my bad english)

Mark Friedman

unread,
Oct 20, 2022, 12:51:31 PM10/20/22
to blo...@googlegroups.com
Livio,

  Have you read through the JS Interpreter documentation?  It might help answer some of your questions.  I'll also add that the
javascriptGenerator.blockToCode() function might be useful to generate the code for your block and pass the resulting code to your interpreter (perhaps via the interpreter's appendCode() method.  One thing to keep in mind is that javascriptGenerator.blockToCode() will return either a string (containing the code) or a two-element array (whose first entry is the code). 

  You mention "... interrupt the execution without reinitializing the interpreter".  Why are you reinitializing the interpreter?  If you don't reinitialize it then you could just continue the execution where you left off by running stepUntilStopped() (or your equivalent).  Do you really need the stack of return values, as you mentioned?  If you do, then you might be interested in the "Serialization" section of the JS Interpreter documentation, which essentially allows you to capture the continuation of your paused interpreter and restart it later.

-Mark


Gregory Dyke

unread,
Oct 20, 2022, 1:20:51 PM10/20/22
to blo...@googlegroups.com
Hi Mark

I think what Livio specifically does not want to do is use appendCode (that will put it right at the end, right, not at the current step). Instead he would like to be able to splice code (so instead of appendCode('sayHallo();'), it would be spliceCode('sayHallo();')) into wherever the interpreter currently is?

I would assume for that he'd have to modify the interpreter (assuming it can be convinced to parse and execute arbitrary code between 2 steps).

Livio, do I understand correctly, assuming you have 2 top level blocks:
- a function displayCount
- a loop that increments a count variable from 1 to 1'000'000 (which might take a few minutes to execute) and then calls displayCount()

You would expect
- user presses run (this starts stepping through the code until it's done)
- user presses displayCount (this interrupts the stepping, calls the displayCount function (displaying maybe 523'345)
- the code now continues stepping through the original loop
- at the end, the displayCount function is called (displaying 1'000'000)

If you don't need such interactions between the state of the currently executing code and the state of the loop, but it's still important for the user to be able to execute these functions during other the running of other code, what I would do is copy all the top level functions into a new workspace, generate code from that, create a new interpreter with just these top level functions, appendCode("sayHallo();") to that interpreter, run it. When that is terminated, resume stepping the original interpreter.

Does that help?
Greg

p.s. Livio, your framing of wanting the interpreter to "start executing from a specific line" is just wrong. Taking your original example in the screenshot, the generated code would have 2 function declarations and a call to sayHallo();. If the user pressed the sayGoodbye button, there is nowhere for the interpreter to "go" as there is no call of the sayGoodbye() function in the code the intepreter knows about.

Livio Cicala

unread,
Oct 20, 2022, 4:16:28 PM10/20/22
to Blockly
I am trying to understand...
...  there is nowhere for the interpreter to "go" as there is no call of the sayGoodbye() function ...

The sayGoodbye() function is present in the code, why it is not possible to call it directly ?
Maybe this is related to the "sandboxing" ?

Gregory Dyke

unread,
Oct 20, 2022, 9:07:05 PM10/20/22
to blo...@googlegroups.com
Hi Livio

I don't think it's related to the sandboxing, but to how the interpreter is designed to work (as near as I can tell - I could be slightly wrong and I haven't looked at its internal workings).

Screenshot 2022-10-21 at 01.46.03.png
So when we create a new interpreter based on this string of code, the interpreter parses the code and creates an AST (abstract syntax tree). something like:
  • FunctionDefinition(sayHello)
    • StatementList
      • call(console.log, "hello")
      • call(sayGoodbye)
  • FunctionDefinition(sayGoodbye)
    • StatementList
      • call(console.log, "goodbye")
  • StatementList
    • ForLoop(VariableInitialisation(i, 0), BinaryOperation(<, VariableRef(i), 1000), UnaryOperation(++, VariableRef(i))
      • StatementList
        • call(sayHello)
The interpreter then will traverse this AST, executing the top level statement list. And when there is a call, it will jump to the functionDefinitions statementList (with some placing of variables on the stack, etc.) then jump back to the original call site (popping any local context back off the stack). The only way to jump to the sayGoodbye function is by having a call(sayGoodbye) element in the AST. But there isn't such a call anywhere (the one inside sayHello doesn't count - because you'd have to call sayHello to get there).

The only way you can get the interpreter (as it currently exists) to include a call(sayGoodbye) in its AST is to append it at the end using interpreter.appendCode("sayGoodbye();"). But that apparently doesn't cover your use case?

You instead want the interpreter to jump from wherever it is into sayGoodbye when the sayGoodbye button is pressed. For that, I'm pretty sure you would need to modify the interpreter to give it the possibility to execute a statement (which would not be part of its AST) from its current state. It can't currently do that, because the interpreter's job is to traverse the AST in the correct order. You're asking it to stop being an AST traverser and instead temporarily do something else.

(Note the javascript console in a browser, during debugging is able to do this. You can step using the debugger functionality. Or you can execute any code from the console by typing it and pressing enter). But this is because javascript consoles in web browsers have this dual capability: stepping through the currently-being-interpreted-code, or executing arbitrary code in the repl.)

Does this make things any clearer? Or am I confused as to what you're trying to achieve?
Greg

Gregory Dyke

unread,
Oct 20, 2022, 9:17:42 PM10/20/22
to blo...@googlegroups.com
I just realised you could do something similar to the multi-threaded example here: https://neil.fraser.name/software/JS-Interpreter/demos/thread.html

In that way, you can maintain 2 stack states: 1 for the main code of the workspace that is under execution. And one that you are continually appendCode-ing to for the new calls you would like to make in response to user clicking of buttons.

Good luck!
Greg

Livio Cicala

unread,
Oct 21, 2022, 6:03:22 AM10/21/22
to Blockly
Thanks, with your advice you are really helping me a lot!
And I begin (with difficulty) to understand!

There are numerous levels of abstraction that I have not understood.
1) There is the code of the blocks in the workspace (which is written in XML?)
2) There is the JS code written in the files: interpreter.js, acorn.js etc ...
3) There is the JS code which is given to the interpreter and which always executes in sequence from start to finish.
4) There could be additional code appended to the interpreter code, that it only executes if it has already done everything else.
5) There are the internal states of the interpreter (variables with their values ​​and also the "program counter", that is the instruction that is currently executed and that is incremented to pass to the next)

If I understand correctly I could, at least at the beginning, try to pass sections of code to the interpreter and make him execute them.
If the interpreter keeps the values ​​of the variables between runs then I see possibilities, otherwise it will be even more difficult.

In the next message I am attaching a sample image to show more clearly what should be done and why.

Livio Cicala

unread,
Oct 21, 2022, 6:39:47 AM10/21/22
to Blockly
Example2.png

This example shows the problem reduced to the essentials.

With the current methods it is not possible to perform the LED-ON and LED-OFF functions. And you can't even run them using appendCode() because in all cases, before you can get to the "appended" code, you always have to go through the count loop code first.

One could think of breaking the code to be supplied to JSinterpreter in various parts and this would allow to execute the functions but then it would be difficult, if not impossible, to continue the counting cycle by restarting it with the exact conditions it had arrived at.

Gregory Dyke

unread,
Oct 21, 2022, 10:07:50 AM10/21/22
to blo...@googlegroups.com
I think I see how to do this. I'll have a proof of concept for you some time this weekend 

Livio Cicala

unread,
Oct 21, 2022, 10:35:53 AM10/21/22
to Blockly
Even the multi-threaded solution does not seem feasible to me, for the following reasons:
- Separate threads cannot share the same variables.
- Variable communication and thread synchronization mechanisms are already difficult to use in normal languages and even for experienced programmers. Using them in this environment would create great difficulties for users and it would be difficult to explain to them that they have to do weird things to account for threads.
- Actually it would take a stack of threads because during an interrupt you could also have a second and so on ...

It therefore seems to me an unsuitable solution to the simple environment we are trying to create. 
If I'm wrong tell me.

Livio Cicala

unread,
Oct 21, 2022, 11:28:49 AM10/21/22
to Blockly
Thanks Greg, your help will be very useful!

I'll show you some applications of our system because Blockly will have to live with it and make it possible for non-programmers to write simple automation programs.

The best way to explain what we do is to see some of our applications that do automation in real time, directly from the PC, without dedicated PLCs and hardware, with completely free and OpenSource software and with super cheap hardware (DIY buildable with a few dozen euro, motors included).

Please watch these videos
http://www.theremino.com/files/Cobot/CobotExample1.mp4
http://www.theremino.com/files/Cobot/CobotExample4.mp4

So it's about opening up to Blockly the possibility of real automation, in real time, while so far I've only seen simulations, for example Picaxe and others, which run ten lines per second.

Now the speed is OK, calling the step function from DotNet Blockly is able to exceed ten thousand lines per second, it is not so much but it can be enough for simple applications. What is missing is the ability to use asynchronous events. And without events you can't do real Input-Output, just games and simulations.

Gregory Dyke

unread,
Oct 21, 2022, 5:55:32 PM10/21/22
to blo...@googlegroups.com
Hi Livio

Here is my proof of concept https://codepen.io/gregdyke/pen/rNvXdBG

As you can see, the code that is executed is equivalent to the generated code from blockly (I didn't include blockly in the poc, because blockly is just a code generator).
- I separated the variable/function definitions from the main body of code so that they are available to the buttons before run is pressed
- Each global function has a corresponding button in the html (again, not automatically generated in the poc)
- When pressing run, the main code (looping from 1 to 1000, switching the led on and off every 50) gets executed
- The handler for each of the button generates a new call to the function and sends it to the interpreter for immediate execution. These functions have access to global scope so can share variables with the main code

This is fake multithreading, because there is only ever one thread at a time that is active, so they can share variables safely (within reason). With this example, the most recent thread must execute through to the end before the one it interrupted gets picked up. This is not obligatory (you could step through each thread in turn, but it would be a bit more expensive and complicated to get right).

As mentioned above, blockly is just a very fancy code generator. It can generate javascript, or python or whatever code you would like. None of the problems you are trying to solve are limited by blockly itself, but by the runtime environment you have (i.e. the virtual machine in which you are choosing to execute the generated code). If you want to persist with javascript, modern javascript is perfectly capable of understanding async code and multithreading and co-routines etc., but you need to expose those blocks in blockly, or you need to write custom code outside of blockly to provide the execution you desire, or you need a different interpreter for your code (JSInterpreter is pretty impressive, but it's a javascript implementation of a javascript interpreter, so it's bound to be slow. You could probably ask your dotnet runtime to interpret the code just fine - though then you'd run into the same problems you currently have)

Hope this helps
Greg

Livio Cicala

unread,
Oct 22, 2022, 3:50:19 AM10/22/22
to Blockly
Incredible the speed and simplicity with which you did it, thanks! 
And your message too, in a few sentences you gave me to think about for quite a while.

Now, since I'm not used to working in this environment, it will take me a while to understand your functions and implement them in my application. But I think you have solved the problem and I also see the possibility of expanding it to deal with even nested interruptions.

Livio Cicala

unread,
Oct 22, 2022, 4:13:37 AM10/22/22
to Blockly
This "false multitreading" is actually the method of calling "subroutines" in response to button presses or other events. Interpreters of all languages have had this possibility since the prehistoric age of computer science.

AGC_View.jpg
Even the simple language of the AGC (Apollo Guidance computer with a RAM of only 2048 words) allowed this possibility.

We read on this Wikipedia page
The AGC responded to each interrupt by temporarily suspending the current program, executing a short interrupt service routine, and then resuming the interrupted program.

And as you rightly wrote this is not a multithreading, which would be much more difficult to use and keep under control. Unlike classic multithreading (which is preemptive multitasking), this interrupt method is also called "cooperative multitasking" and if used well it is even more efficient than multithreading. And it's also easier for novice programmers to understand.

Message has been deleted

Livio Cicala

unread,
Oct 23, 2022, 3:47:59 PM10/23/22
to Blockly
Hi Greg,
I haven't been able to get your code to go into my application yet. This is certainly due to the fact that I have not yet understood how JS-Interpreter works and also from the fact that I do not understand JavaScript very well.

In the next few days I will insist, but try to take a look at what I have written so far, and maybe you will immediately find what I am wrong.

-----

The file "theremino.js" which is short and contains the essential parts with the word "GREG" at the beginning
I apologize for the super-wide indentation but I need it to understand where the constructs start and end.

The file "heremino_custom_blocks.js" creates the pieces of global code from the blocks.

If I use the "GREG CODE DISABLED" zone it works (without interruptions but it works), but if I enable the "GREG CODE ENABLED" zone I can't get him to do anything. I don't see the variables and it doesn't call the block functions.

Here the link to the ZIP containing the two files
http://www.theremino.com/files/Theremino_Blockly_V1.0.49_FilesJS.zip

------

If anyone wants to try the complete program, they can find it here with the executable ready and running on every computer from Windows7 to Windows11 and also with all source files in OpenSource and totally free of any copyright. In other words, use everything exactly as you like, like all our other projects.

(to download the ZIP files open a new page and copy this link on the address)

Livio Cicala

unread,
Oct 25, 2022, 2:39:28 AM10/25/22
to Blockly
Thanks again, Greg, for your patience,
your code works even better than i could have hoped for. Multiple (nested) breaks also work.

Yours is a simple solution that may also be useful to others, so in the next few days I will fine-tune the connection details with my application and then publish everything here.

Mark Friedman

unread,
Oct 26, 2022, 2:50:19 PM10/26/22
to blo...@googlegroups.com

Livio Cicala

unread,
Nov 6, 2022, 12:29:58 PM11/6/22
to Blockly
I don't forget all of you, thanks again for your help and soon I will post the final version of the functions based on Greg's method here.
I still have to sort out some details and finish the documentation.
But I'll be back soon.
Message has been deleted

Livio Cicala

unread,
Nov 23, 2022, 4:31:08 AM11/23/22
to Blockly
Hello everybody,
Finally our version of blockly is complete and robust enough to publish it, you can download it from this address:
https://www.theremino.com/en/downloads/automation#blockly

Those interested in JavaScript files only, should download the version with the sources and then open the folder: 
- Sources\Blockly\Files\JS

which contains the files:
- theremino.js
- theremino_custom_blocks.js

These files contain our specific blocks and Greg's interrupt implementation.

A big thank you to everyone and particularly to Greg for the substantial help he has given us.

Gregory Dyke

unread,
Nov 23, 2022, 8:03:21 AM11/23/22
to blo...@googlegroups.com
Wonderful Livio!

Christopher Allen

unread,
Nov 24, 2022, 11:40:34 AM11/24/22
to blo...@googlegroups.com, Neil Fraser
Hi Greg & Livio,

I'm very glad that you were able to help each other and that the original question has been so satisfactorily answered that we can now enjoy the fruits of both your labour.

Re-reading the earlier bits of the thread (which I had not seen at the time), I did have a few comments to add for posterity:


On Fri, 21 Oct 2022 at 22:55, Gregory Dyke <greg...@gmail.com> wrote:
Here is my proof of concept https://codepen.io/gregdyke/pen/rNvXdBG

Very nice.  Neil actually already had a demo of multi-threading using the JS Interpreter but your proof of concept shows nicely how to implement multiple threads with a clearly defined (last in first out) priority order.

This is fake multithreading, because there is only ever one thread at a time that is active…

No, it's definitely real multithreading—don't forget that until pretty recently most computers had a single core!—it's just not truly parallel, but instead is somewhere between strictly cooperative and strictly preemptive, without round-robin scheduling…

With this example, the most recent thread must execute through to the end before the one it interrupted gets picked up.

It's worth noting that the concurrency model that Greg has implemented does differ slightly from the usual event-driven way that JavaScript runtimes work, where an incoming event will not get run until any previously running code has completed.  That's why there's no sleep() function in JavaScript, and why JavaScript APIs are littered with callbacks, Promises (and why we now have async / await): all to avoid the potential pitfalls of concurrent execution that Greg's approach opens up, where some globally-observable state could get changed unexpectedly in the middle of your code's execution due to another thread interrupting the one running.

On Sat, 22 Oct 2022 at 09:13, Livio Cicala <li...@freetw.net> wrote:
This "false multitreading" is actually the method of calling "subroutines" in response to button presses or other events. Interpreters of all languages have had this possibility since the prehistoric age of computer science.

AGC_View.jpg
Even the simple language of the AGC (Apollo Guidance computer with a RAM of only 2048 words) allowed this possibility.

We read on this Wikipedia page
The AGC responded to each interrupt by temporarily suspending the current program, executing a short interrupt service routine, and then resuming the interrupted program.

And as you rightly wrote this is not a multithreading, which would be much more difficult to use and keep under control. Unlike classic multithreading (which is preemptive multitasking), this interrupt method is also called "cooperative multitasking" and if used well it is even more efficient than multithreading. And it's also easier for novice programmers to understand.

Aah, interrupt handlers!  I wasn't quite sure what you wanted, Livio, until I read this message.

I believe it is possible to implement these using the JavaScript Interpreter without also implementing full multi-threading as Greg has done.  There's no proper API for it, but in principle all you need to do to call an interrupt handler is to push a carefully-constructed State object onto .stateStack; the next time step() is called it will be this interrupt 'call' that gets executed, and it will continue to be executed until it has completed and gets popped off .stateStack, at which point execution will resume wherever it was before the interrupt.

Very roughly the code would look something like this (NB: incomplete, untested and almost certainly buggy!):

    const interrupt = new Interpreter.State({
      // A fake AST node:
      type: 'CallExpression',
      // probably more stuff needed here.
    }, myInterpreter.globalScope);
    interrupt.func_ = /* interpreter function object to call */;
    interrupt.arguments_ = [ /* primitives and/or interpreter objects... */ ];
    interrupt.doneArgs_ = 2;  // Skip evaluation of callee and arguments.
    myInterpreter.stateStack.push(interrupt);

Note that this suffers from the same potential pitfalls that Greg's multi-threaded solution does (but that seems to be unavoidable given what Livio wants), and this also violates the encapsulation of the interpreter State objects, so no guarantees that a future update to JS Interpreter doesn't break it.


Christopher

Reply all
Reply to author
Forward
0 new messages