├── .github └── FUNDING.yml ├── .gitignore ├── LICENSE ├── README.md ├── dist ├── compiler.js ├── compiler.js.map ├── main.js ├── main.js.map ├── main.lite.js └── main.lite.js.map ├── package-lock.json ├── package.json ├── src ├── AbstractQuantumScript.js ├── api.compiler.global.js ├── api.global.js ├── api.global.lite.js ├── compiler │ ├── $qDownstream.js │ ├── $qIdentifier.js │ ├── Compiler.js │ ├── Node.js │ ├── Parser.js │ ├── Scope.js │ └── index.js ├── index.js ├── index.lite.js ├── params.js ├── runtime │ ├── AutoAsyncIterator.js │ ├── AutoIterator.js │ ├── Autorun.js │ ├── EventTarget.js │ ├── Runtime.js │ ├── Scope.js │ ├── Signal.js │ ├── State.js │ ├── hot-module-registry.js │ └── index.js └── util.js └── test ├── ast.test.driver.js ├── ast.test.obsolete.js ├── behaviour.test.js └── m.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: ox-harris # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: webqit 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry 13 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | !/.gitignore 3 | node_modules 4 | src2 -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022-present, WebQit, Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Quantum JS 2 | 3 | [![npm version][npm-version-src]][npm-version-href] 4 | [![bundle][bundle-src]][bundle-href] 5 | [![License][license-src]][license-href] 6 | 7 | [Overview](#overview) • [Creating Quantum Programs](#creating-quantum-programs) • [Implementation](#implementation) • [Examples](#examples) • [License](#license) 8 | 9 | Quantum JS is a runtime extension to JavaScript that brings Imperative Reactive Programming to JavaScript! 10 | 11 | What's that? 12 | 13 | ## Overview 14 | 15 | Where you normally would require certain reactive primitives to express reactive logic... 16 | 17 | ```js 18 | // Import reactive primitives 19 | import { createSignal, createMemo, createEffect } from 'solid-js'; 20 | 21 | // Declare values 22 | const [ count, setCount ] = createSignal(5); 23 | const doubleCount = createMemo(() => count() * 2); 24 | // Log this value live 25 | createEffect(() => { 26 | console.log(doubleCount()); 27 | }); 28 | ``` 29 | 30 | ```js 31 | // Setup periodic updates 32 | setInterval(() => setCount(10), 1000); 33 | ``` 34 | 35 | Quantum JS lets you acheive the same in the ordinary imperative form of the language: 36 | 37 | ```js 38 | // Declare values 39 | let count = 5; 40 | let doubleCount = count * 2; 41 | // Log this value live 42 | console.log(doubleCount); 43 | ``` 44 | 45 | ```js 46 | // Setup periodic updates 47 | setInterval(() => count = 10, 1000); 48 | ``` 49 | 50 | Wanna play?: 51 | 52 | 1. Add the following script to your page: 53 | 54 | ```html 55 | 56 | ``` 57 | 58 | 2. Write your logic within a `quantum` script tag: 59 | 60 | ```html 61 | 71 | ``` 72 | 73 | 3. Watch your console. Have fun. 74 | 75 | Wanna see how magical things really are here? Update your step 2 to split the logic into two separate scripts: 76 | 77 | 2. One ordinary script and one quantum script: 78 | 79 | ```html 80 | 87 | ``` 88 | 89 | ```html 90 | 94 | ``` 95 | 96 | Watching your console? Reactivity is still intact! 97 | 98 | That reactivity is really happening within the Quantum script! It's a regular script in every way except that any peice of code thrown in is able to statically reflect changes to state in granular details! 99 | 100 | To define, Quantum programs are an extension to JavaScript that has any piece of code stay sensitive to changes in the most fine-grained details - and entirely with no moving parts! 101 | 102 | Now, while that is the `` part of the HTML page above, there are many different ways to have this form of reactivity play out. Examples are [just ahead](#examples). 103 | 104 | This project pursues a futuristic, more efficient way to write reactive applocations *today*! And it occupies [a new category](https://en.wikipedia.org/wiki/Reactive_programming#Imperative) in the reactivity spectrum! 105 | 106 | 137 | 138 | ## Implementation 139 | 140 | As seen above, you can have all of Quantum JS live in the browser! 141 | 142 | Up there, we've used a version of the Quantum JS implementation that supports HTML ` 151 | ``` 152 | 153 | └ This is to be placed early on in the document and should be a classic script without any `defer` or `async` directives! 154 | 155 | ```js 156 | // Destructure from the webqit namespace 157 | const { QuantumFunction, AsyncQuantumFunction, QuantumScript, QuantumModule, State, Observer } = window.webqit; 158 | ``` 159 | 160 | 161 | 162 |
Install from NPM
163 | └─────────
164 | 165 | ```js 166 | // npm install 167 | npm i @webqit/quantum-js 168 | ``` 169 | 170 | ```js 171 | // Import API 172 | import { QuantumFunction, AsyncQuantumFunction, QuantumScript, AsyncQuantumScript, QuantumModule, State, Observer } from '@webqit/quantum-js'; 173 | ``` 174 | 175 |
176 | 177 | 178 | 179 | ### Quantum JS Lite 180 | 181 | It is possible to use a lighter version of Quantum JS where you want something further feather weight for your initial application load. 182 | 183 |
184 | Load from a CDN
185 | └─────────
186 | 187 | ```html 188 | 189 | ``` 190 | 191 | └ This is to be placed early on in the document and should be a classic script without any `defer` or `async` directives! 192 | 193 | ```js 194 | // Destructure from the webqit namespace 195 | const { AsyncQuantumFunction, AsyncQuantumScript, QuantumModule, State, Observer } = window.webqit; 196 | ``` 197 | 198 |
Additional details 199 | 200 | The Lite APIs initially come without the compiler and yet lets you work with Quantum JS ahead of that. Additionally, these APIs are able to do their compilation off the main thread by getting the Quantum JS compiler loaded into a [Web Worker](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers)! 201 | 202 | > But if you may, the Quantum JS Compiler is all still loadable directly - as if short-circuiting the lazy-loading strategy of the Lite APIs: 203 | > 204 | > ```html 205 | > 206 | > 207 | > 208 | > 209 | > ``` 210 | 211 |
212 | 213 |
214 | 215 |
Install from NPM
216 | └─────────
217 | 218 | ```js 219 | // npm install 220 | npm i @webqit/quantum-js 221 | ``` 222 | 223 | ```js 224 | // Import Lite API 225 | import { AsyncQuantumFunction, AsyncQuantumScript, QuantumModule, State, Observer } from '@webqit/quantum-js/lite'; 226 | ``` 227 | 228 |
229 | 230 | ## Creating Quantum Programs 231 | 232 | You can write Quantum programs in either of two ways: as "Quantum Functions" and as "Quantum Scripts"! 233 | 234 | ### Quantum Functions 235 | 236 | Here, we introduce a new type of function that works like any other JavaScript function but in reactive mode. This function may be wrtten in any of the forms below: 237 | 238 | #### Syntax 1: Using the `quantum` Function keyword 239 | 240 | > Available since v4.3. 241 | 242 | Here you prepend your function with the `quantum` keyword, just in how you use the `async` keyword: 243 | 244 | ```js 245 | // Quantum function declaration 246 | quantum function bar() { 247 | let count = 5; 248 | let doubleCount = count * 2; 249 | console.log(doubleCount); 250 | } 251 | bar(); 252 | ``` 253 | 254 | ```js 255 | // Async quantum function declaration 256 | async quantum function bar() { 257 | let count = await 5; 258 | let doubleCount = count * 2; 259 | console.log(doubleCount); 260 | } 261 | await bar(); 262 | ``` 263 | 264 |
Show more syntax examples 265 | 266 | ```js 267 | // Quantum function expression 268 | const bar = quantum function() { 269 | } 270 | const bar = async quantum function() { 271 | } 272 | ``` 273 | 274 | ```js 275 | // Quantum object property 276 | const foo = { 277 | bar: quantum function() { ... }, 278 | } 279 | const foo = { 280 | bar: async quantum function() { ... }, 281 | } 282 | ``` 283 | 284 | ```js 285 | // Quantum object method 286 | const foo = { 287 | quantum bar() { ... }, 288 | } 289 | const foo = { 290 | async quantum bar() { ... }, 291 | } 292 | ``` 293 | 294 | ```js 295 | // Quantum class method 296 | class Foo { 297 | quantum bar() { ... } 298 | } 299 | class Foo { 300 | async quantum bar() { ... } 301 | } 302 | ``` 303 | 304 | ```js 305 | // Quantum arrow function expression 306 | const bar = quantum () => { 307 | } 308 | const bar = async quantum () => { 309 | } 310 | ``` 311 | 312 | ```js 313 | // Quantum arrow function expression 314 | const bar = quantum arg => { 315 | } 316 | const bar = async quantum arg => { 317 | } 318 | ``` 319 | 320 |
321 | 322 |
Show polyfill support 323 | 324 | This syntax is universally supported both within: 325 | 326 | + Quantum JS' dynamic APIs: 327 | 328 | ```js 329 | const program = new QuantumFunction(` 330 | // External dependency 331 | let externalVar = 10; 332 | 333 | // QuantumFunction 334 | quantum function sum(a, b) { 335 | return a + b + externalVar; 336 | } 337 | const state = sum(10, 10); 338 | 339 | // Inspect 340 | console.log(state.value); // 30 341 | `); 342 | program(); 343 | ``` 344 | 345 | + and inline ` 361 | ``` 362 | 363 |
364 | 365 | #### Syntax 2: Using the Double Star `**` Notation 366 | 367 | Here you append your function with the double star `**` notation, much like how you write [generator functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator): 368 | 369 | ```js 370 | // Quantum function declaration 371 | function** bar() { 372 | let count = 5; 373 | let doubleCount = count * 2; 374 | console.log(doubleCount); 375 | } 376 | bar(); 377 | ``` 378 | 379 | ```js 380 | // Async quantum function declaration 381 | async function** bar() { 382 | let count = await 5; 383 | let doubleCount = count * 2; 384 | console.log(doubleCount); 385 | } 386 | await bar(); 387 | ``` 388 | 389 |
Show more syntax examples 390 | 391 | ```js 392 | // Quantum function expression 393 | const bar = function** () { 394 | } 395 | const bar = async function** () { 396 | } 397 | ``` 398 | 399 | ```js 400 | // Quantum object property 401 | const foo = { 402 | bar: function** () { ... }, 403 | } 404 | const foo = { 405 | bar: async function** () { ... }, 406 | } 407 | ``` 408 | 409 | ```js 410 | // Quantum object method 411 | const foo = { 412 | **bar() { ... }, 413 | } 414 | const foo = { 415 | async **bar() { ... }, 416 | } 417 | ``` 418 | 419 | ```js 420 | // Quantum class method, optionally async 421 | class Foo { 422 | **bar() { ... } 423 | } 424 | class Foo { 425 | async **bar() { ... } 426 | } 427 | ``` 428 | 429 |
430 | 431 |
Show polyfill support 432 | 433 | This syntax is universally supported both within: 434 | 435 | + Quantum JS' dynamic APIs: 436 | 437 | ```js 438 | const program = new QuantumFunction(` 439 | // External dependency 440 | let externalVar = 10; 441 | 442 | // QuantumFunction 443 | function** sum(a, b) { 444 | return a + b + externalVar; 445 | } 446 | const state = sum(10, 10); 447 | 448 | // Inspect 449 | console.log(state.value); // 30 450 | `); 451 | program(); 452 | ``` 453 | 454 | + and inline ` 470 | ``` 471 | 472 |
473 | 474 | #### Syntax 3: Using Quantum Function Constructors 475 | 476 | Here you use special function constructors to create a new Quantum function: 477 | 478 | ```js 479 | // Quantum function constructor 480 | const bar = QuantumFunction(` 481 | let count = 5; 482 | let doubleCount = count * 2; 483 | console.log(doubleCount); 484 | `); 485 | bar(); 486 | ``` 487 | 488 | ```js 489 | // Async quantum function constructor 490 | const bar = AsyncQuantumFunction(` 491 | let count = await 5; 492 | let doubleCount = count * 2; 493 | console.log(doubleCount); 494 | `); 495 | await bar(); 496 | ``` 497 | 498 |
Show more syntax examples 499 | 500 | ```js 501 | // With function parameters 502 | const bar = QuantumFunction( param1, ... paramN, functionBody ); 503 | ``` 504 | 505 | ```js 506 | // With the new keyword 507 | const bar = new QuantumFunction( param1, ... paramN, functionBody ); 508 | ``` 509 | 510 | ```js 511 | // As class property 512 | class Foo { 513 | bar = QuantumFunction( param1, ... paramN, functionBody ); 514 | } 515 | ``` 516 | 517 |
518 | 519 |
Show polyfill support 520 | 521 | This is how you obtain the APIs: 522 | 523 | ```js 524 | // Import API 525 | import { QuantumFunction, AsyncQuantumFunction } from '@webqit/quantum-js'; 526 | ``` 527 | 528 | ```js 529 | const { QuantumFunction, AsyncQuantumFunction } = window.webqit; 530 | ``` 531 | 532 | Here, the difference between `QuantumFunction` and `AsyncQuantumFunction` is same as the difference between a regular synchronous JS function (`function() {}`) and its *async* equivalent (`async function() {}`). 533 | 534 | ```js 535 | // External dependency 536 | globalThis.externalVar = 10; 537 | 538 | // QuantumFunction 539 | const sum = QuantumFunction(`a`, `b`, ` 540 | return a + b + externalVar; 541 | `); 542 | const state = sum(10, 10); 543 | 544 | // Inspect 545 | console.log(state.value); // 30 546 | ``` 547 | 548 | > Note that, unlike the main Quantum JS build, the Quantum JS Lite edition only implements the `AsyncQuantumFunction` API which falls within the asynchronous operation of the Lite edition. 549 | 550 |
551 | 552 |
Additional details 553 | 554 | Note that unlike normal function declarations and expressions that can see their surrounding scope, as in syntaxes 1 and 2 above, code in [function constructors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/Function) is only able to see the global scope: 555 | 556 | ```js 557 | let a; 558 | globalThis.b = 2; 559 | var c = 'c'; // Equivalent to globalThis.c = 'c' assuming that we aren't running in a function scope or module scope 560 | const bar = QuantumFunction(` 561 | console.log(typeof a); // undefined 562 | console.log(typeof b); // number 563 | console.log(typeof c); // string 564 | `); 565 | bar(); 566 | ``` 567 | 568 |
569 | 570 | ### Quantum Scripts (Whole Programs) 571 | 572 | Here, whole programs are able to run in *quantum* execution mode using special scripting APIs: 573 | 574 | ```js 575 | // Quantum regular JS 576 | const program = new QuantumScript(` 577 | let count = 5; 578 | let doubleCount = count * 2; 579 | console.log(doubleCount); 580 | `); 581 | program.execute(); 582 | ``` 583 | 584 | ```js 585 | // Quantum module 586 | const program = new QuantumModule(` 587 | let count = await 5; 588 | let doubleCount = count * 2; 589 | console.log(doubleCount); 590 | `); 591 | await program.execute(); 592 | ``` 593 | 594 | These will run in the global scope! 595 | 596 | The latter does certainly let you use `import` and `export` statements! 597 | 598 |
Exanple 599 | 600 | ```js 601 | // Quantum module 602 | const program = new QuantumModule(` 603 | import module1, { module2 } from 'package-name'; 604 | import { module3 as alias } from 'package-name'; 605 | ... 606 | export * from 'package-name'; 607 | export let localVar = 0; 608 | `); 609 | ``` 610 | 611 |
612 | 613 |
Show polyfill support 614 | 615 | This is how you obtain the APIs: 616 | 617 | ```js 618 | // Import API 619 | import { QuantumScript, QuantumModule, AsyncQuantumScript } from '@webqit/quantum-js'; 620 | ``` 621 | 622 | ```js 623 | const { QuantumScript, QuantumModule, AsyncQuantumScript } = window.webqit; 624 | ``` 625 | 626 | Here, the difference between `QuantumScript`, `AsyncQuantumScript`, and `QuantumModule` is same as the difference between a regular synchronous script element (`