Skip to content

A scripting language that simplifies GUI programming in browser for more linear code in line with async/await and structured concurrency

License

Notifications You must be signed in to change notification settings

cl4cnam/funcSug

Repository files navigation

header

FuncSug: GUI programming made easy

FuncSug is an experimental scripting language that aims to simplify GUI programming in browser. It enables a more linear code in line with async/await and structured concurrency. It hides event-driven programming in a concurrent programming syntax.

Tutorials - Getting started - Examples - REPL - Test code directly online

A few samples

Play multiple sounds at the same time

parallel ||
	playSoundFile('sound1.mp3')
||
	playSoundFile('sound2.mp3')
||
	playSoundFile('sound3.mp3')

Play multiple sounds one at a time

playSoundFile('sound1.mp3')
playSoundFile('sound2.mp3')
playSoundFile('sound3.mp3')

Timeouts

displayNewMessage('What is ...?')
parallel exitAfter 1 finished ||
	var theAnswer := awaitHumanText()
||
	waitSeconds(15)

Why

Many people ask why GUI programming is so difficult. Some of common difficulties come from event-driven programming problems. That's why, with FuncSug, programmers do NOT have to follow the event-driven programming paradigm.

How

FuncSug suppresses event-driven programming problems: callback hell and state management.

Advantages:

  • It avoids callback hell (You don't need callback any more).
  • Your code follows the order of execution so you avoid spaghetti code and debug more easily.
  • It solves the state management problem (It eliminates the need to manage all the combinations of component states).
  • It easily manages task cancellations (including task timeouts).
  • It can detect a change in the value of a variable and react accordingly.
  • It's deterministic.
  • (You can also include JavaScript snippets, pass FuncSug variables to it and get back JavaScript return).

by means of:

  • explicit logical parallelism,
  • block cancellations,
  • and "await event" instructions.

Please, let me know what you think (Github discussions, Mastodon).
I'd be happy to read your opinion and answer your questions.

solve2_c2

Compare

These two codes do the same thing:

JavaScript FuncSug
let numberOfClick
function $(id) {
   return document.getElementById(id)
}
function launch() {
   $('launch').removeEventListener('click', launch)
   setTimeout(
      ()=>{
         $('clickZone')
         .removeEventListener('click', clickZone)
         console.log("Good job! See you soon!")
      },
      30000
   )
   numberOfClick = 0
   $('clickZone')
   .addEventListener('click', clickZone)
}
let timeoutID
function clickZone() {
   if (numberOfClick == 0) {
      timeoutID = setTimeout(
         ()=>{
            if (numberOfClick < 3) {
               numberOfClick = 0
               console.log("Non-triple click")
            }
         },
         2000
      )
   }
   numberOfClick += 1
   if (numberOfClick == 3) {
      numberOfClick = 0
      console.log("Triple click")
      clearTimeout(timeoutID)
   }
}
$('launch').addEventListener('click', launch)
awaitClickBeep('#launch')
parallel exitAfter 1 finished ||
   waitSeconds(30)
||
   while true:
      awaitClickBeep('#clickZone')
      parallel exitAfter 1 finished ||
         awaitClickBeep('#clickZone')
         awaitClickBeep('#clickZone')
         print("Triple click")
      ||
         waitSeconds(2)
         print("Non-triple click")
print("Good job! See you soon!")

If you copy and paste, replace each three initial spaces by one tabulation (Github markdown doesn't allow to change tabulation size in this context).

Get a taste of the language

Have a look at:

Look at the global structure of the code: One cannot follow this structure in usual programming language.

Let's take, for example, the 'Make Gems' code:

In this language, there is no need to build a ball object, just call a function lifeOfBall. You just run multiple lifeOfBall, birthOfGem and a lifeOfPaddle in parallel. These functions do not build objects: they are just like any classical function.
In the body of lifeOfPaddle, just code the various behaviors of the ball in parallel.

In 'Guess the Number', gameCourse is just a function. The line just after the call of gameCourse is executed only when the player has found the number, that is only when gameCourse has returned, just like any classical function call.

You can also try FuncSug online or test snippets of it.

Concurrent way

logo

FuncSug uses the "concurrent way" or more exactly the "logically parallel way". That is, it introduces explicit concurrency where no concurrency seems present or mandatory. The concurrency is reputed to be very difficult. However, amazingly, many cases (in particular concurrency of waits) are simpler with explicit concurrency!

With this style, you can program (as you program for the console) without users losing the multiple possibilities of interaction (Indeed, this style suppresses the inversion of control of the callback style of event-driven programming).

This "concurrent way" is expressed thanks to additional control flow instructions (beyond if, while, for,...):

  • parallel / parallel exitAfter ... finished / parallel(select ...) / parallel(for ... in ...),
  • select,
  • spawn,
  • whileTrue_dependsOn / whileTrueAwaitFrame_js / whileTrueAwaitDom_js,
  • repeat,
  • sequence,
  • ...

and with new modifiers (beyond break, return): restart, pause, resume.

That enables a better structure of code (that is, a more natural and more readable structure).

If you're curious, you can read the the 'Memory' code or try it or read this post.

It's loosely based on SugarCubes by Jean-Ferdy Susini which is a derivative of Esterel by Gérard Berry. It adheres to "structured concurrency" principles. It doesn't use OS threads. It doesn't aim to improve speed of execution.

Note

Goal: facilitating GUI programming (in fact, the interactivity aspect)
Non-goals: improving speed of execution, facilitating data structuration, object management, mutability, types

Instructions for use

In your myProgram.html, in the end of the body element, include the lines:

  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libStd.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libDOM.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libDOMSVG.fg" type="application/funcsug"></script>
  <script src="myProgram.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/parser.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/parserPy.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/interpreter.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/DOMloader.js"></script>

Write your code in the file myProgram.fg.

Specific case: With phaser_ce

In your myProgram.html, in the end of the body element, include the lines:

  <script src="https://cdn.jsdelivr.net/gh/photonstorm/phaser-ce/build/phaser.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libStd.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libDOM.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/libPhaser.fg" type="application/funcsug"></script>
  <script src="myProgram.fg" type="application/funcsug"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/parser.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/parserPy.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/interpreter.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/cl4cnam/funcSug/DOMloader.js"></script>

Write your code in the file myProgram.fg.

Some features

Parallel construct

Beware that this is just a logical parallelism.

You can write

parallel ||
	<parallelBranch1>
||
	<parallelBranch2>

to execute parallelBranch1 and parallelBranch2 concurrently.

You can also:

  • add a branch dynamically,
  • select parallel branch(es) according to event(s).

Interruptible block

You can interrupt (break), pause (pause), resume (resume) or restart (restart) a block. For example:

parallel ||
	@myBlock
	<instruction1>
	<instruction2>
||
	break myBlock

Reaction to a change of a variable

You can react to a change of a variable, for example, like this:

while true:
	awaitBeep myVariable
	<whatToDoInThisCase>

Syntax elements

The syntax is very similar to that of Python.

After the colon : of some blocks, you can add @label just after the function name to label the expression (This is useful for the break instruction). This 'label' must be a declared variable.

A few instructions/expressions (loose description)

print(value) writes value onto the console.

var variableName declares a new local variable.

variableName := value assigns value to variableName.

variableName += value increments variableName by value (also for -,*,/ operators).

true and false returns the boolean value 'true' and 'false' respectively.

value1 + value2 returns the sum/concatenation of the two values (also for -,*,/,mod).

value1 < value2 returns the truth value of value1 < value2 (also for <=,=,/=, and, or).

randomIntBetween(value1, value2) returns a random integer between value1 and value2.

if, while, def acts as usual in Python.

break label interrupts the execution of the expression labelled by label.

seq:
	expression1
	...
	expressionN

executes the expressions in sequence and returns the value of the last.

par:
	expression1
	...
	expressionN

executes the expressions in parallel.

onBreak:
	expression

executes expression if the preceding expression is interrupted.

js (variable1, ..., variableN):
	jsString

executes the code in jsString (in which variable1 ... variableN can be used).

displayNewMessageIn(text cssSelector) displays a new message text into the DOM element identified by cssSelector.

displayNewMessageIn(text cssSelector/cssClass) displays a new message text into the DOM element identified by cssSelector and assigns cssClass to it.

awaitNewHumanNumberIn(cssSelector) awaits a number from user in the DOM element identified by cssSelector.

awaitClickBeep(cssSelector) awaits a click from user in the DOM element identified by cssSelector.

Contributing

Pull requests are welcome.

If you want to make big change, please open an issue first to discuss it.

You can just post your thoughts into discussions tab too.

Examples of contributions: Tutorials, optimization, translations, fixing bugs, ...

Various

This language has syntax highlighting for Geany; otherwise, with Geany, if you choose that of zephir (or nsis, powershell, ruby), it can be pleasant.

The file parserPy.js has been generated online from the file funcSugPy.peggyjs on the site https://peggyjs.org/online.html with "parser variable" set to "pegPy".

For now, this implementation consists just in a quick and dirty interpreter but it works enough to be appreciated.

Work in progress.